diff --git a/tests/component-tests/apiGateway-tests/createMi.spec.ts b/tests/component-tests/apiGateway-tests/createMi.spec.ts new file mode 100644 index 00000000..0455c06d --- /dev/null +++ b/tests/component-tests/apiGateway-tests/createMi.spec.ts @@ -0,0 +1,110 @@ +import {test, expect} from '@playwright/test'; +import { getRestApiGatewayBaseUrl } from "../../helpers/awsGatewayHelper"; +import { MI_ENDPOINT } from '../../constants/api_constants'; +import { createHeaderWithNoCorrelationId, createHeaderWithNoRequestId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; +import { miInvalidDateRequest, miInvalidRequest, miValidRequest } from './testCases/createMi'; +import { time } from 'console'; +import { error400InvalidDate, error400ResponseBody, error403ResponseBody, error404ResponseBody, requestId500Error } from '../../helpers/commonTypes'; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe('API Gateway Tests to Verify Mi Endpoint', () => { + test(`Post /mi returns 200 when a valid request is passed`, async ({ request }) => { + + const headers = createValidRequestHeaders(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(201); + expect(responseBody.data.attributes).toMatchObject({ + groupId: 'group123', + lineItem: 'envelope-business-standard', + quantity: 10, + specificationId: 'Test-Spec-Id', + stockRemaining: 100, + timestamp: body.data.attributes.timestamp, + }); + expect(responseBody.data.type).toBe('ManagementInformation'); + }); + + test(`Post /mi returns 400 when a invalid request is passed`, async ({ request }) => { + const headers = createValidRequestHeaders(); + const body = miInvalidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(error400ResponseBody()); + }); + + test(`Post /mi returns 403 when a authentication header is not passed`, async ({ request }) => { + const headers = createInvalidRequestHeaders(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(403); + expect(responseBody).toMatchObject(error403ResponseBody()); + }); + + test(`Post /mi returns 500 when a correlationId is not passed`, async ({ request }) => { + const headers = createHeaderWithNoCorrelationId(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const res = await response.json(); + expect(response.status()).toBe(500); + expect(res.errors[0].detail).toBe("The request headers don't contain the APIM correlation id"); + }); + + test(`Post /mi returns 500 when a x-request-id is not passed`, async ({ request }) => { + const headers = createHeaderWithNoRequestId(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(500); + expect(responseBody).toMatchObject(requestId500Error()); + }); + + test(`Post /mi returns 400 when a invalid Date is passed`, async ({ request }) => { + const headers = createValidRequestHeaders(); + const body = miInvalidDateRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers: headers, + data: body + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(error400InvalidDate()); + }); + + +}); diff --git a/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts new file mode 100644 index 00000000..7382cb79 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts @@ -0,0 +1,71 @@ +import { test, expect } from '@playwright/test'; +import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; +import { getLettersBySupplier } from '../../helpers/generate_fetch_testData'; +import { SUPPLIER_LETTERS, SUPPLIERID } from '../../constants/api_constants'; +import { createValidRequestHeaders } from '../../constants/request_headers'; +import { error404ResponseBody, error500ResponseBody } from '../../helpers/commonTypes'; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe('API Gateway Tests to Verify Get Letter Status Endpoint', () => { + test(`Get /letters/{id} returns 200 and valid response for a given id`, async ({ request }) => { + + const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); + + if (!letters?.length) { + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); + return; + } + const letter = letters[0]; + const headers = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { + headers: headers, + }); + + const responseBody = await response.json(); + + expect(response.status()).toBe(200); + expect(responseBody).toMatchObject({ + data:{ + attributes: { + status: 'PENDING', + specificationId: letter.specificationId, + groupId: letter.groupId, + }, + id: letter.id, + type: 'Letter' + } + }); + }); + + test(`Get /letters/{id} returns 404 if no resource is found for id`, async ({ request }) => + { + const id = '11'; + const headers = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers: headers, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(404); + expect(responseBody).toMatchObject(error404ResponseBody()); + }); + + test(`Get /letters/{id} returns 500 if letter is not found for supplierId ${SUPPLIERID}`, async ({ request }) => + { + const id = 'non-existing-id-12345'; + const headers = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers: headers, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(500); + expect(responseBody).toMatchObject(error500ResponseBody(id)); + }); + +}); diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 08b30252..b21fa5b9 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -3,7 +3,6 @@ import { SUPPLIER_LETTERS } from '../../constants/api_constants'; import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; import { validateApiResponse } from '../../helpers/validateJsonSchema'; -import { link } from 'fs'; let baseUrl: string; diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index f747f03e..875541b1 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -1,6 +1,6 @@ import { RequestHeaders } from '../../../constants/request_headers'; -import { supplierId } from '../../../constants/api_constants'; +import { SUPPLIERID } from '../../../constants/api_constants'; import { ErrorMessageBody } from '../../../helpers/commonTypes'; export type PatchMessageRequestBody = { @@ -33,7 +33,7 @@ export function patchRequestHeaders(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': supplierId, + 'NHSD-Supplier-ID': SUPPLIERID, 'NHSD-Correlation-ID': '12344', 'X-Request-ID': 'requestId1' }; @@ -106,7 +106,7 @@ export function patch500ErrorResponseBody (id: string) : ErrorMessageBody{ }, status: "500", title: "Internal server error", - detail: `Letter with id ${id} not found for supplier ${supplierId}` + detail: `Letter with id ${id} not found for supplier ${SUPPLIERID}` } ] }; diff --git a/tests/component-tests/apiGateway-tests/testCases/createMi.ts b/tests/component-tests/apiGateway-tests/testCases/createMi.ts new file mode 100644 index 00000000..f7f0b222 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/testCases/createMi.ts @@ -0,0 +1,70 @@ + + + +export type MiRequestBody = { + data: { + type: string; + attributes: { + groupId: string; + lineItem: string; + quantity: number; + specificationId: string; + stockRemaining: number; + timestamp: string; + }; + }; +}; + +export function miValidRequest() : MiRequestBody{ + let requestBody: MiRequestBody; + + requestBody = { + data: { +  attributes: { + groupId: 'group123', + lineItem: 'envelope-business-standard', + quantity: 10, + specificationId: 'Test-Spec-Id', + stockRemaining: 100, + timestamp: new Date().toISOString(), + }, + type: 'ManagementInformation', + }}; + return requestBody; +} + +export function miInvalidRequest() : MiRequestBody{ + let requestBody: MiRequestBody; + + requestBody = { + data: { +  attributes: { + groupId: 'group123', + lineItem: 'envelope-business-standard', + quantity: 10, + specificationId: 'Test-Spec-Id', + stockRemaining: 100, + timestamp: new Date().toISOString(), + }, + type: '?', + }}; + return requestBody; +} + +export function miInvalidDateRequest() : MiRequestBody{ + let requestBody: MiRequestBody; + + requestBody = { + data: { +  attributes: { + groupId: 'group123', + lineItem: 'envelope-business-standard', + quantity: 10, + specificationId: 'Test-Spec-Id', + stockRemaining: 100, + timestamp: '2021-10-28T', + }, + type: 'ManagementInformation', + }}; + return requestBody; +} diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts index c7211ca9..99484bf0 100644 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -1,10 +1,11 @@ import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS, supplierId } from '../../constants/api_constants'; +import { SUPPLIER_LETTERS, SUPPLIERID } from '../../constants/api_constants'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { patch400ErrorResponseBody, patch500ErrorResponseBody, patchFailureRequestBody, patchRequestHeaders, patchValidRequestBody } from './testCases/UpdateLetterStatus'; +import { patch400ErrorResponseBody, patch500ErrorResponseBody, patchFailureRequestBody, patchRequestHeaders, patchValidRequestBody } from './testCases/updateLetterStatus'; import { createTestData, deleteLettersBySupplier, getLettersBySupplier } from '../../helpers/generate_fetch_testData'; import { randomUUID } from 'crypto'; import { createInvalidRequestHeaders } from '../../constants/request_headers'; +import { error403ResponseBody } from '../../helpers/commonTypes'; let baseUrl: string; @@ -15,11 +16,11 @@ test.beforeAll(async () => { test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { test(`Patch /letters returns 200 and status is updated to ACCEPTED`, async ({ request }) => { - await createTestData(supplierId); - const letters = await getLettersBySupplier(supplierId, 'PENDING', 1); + await createTestData(SUPPLIERID); + const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); if (!letters?.length) { - test.fail(true, `No PENDING letters found for supplier ${supplierId}`); + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); return; } const letter = letters[0]; @@ -31,9 +32,9 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data: body }); - const res = await response.json(); + const responseBody = await response.json(); expect(response.status()).toBe(200); - expect(res).toMatchObject({ + expect(responseBody).toMatchObject({ data:{ attributes: { status: 'ACCEPTED', @@ -50,11 +51,11 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { test(`Patch /letters returns 200 and status is updated to REJECTED`, async ({ request }) => { - await createTestData(supplierId); - const letters = await getLettersBySupplier(supplierId, 'PENDING', 1); + await createTestData(SUPPLIERID); + const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); if (!letters?.length) { - test.fail(true, `No PENDING letters found for supplier ${supplierId}`); + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); return; } const letter = letters[0]; @@ -66,9 +67,9 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data: body }); - const res = await response.json(); + const responseBody = await response.json(); expect(response.status()).toBe(200); - expect(res).toMatchObject({ + expect(responseBody).toMatchObject({ data:{ attributes: { status: 'REJECTED', @@ -94,10 +95,10 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data: body }); - const res = await response.json(); + const responseBody = await response.json(); expect(response.status()).toBe(400); - expect(res).toMatchObject(patch400ErrorResponseBody()); + expect(responseBody).toMatchObject(patch400ErrorResponseBody()); }); test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => { @@ -110,13 +111,13 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data: body }); - const res = await response.json(); + const responseBody = await response.json(); expect(response.status()).toBe(500); - expect(res).toMatchObject(patch500ErrorResponseBody(id)); + expect(responseBody).toMatchObject(patch500ErrorResponseBody(id)); }); test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => { - const headers = await createInvalidRequestHeaders(); + const headers = createInvalidRequestHeaders(); const id = randomUUID() const body = patchValidRequestBody(id, 'PENDING'); @@ -125,7 +126,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data: body }); - const res = await response.json(); + const responseBody = await response.json(); expect(response.status()).toBe(403); + expect(responseBody).toMatchObject(error403ResponseBody()); }); }); diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index fc7f3487..53e1d703 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1,10 +1,8 @@ export const SUPPLIER_LETTERS = 'letters'; export const SUPPLIER_API_URL_SANDBOX = 'https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier'; export const AWS_REGION = 'eu-west-2'; - export const envName = process.env.PR_NUMBER ?? 'main'; export const API_NAME = `nhs-${envName}-supapi`; export const LETTERSTABLENAME = `nhs-${envName}-supapi-letters`; - -// Test -export const supplierId = 'TestSupplier1'; +export const SUPPLIERID = 'TestSupplier1'; +export const MI_ENDPOINT = 'mi'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index 02e2d33b..5440f818 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -1,5 +1,5 @@ import { randomUUID } from 'node:crypto'; -import { supplierId } from './api_constants'; +import { SUPPLIERID } from './api_constants'; export const sandBoxHeader: RequestSandBoxHeaders = { 'X-Request-ID': randomUUID(), @@ -25,7 +25,7 @@ export function createInvalidRequestHeaders(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: '', - 'NHSD-Supplier-ID': supplierId, + 'NHSD-Supplier-ID': SUPPLIERID, 'NHSD-Correlation-ID': '1234', 'X-Request-ID': 'requestId1' }; @@ -36,7 +36,7 @@ export function createHeaderWithNoCorrelationId(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': supplierId, + 'NHSD-Supplier-ID': SUPPLIERID, 'NHSD-Correlation-ID': '', 'X-Request-ID': 'requestId1' }; @@ -47,9 +47,20 @@ export function createValidRequestHeaders(): RequestHeaders{ let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': supplierId, + 'NHSD-Supplier-ID': SUPPLIERID, 'NHSD-Correlation-ID': '12345', 'X-Request-ID': 'requestId1' }; return requestHeaders; } + +export function createHeaderWithNoRequestId(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': SUPPLIERID, + 'NHSD-Correlation-ID': '1234', + 'X-Request-ID': '' + }; + return requestHeaders; +} diff --git a/tests/helpers/commonTypes.ts b/tests/helpers/commonTypes.ts index 465ea0ff..c76ef4f7 100644 --- a/tests/helpers/commonTypes.ts +++ b/tests/helpers/commonTypes.ts @@ -1,3 +1,6 @@ +import { error } from "console"; +import { SUPPLIERID } from "../constants/api_constants"; + export type ErrorLink = { about: string; }; @@ -14,3 +17,108 @@ type ErrorResponse = { export type ErrorMessageBody = { errors: ErrorResponse[]; }; + +export function error404ResponseBody () : ErrorMessageBody{ + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_LETTER_NOT_FOUND", + links: { + "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: "404", + title: "Not found", + detail: "No resource found with that ID" + } + ] + }; + return responseBody; +}; + +export function error500ResponseBody (id: string) : ErrorMessageBody{ + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_INTERNAL_SERVER_ERROR", + links: { + "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: "500", + title: "Internal server error", + detail: `Letter with id ${id} not found for supplier ${SUPPLIERID}` + } + ] + }; + return responseBody; +} + + +export function error400ResponseBody () : ErrorMessageBody{ + + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: '12345', + code: "NOTIFY_INVALID_REQUEST", + links: { + "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: "400", + title: "Invalid request", + detail: "The request body is invalid" + } + ] + }; + return responseBody; +} + +export function requestId500Error () : ErrorMessageBody{ + let responseBody: ErrorMessageBody; + + responseBody = { + errors: [ + { + id: "1234", + code: "NOTIFY_INTERNAL_SERVER_ERROR", + links: { + "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: "500", + title: "Internal server error", + detail: "The request headers don't contain the x-request-id" + } + ] + }; + return responseBody; +} + +export function error403ResponseBody () : { Message: string }{ + return { + Message : 'User is not authorized to access this resource with an explicit deny in an identity-based policy' + }; +} + +export function error400InvalidDate () : ErrorMessageBody{ + + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: '12345', + code: "NOTIFY_INVALID_REQUEST", + links: { + "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: "400", + title: "Invalid request", + detail: "Timestamps should be UTC date/times in ISO8601 format, with a Z suffix" + } + ] + }; + return responseBody; +} diff --git a/tests/helpers/generate_fetch_testData.ts b/tests/helpers/generate_fetch_testData.ts index 3dafa9a9..2fd63aa6 100644 --- a/tests/helpers/generate_fetch_testData.ts +++ b/tests/helpers/generate_fetch_testData.ts @@ -1,4 +1,4 @@ -import { envName, LETTERSTABLENAME, supplierId } from "../constants/api_constants"; +import { envName, LETTERSTABLENAME, SUPPLIERID } from "../constants/api_constants"; import { runCreateLetter } from "./pnpmHelpers"; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DeleteCommand, DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb'; @@ -62,7 +62,7 @@ export const deleteLettersBySupplier = async(id: string) => { const resp = await docClient.send( new DeleteCommand({ TableName: LETTERSTABLENAME, - Key: { supplierId, id }, + Key: { supplierId: SUPPLIERID, id }, ReturnValues: 'ALL_OLD', }) )