This repository has been archived by the owner on Jul 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(test): rewrite test mode validation
re #51
- Loading branch information
Showing
9 changed files
with
256 additions
and
416 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,172 @@ | ||
import { testConfigs } from './test' | ||
import { randomString, mockFn, mockObj, mocked } from '~test-helpers' | ||
import { TestFn, doItAll } from '~commands/test/validators' | ||
import logger from '~logger' | ||
import { runTests } from './test' | ||
import { randomString, mockFn, mockObj, randomNumber } from '~test-helpers' | ||
import stripAnsi from 'strip-ansi' | ||
import { ProblemType } from '~problem' | ||
import { TestConfig } from './config' | ||
import { FetchResource } from './http-client' | ||
import { TypeValidator } from '~validation' | ||
import { ConfigBuilder } from '~config/types' | ||
import { Logger } from 'winston' | ||
|
||
jest.unmock('./test') | ||
jest.unmock('~messages') | ||
jest.unmock('~commands/shared') | ||
jest.disableAutomock() | ||
|
||
describe('test configs', () => { | ||
const baseUrl = randomString('base-url') | ||
const baseUrl = randomString('http://example') + '.com' | ||
const mockLogger = mockObj<Logger>({ error: jest.fn(), info: jest.fn() }) | ||
const mockFetchResource = mockFn<FetchResource>() | ||
const mockTypeValidator = mockObj<TypeValidator>({}) | ||
const mockTest = mockFn<TestFn>() | ||
const mockDoItAll = mocked(doItAll) | ||
const mockLogger = mockObj(logger) | ||
const mockTypeValidator = mockObj<TypeValidator>({ validate: jest.fn() }) | ||
const mockGetTypeValidator = mockFn<() => TypeValidator>() | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks() | ||
mockDoItAll.mockReturnValue(mockTest) | ||
mockGetTypeValidator.mockReturnValue(mockTypeValidator) | ||
}) | ||
|
||
it('calls doItAll with the correct args', async () => { | ||
const configs: TestConfig[] = [ | ||
{ name: 'yo!', request: { endpoint: randomString('endpoint') } } as TestConfig, | ||
] | ||
mockTest.mockResolvedValue([]) | ||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
const act = (...configs: TestConfig[]) => | ||
runTests(baseUrl, mockFetchResource, configs, mockGetTypeValidator, mockLogger) | ||
|
||
await testConfigs(baseUrl, mockFetchResource, configs, mockTypeValidator) | ||
const getLoggedMessage = (method: 'error' | 'info', callIndex = 0): string => | ||
stripAnsi((mockLogger[method].mock.calls[callIndex][0] as unknown) as string) | ||
|
||
expect(mockDoItAll).toBeCalledWith(mockTypeValidator, mockFetchResource) | ||
it('calls fetchResource with the correct args', async () => { | ||
const config = new ConfigBuilder().build() | ||
mockFetchResource.mockResolvedValue({ status: randomNumber() }) | ||
|
||
await act(config) | ||
|
||
expect(mockFetchResource).toBeCalledWith(config) | ||
}) | ||
|
||
it('logs when a test passes', async () => { | ||
const configs: TestConfig[] = [ | ||
{ name: 'yo!', request: { endpoint: randomString('endpoint') } } as TestConfig, | ||
] | ||
mockTest.mockResolvedValue([]) | ||
it('logs a failure when fetching a resource throws', async () => { | ||
const config = new ConfigBuilder().withName('Bob').withEndpoint('/jim').build() | ||
const errorMessage = randomString('error message') | ||
mockFetchResource.mockRejectedValue(new Error(errorMessage)) | ||
|
||
await testConfigs(baseUrl, mockFetchResource, configs, mockTypeValidator) | ||
const result = await act(config) | ||
|
||
const expectedMessage = `PASSED: yo! - ${baseUrl}${configs[0].request.endpoint}\n` | ||
expect(mockLogger.info).toBeCalled() | ||
expect(stripAnsi((mockLogger.info.mock.calls[0][0] as unknown) as string)).toEqual(expectedMessage) | ||
expect(getLoggedMessage('error')).toEqual(`FAILED: Bob - ${baseUrl}/jim\n${errorMessage}`) | ||
expect(result).toEqual('Failure') | ||
}) | ||
|
||
it('logs when a test fail', async () => { | ||
const configs: TestConfig[] = [ | ||
{ name: 'yo!', request: { endpoint: randomString('endpoint') } } as TestConfig, | ||
] | ||
mockTest.mockResolvedValue([{ path: 'some.path', problemType: ProblemType.Request, message: 'message' }]) | ||
it('returns a failed message when the status code does not match', async () => { | ||
const expectedStatus = randomNumber() | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseCode(expectedStatus) | ||
.build() | ||
mockFetchResource.mockResolvedValue({ status: 123 }) | ||
|
||
await expect(testConfigs(baseUrl, mockFetchResource, configs, mockTypeValidator)).rejects.toThrowError( | ||
'Not all tests passed', | ||
) | ||
const result = await act(config) | ||
|
||
const expectedMessage = `FAILED: yo!\nURL: ${baseUrl}${configs[0].request.endpoint}\n` | ||
const expectedError1 = `Request some.path message\n` | ||
expect(mockLogger.error).toBeCalled() | ||
expect(stripAnsi((mockLogger.error.mock.calls[0][0] as unknown) as string)).toEqual( | ||
expectedMessage + expectedError1, | ||
expect(getLoggedMessage('error')).toEqual( | ||
`FAILED: Bob - ${baseUrl}/jim\nExpected status code ${expectedStatus} but got 123`, | ||
) | ||
expect(result).toEqual('Failure') | ||
}) | ||
|
||
it('logs when a test throws an error', async () => { | ||
const configs: TestConfig[] = [ | ||
{ name: 'yo!', request: { endpoint: randomString('endpoint') } } as TestConfig, | ||
] | ||
mockTest.mockRejectedValue(new Error('woah dude')) | ||
describe('validation when a response body is configured', () => { | ||
it('does not return a failure message when the body matches the specified object', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseBody({ hello: ['to', 'the', { world: 'earth' }], cya: 'later', mate: 23 }) | ||
.build() | ||
mockFetchResource.mockResolvedValue({ | ||
status: 200, | ||
data: { hello: ['to', 'the', { world: 'earth' }], cya: 'later', mate: 23 }, | ||
}) | ||
|
||
await expect(testConfigs(baseUrl, mockFetchResource, configs, mockTypeValidator)).rejects.toThrowError( | ||
'Not all tests passed', | ||
) | ||
const result = await act(config) | ||
|
||
expect(getLoggedMessage('info')).toEqual(`PASSED: Bob - ${baseUrl}/jim`) | ||
expect(result).toEqual('Success') | ||
}) | ||
|
||
it('returns a failure message when the response body is undefined', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseBody(randomString('body')) | ||
.build() | ||
mockFetchResource.mockResolvedValue({ status: 200 }) | ||
|
||
const result = await act(config) | ||
|
||
expect(getLoggedMessage('error')).toEqual(`FAILED: Bob - ${baseUrl}/jim\nNo response body was received`) | ||
expect(result).toEqual('Failure') | ||
}) | ||
|
||
it('returns a failure message when the body does not match the specified object', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseBody({ hello: ['to', 'the', { world: 'earth' }], cya: 'later', mate: 23 }) | ||
.build() | ||
const actualResponseBody = { hello: 'world' } | ||
mockFetchResource.mockResolvedValue({ status: 200, data: actualResponseBody }) | ||
|
||
const result = await act(config) | ||
|
||
expect(getLoggedMessage('error')).toEqual( | ||
`FAILED: Bob - ${baseUrl}/jim\nThe response body was not deeply equal to your configured fixture\nReceived:\n{ hello: 'world' }`, | ||
) | ||
expect(result).toEqual('Failure') | ||
}) | ||
}) | ||
|
||
describe('when the config has a response type specified', () => { | ||
it('calls the type validator with the correct params', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseType(randomString('res type')) | ||
.withResponseBody(undefined) | ||
.build() | ||
const actualData = randomString('data') | ||
mockFetchResource.mockResolvedValue({ status: 200, data: actualData }) | ||
mockTypeValidator.validate.mockResolvedValue({ success: true }) | ||
|
||
await act(config) | ||
|
||
expect(mockTypeValidator.validate).toBeCalledWith(actualData, config.response.type) | ||
}) | ||
|
||
it('does not return a failure message when the types match', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseType(randomString('res type')) | ||
.withResponseBody(undefined) | ||
.build() | ||
mockTypeValidator.validate.mockResolvedValue({ success: true }) | ||
mockFetchResource.mockResolvedValue({ status: 200 }) | ||
|
||
const result = await act(config) | ||
|
||
expect(getLoggedMessage('info')).toEqual(`PASSED: Bob - ${baseUrl}/jim`) | ||
expect(result).toEqual('Success') | ||
}) | ||
|
||
it('returns a failure message when the types do not match', async () => { | ||
const config = new ConfigBuilder() | ||
.withName('Bob') | ||
.withEndpoint('/jim') | ||
.withResponseType(randomString('res type')) | ||
.withResponseBody(undefined) | ||
.build() | ||
const error1 = randomString('error2') | ||
const error2 = randomString('error2') | ||
mockTypeValidator.validate.mockResolvedValue({ success: false, errors: [error1, error2] }) | ||
mockFetchResource.mockResolvedValue({ status: 200 }) | ||
|
||
const result = await act(config) | ||
|
||
const expectedMessage = `ERROR: yo!\nURL: ${baseUrl}${configs[0].request.endpoint}\nwoah dude\n` | ||
expect(mockLogger.error).toBeCalled() | ||
expect(stripAnsi((mockLogger.error.mock.calls[0][0] as unknown) as string)).toEqual(expectedMessage) | ||
const expectedPart1 = `FAILED: Bob - ${baseUrl}/jim\n` | ||
const expectedPart2 = `The received body does not match the type ${config.response.type}\n` | ||
const expectedPart3 = `${error1}\n${error2}` | ||
expect(getLoggedMessage('error')).toEqual(`${expectedPart1}${expectedPart2}${expectedPart3}`) | ||
expect(result).toEqual('Failure') | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.