Skip to content

Commit

Permalink
feat(DTFS2-7049): refactor testing helpers, other improvements for PR
Browse files Browse the repository at this point in the history
  • Loading branch information
avaitonis committed Apr 30, 2024
1 parent d3b858f commit 45a9885
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 272 deletions.
13 changes: 0 additions & 13 deletions src/helper-modules/govuk-notify/known-errors.ts

This file was deleted.

5 changes: 2 additions & 3 deletions src/modules/emails/emails.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { PostEmailsRequestItemDto } from '@ukef/modules/emails/dto/post-emails-r
export class EmailsService {
constructor(private readonly govukNotifyService: GovukNotifyService) {}

async sendEmail(govUkNotifyKey, postEmailRequestItem: PostEmailsRequestItemDto): Promise<PostEmailsResponseDto> {
const response: PostEmailsResponseDto = await this.govukNotifyService.sendEmail(govUkNotifyKey, postEmailRequestItem);
return response;
sendEmail(govUkNotifyKey, postEmailRequestItem: PostEmailsRequestItemDto): Promise<PostEmailsResponseDto> {
return this.govukNotifyService.sendEmail(govUkNotifyKey, postEmailRequestItem);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { RandomValueGenerator } from '@ukef-test/support/generator/random-value-
import { prepareModifiedRequest } from '@ukef-test/support/helpers/request-field-validation-helper';
import request from 'supertest';

import { withRequiredFieldValidationApiTests } from './partials/require-validation';
import { withTypeFieldValidationApiTests } from './partials/type-validation';

export interface EmailFieldValidationApiTestOptions<RequestBodyItem, RequestBodyItemKey extends keyof RequestBodyItem> {
fieldName: RequestBodyItemKey;
required?: boolean;
Expand Down Expand Up @@ -77,88 +80,21 @@ export const withEmailFieldValidationApiTests = <RequestBodyItem, RequestBodyIte
});
});

describe(`${fieldName} type validation`, () => {
it(`returns a 400 response if ${fieldName} is number`, async () => {
const requestWithNumberField = { ...requestBodyItem, [fieldNameSymbol]: 1 };
const preparedRequestWithNumberField = prepareModifiedRequest(requestIsAnArray, requestWithNumberField);

const { status, body } = await makeRequest(preparedRequestWithNumberField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be ${typeNameForErrorMessages}`]),
statusCode: 400,
});
});

it(`returns a 400 response if ${fieldName} is array`, async () => {
const requestWithArrayField = { ...requestBodyItem, [fieldNameSymbol]: [''] };
const preparedRequestWithArrayField = prepareModifiedRequest(requestIsAnArray, requestWithArrayField);

const { status, body } = await makeRequest(preparedRequestWithArrayField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be ${typeNameForErrorMessages}`]),
statusCode: 400,
});
});
withRequiredFieldValidationApiTests({
fieldName: fieldNameSymbol,
required,
validRequestBody,
makeRequest,
typeNameForErrorMessages,
givenAnyRequestBodyWouldSucceed,
});

describe(`${fieldName} is required validation`, () => {
if (required) {
const expectedRequiredFieldError = `${fieldName} must be ${typeNameForErrorMessages}`;

it(`returns a 400 response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithoutTheField } = requestBodyItem;
const preparedRequestWithoutTheField = prepareModifiedRequest(requestIsAnArray, requestWithoutTheField);

const { status, body } = await makeRequest(preparedRequestWithoutTheField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([expectedRequiredFieldError]),
statusCode: 400,
});
});

it(`returns a 400 response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status, body } = await makeRequest(preparedRequestWithNullField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([expectedRequiredFieldError]),
statusCode: 400,
});
});
} else {
it(`returns a 2xx response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithField } = requestBodyItem;
const preparedRequestWithField = prepareModifiedRequest(requestIsAnArray, requestWithField);

const { status } = await makeRequest(preparedRequestWithField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});

it(`returns a 2xx response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status } = await makeRequest(preparedRequestWithNullField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});
}
withTypeFieldValidationApiTests({
fieldName: fieldNameSymbol,
validRequestBody,
makeRequest,
typeNameForErrorMessages,
givenAnyRequestBodyWouldSucceed,
});
});
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { prepareModifiedRequest } from '@ukef-test/support/helpers/request-field-validation-helper';
import request from 'supertest';

import { withRequiredFieldValidationApiTests } from './partials/require-validation';
import { withTypeFieldValidationApiTests } from './partials/type-validation';

