UNPKG

specmatic

Version:
273 lines (205 loc) 11.5 kB
import path from 'path'; import { ChildProcess, spawn } from 'child_process'; import { mock as jestMock, mockReset } from 'jest-mock-extended'; import { Readable } from 'stream'; import terminate from 'terminate/promise'; import * as specmatic from '../..'; import { specmaticCoreJarName } from '../../config'; import { Stub } from '..'; import * as shutDown from '../shutdownUtils' jest.mock('child_process'); jest.mock('terminate'); jest.mock('../shutdownUtils') const SPECMATIC_JAR_PATH = path.resolve(__dirname, '..', '..', '..', specmaticCoreJarName); const HOST = 'localhost'; const PORT = 8000; const javaProcessMock = jestMock<ChildProcess>(); const readableMock = jestMock<Readable>(); javaProcessMock.stdout = readableMock; javaProcessMock.stderr = readableMock; const stubUrl = `http://${HOST}:${PORT}`; const stub = new Stub(HOST, PORT, stubUrl, javaProcessMock); beforeEach(() => { jest.resetAllMocks(); }); test('should be able to parse stub port on random assignment', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback("Free port found: 1234"); messageCallback(`- http://${HOST}:1234 serving endpoints from specs:`); }, 0); await expect(specmatic.startStub()).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub"]); }); test('should stick to random port assignment even if later logs contradict it', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback("Free port found: 1234"); messageCallback(`- http://${HOST}:9000 serving endpoints from specs:`); }, 0); await expect(specmatic.startStub()).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub"]); }); test('should stick to passed port assignment even if final log contradicts it', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback("Free port found: 4567"); messageCallback(`- http://${HOST}:9000 serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST, 1234)).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=1234`]); }); test('should be able to parse stub port even if the multi base url has postfix paths', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback(`- http://${HOST}:1234/api serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST, 1234)).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234/api`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=1234`]); }); test('should be able to parse stub port even if the multi base url has postfix paths when passed port is not same as one in the log', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback(`- http://${HOST}:9000/api serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST, 1234)).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234/api`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=1234`]); }); test('should use the default http or https port when no port is specified in the multi base url logs', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback(`- http://${HOST}/api serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST)).resolves.toStrictEqual(new Stub(HOST, 80, `http://${HOST}:80/api`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`]); }); test('should fail if specified port in logs is out of valid range', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback(`- http://${HOST}:99999/api serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST)).toReject() }); test('should pick the first port when multi-port stub is being used', async () => { spawn.mockReturnValue(javaProcessMock); const messages = [ "Stub server is running on the following URLs:", "- http://localhost:1234 serving endpoints from specs:", " 1. ./simpleRandom.yaml", "", "!@! http://localhost:9001 serving endpoints from specs #$!", " 1. ./simple9001.yaml", "", "-: http://localhost:9002 serving endpoints from specs", " 1. ./simple9002.yaml", " 2. ./simple9002Second.yaml" ]; setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messages.forEach(messageCallback); }, 0); await expect(specmatic.startStub()).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub"]); }); test('test with multi port stub and random port assignment', async () => { spawn.mockReturnValue(javaProcessMock); const messages = [ "Free port found: 1234", "Stub server is running on the following URLs:", "- http://localhost:9000 serving endpoints from specs:", " 1. ./simpleRandom.yaml", "", "!@! http://localhost:9001 serving endpoints from specs #$!", " 1. ./simple9001.yaml", ]; setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messages.forEach(messageCallback); }, 0); await expect(specmatic.startStub()).resolves.toStrictEqual(new Stub(HOST, 1234, `http://${HOST}:1234`, javaProcessMock)); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual([`-jar`, path.resolve(SPECMATIC_JAR_PATH), "stub"]); }); test('starts the specmatic stub server', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1](`- ${stubUrl} serving endpoints from specs:`), 0); await expect(specmatic.startStub(HOST, PORT)).resolves.toStrictEqual(stub); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('starts the specmatic stub server with leading and trailing garbage values', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1](`-:!+@+ ${stubUrl} serving endpoints from specs: !@+#@`), 0); await expect(specmatic.startStub(HOST, PORT)).resolves.toStrictEqual(stub); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('notifies when start fails due to port not available', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1]('Address already in use'), 0); await expect(specmatic.startStub(HOST, PORT)).toReject(); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('returns host, port and stub url', async () => { spawn.mockReturnValue(javaProcessMock); const randomPort = 62269; const stubUrl = `http://${HOST}:${randomPort}`; setTimeout(() => { const messageCallback = readableMock.on.mock.calls[0][1]; messageCallback(`- ${stubUrl} serving endpoints from specs:`); }, 0); await expect(specmatic.startStub(HOST, PORT)).resolves.toStrictEqual(stub); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('fails if stub url is not available in start up message', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1]("serving endpoints from specs:"), 0); await expect(specmatic.startStub(HOST, PORT)).toReject(); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('fails if host info is not available in start up message', async () => { spawn.mockReturnValue(javaProcessMock); const stubUrl = `http://`; setTimeout(() => readableMock.on.mock.calls[0][1](`- ${stubUrl} serving endpoints from specs:`), 0); await expect(specmatic.startStub(HOST, PORT)).toReject(); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`]); }); test('host and port are optional', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1](`- ${stubUrl} serving endpoints from specs:`), 0); await expect(specmatic.startStub()).resolves.toStrictEqual(stub); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub"]); }); test('additional pass through arguments can be string or number', async () => { spawn.mockReturnValue(javaProcessMock); setTimeout(() => readableMock.on.mock.calls[0][1](`- ${stubUrl} serving endpoints from specs:`), 0); await expect(specmatic.startStub(HOST, PORT, ['p1', 123])).resolves.toStrictEqual(stub); expect(spawn.mock.calls[0][0]).toBe(`java`); expect(spawn.mock.calls[0][1]).toEqual(['-jar', path.resolve(SPECMATIC_JAR_PATH), "stub", `--host=${HOST}`, `--port=${PORT}`, "p1", "123"]); }); test('stopStub method stops any running stub server', async () => { shutDown.gracefulShutdown.mockResolvedValue(true); specmatic.stopStub(stub); expect(readableMock.removeAllListeners).toHaveBeenCalledTimes(2); expect(javaProcessMock.removeAllListeners).toHaveBeenCalledTimes(1); expect(shutDown.gracefulShutdown).toHaveBeenCalledTimes(1); });