export interface ObjectFieldValidationApiTests<RequestBodyItem, RequestBodyItemKey extends keyof RequestBodyItem> {
fieldName: RequestBodyItemKey;
required?: boolean;
Expand All @@ -17,106 +19,29 @@ export function withObjectFieldValidationApiTests<RequestBodyItem, RequestBodyIt
givenAnyRequestBodyWouldSucceed,
}: ObjectFieldValidationApiTests<RequestBodyItem, RequestBodyItemKey>): void {
const fieldName = fieldNameSymbol.toString();
const requestIsAnArray = Array.isArray(validRequestBody);
const requestBodyItem = requestIsAnArray ? validRequestBody[0] : validRequestBody;

const typeNameForErrorMessages = 'an object';
required = required ?? true;

describe(`${fieldName} validation`, () => {
beforeEach(() => {
givenAnyRequestBodyWouldSucceed();
});

it(`returns a 400 response if ${fieldName} is number`, async () => {
const requestWithNumberField = { ...requestBodyItem, [fieldNameSymbol]: 1 };
const preparedRequestWithNumberField = prepareModifiedRequest(requestIsAnArray, requestWithNumberField);

const { status, body } = await makeRequest(preparedRequestWithNumberField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be an object`]),
statusCode: 400,
});
withRequiredFieldValidationApiTests({
fieldName: fieldNameSymbol,
required,
validRequestBody,
makeRequest,
typeNameForErrorMessages,
givenAnyRequestBodyWouldSucceed,
});

it(`returns a 400 response if ${fieldName} is string`, async () => {
const requestWithStringField = { ...requestBodyItem, [fieldNameSymbol]: '' };
const preparedRequestWithStringField = prepareModifiedRequest(requestIsAnArray, requestWithStringField);

const { status, body } = await makeRequest(preparedRequestWithStringField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be an object`]),
statusCode: 400,
});
withTypeFieldValidationApiTests({
fieldName: fieldNameSymbol,
validRequestBody,
makeRequest,
typeNameForErrorMessages,
givenAnyRequestBodyWouldSucceed,
});

it(`returns a 400 response if ${fieldName} is array`, async () => {
const requestWithArrayField = { ...requestBodyItem, [fieldNameSymbol]: [] };
const preparedRequestWithArrayField = prepareModifiedRequest(requestIsAnArray, requestWithArrayField);

const { status, body } = await makeRequest(preparedRequestWithArrayField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be an object`]),
statusCode: 400,
});
});

if (required) {
it(`returns a 400 response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithoutTheField } = requestBodyItem;
const preparedRequestWithoutTheField = prepareModifiedRequest(requestIsAnArray, requestWithoutTheField);

const { status, body } = await makeRequest(preparedRequestWithoutTheField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be an object`]),
statusCode: 400,
});
});

it(`returns a 400 response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status, body } = await makeRequest(preparedRequestWithNullField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([`${fieldName} must be an object`]),
statusCode: 400,
});
});
} else {
it(`returns a 2xx response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithField } = requestBodyItem;
const preparedRequestWithField = prepareModifiedRequest(requestIsAnArray, requestWithField);

const { status } = await makeRequest(preparedRequestWithField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});

it(`returns a 2xx response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status } = await makeRequest(preparedRequestWithNullField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});
}
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { prepareModifiedRequest } from '@ukef-test/support/helpers/request-field-validation-helper';
import request from 'supertest';

export interface RequiredFieldValidationApiTestOptions<RequestBodyItem, RequestBodyItemKey extends keyof RequestBodyItem> {
fieldName: RequestBodyItemKey;
required?: boolean;
pattern?: RegExp;
enum?: any;
typeNameForErrorMessages: string;
generateFieldValueOfLength?: (length: number) => RequestBodyItem[RequestBodyItemKey];
generateFieldValueThatDoesNotMatchEnum?: () => RequestBodyItem[RequestBodyItemKey];
validRequestBody: RequestBodyItem[] | RequestBodyItem;
makeRequest: ((body: unknown[]) => request.Test) | ((body: unknown) => request.Test);
givenAnyRequestBodyWouldSucceed: () => void;
}

export function withRequiredFieldValidationApiTests<RequestBodyItem, RequestBodyItemKey extends keyof RequestBodyItem>({
fieldName: fieldNameSymbol,
required,
enum: theEnum,
typeNameForErrorMessages,
generateFieldValueThatDoesNotMatchEnum,
validRequestBody,
makeRequest,
givenAnyRequestBodyWouldSucceed,
}: RequiredFieldValidationApiTestOptions<RequestBodyItem, RequestBodyItemKey>): void {
const fieldName = fieldNameSymbol.toString();
const requestIsAnArray = Array.isArray(validRequestBody);
const requestBodyItem = requestIsAnArray ? validRequestBody[0] : validRequestBody;

required = required ?? true;

beforeEach(() => {
givenAnyRequestBodyWouldSucceed();
});

describe(`${fieldName} is required validation`, () => {
if (required) {
const expectedRequiredFieldError =
theEnum && generateFieldValueThatDoesNotMatchEnum
? `${fieldName} must be one of the following values: ${Object.values(theEnum).join(', ')}`
: `${fieldName} must be ${typeNameForErrorMessages}`;

it(`returns a 400 response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithoutTheField } = requestBodyItem;
const preparedRequestWithoutTheField = prepareModifiedRequest(requestIsAnArray, requestWithoutTheField);

const { status, body } = await makeRequest(preparedRequestWithoutTheField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([expectedRequiredFieldError]),
statusCode: 400,
});
});

it(`returns a 400 response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status, body } = await makeRequest(preparedRequestWithNullField);

expect(status).toBe(400);
expect(body).toMatchObject({
error: 'Bad Request',
message: expect.arrayContaining([expectedRequiredFieldError]),
statusCode: 400,
});
});
} else {
it(`returns a 2xx response if ${fieldName} is not present`, async () => {
const { [fieldNameSymbol]: _removed, ...requestWithField } = requestBodyItem;
const preparedRequestWithField = prepareModifiedRequest(requestIsAnArray, requestWithField);

const { status } = await makeRequest(preparedRequestWithField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});

it(`returns a 2xx response if ${fieldName} is null`, async () => {
const requestWithNullField = { ...requestBodyItem, [fieldNameSymbol]: null };
const preparedRequestWithNullField = prepareModifiedRequest(requestIsAnArray, requestWithNullField);

const { status } = await makeRequest(preparedRequestWithNullField);

expect(status).toBeGreaterThanOrEqual(200);
expect(status).toBeLessThan(300);
});
}
});
}
Loading

0 comments on commit 45a9885

Please sign in to comment.