Skip to content

Commit 95c9408

Browse files
CCM 12179: Add Component Tests (#194)
* test * Comp tests * Comp tests * component tests * test * Comp tests * Comp tests * component tests * review comments * tests on pipeline * dependencies * tests * revert test data changes * fix * fix-cicd * fix * to discuss schema * to discuss schema * Remove test from pipeline * Remove test from pipeline * amend workflow * Review Changes * review fix * review fixes
1 parent 6a95229 commit 95c9408

28 files changed

+2016
-211
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ config:: _install-dependencies version # Configure development environment (main
9898
npm install
9999
(cd docs && make install && cd ..)
100100

101+
test-component:
102+
(cd tests && npm install && npm run test:component)
101103

102104
version:
103105
rm -f .version

package-lock.json

Lines changed: 774 additions & 127 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
{
22
"dependencies": {
3+
"@aws-sdk/client-api-gateway": "^3.906.0",
4+
"@playwright/test": "^1.55.1",
5+
"ajv": "^8.17.1",
6+
"js-yaml": "^4.1.0",
7+
"openapi-response-validator": "^12.1.3",
38
"serve": "^14.2.4"
49
},
510
"devDependencies": {
611
"@openapitools/openapi-generator-cli": "^2.21.4",
712
"@redocly/cli": "^1.34.5",
813
"@tsconfig/node22": "^22.0.2",
914
"@types/jest": "^29.5.14",
15+
"@types/js-yaml": "^4.0.9",
1016
"@typescript-eslint/eslint-plugin": "^8.27.0",
1117
"@typescript-eslint/parser": "^8.27.0",
1218
"esbuild": "^0.24.0",

tests/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ node_modules/
77
/playwright/.cache/
88
/allure-results
99
/target
10+
/playwright/.auth/
11+
/allure-report
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { test, expect } from '@playwright/test';
2+
import { SUPPLIER_LETTERS } from '../../constants/api_constants';
3+
import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers';
4+
import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper';
5+
import { validateApiResponse } from '../../helpers/validateJsonSchema';
6+
import { link } from 'fs';
7+
8+
let baseUrl: string;
9+
10+
test.beforeAll(async () => {
11+
baseUrl = await getRestApiGatewayBaseUrl();
12+
});
13+
14+
test.describe('API Gateway Tests To Get List Of Pending Letters', () =>
15+
{
16+
test('GET /letters should return 200 and list items', async ({ request }) =>
17+
{
18+
const header = createValidRequestHeaders();
19+
const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{
20+
headers: header,
21+
params: {
22+
limit:'2'},
23+
},
24+
);
25+
26+
expect(response.status()).toBe(200);
27+
const responseBody = await response.json();
28+
expect(responseBody.data.length.toString()).toEqual('2');
29+
30+
const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody);
31+
if (validationResult) {
32+
console.error("API response validation failed:", validationResult);
33+
}
34+
expect(validationResult).toBeUndefined();
35+
});
36+
37+
test('GET /letters with invalid authentication should return 403', async ({ request }) => {
38+
const header = createInvalidRequestHeaders();
39+
const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{
40+
headers: header,
41+
params:{
42+
limit:'2'
43+
},
44+
},
45+
);
46+
expect(response.status()).toBe(403);
47+
const responseBody = await response.json();
48+
expect(responseBody).toMatchObject({
49+
Message : 'User is not authorized to access this resource with an explicit deny in an identity-based policy' }
50+
);
51+
});
52+
53+
test('GET /letters with empty correlationId should return 500', async ({ request }) => {
54+
const header = createHeaderWithNoCorrelationId();
55+
const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{
56+
headers: header,
57+
params:{
58+
limit:'2'
59+
},
60+
},
61+
);
62+
expect(response.status()).toBe(500);
63+
const responseBody = await response.json();
64+
expect(responseBody.errors[0].code).toBe('NOTIFY_INTERNAL_SERVER_ERROR');
65+
expect(responseBody.errors[0].detail).toBe("The request headers don't contain the APIM correlation id");
66+
67+
});
68+
69+
test('GET /letters with invalid query param return 400', async ({ request }) => {
70+
const header = createValidRequestHeaders();
71+
const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{
72+
headers: header,
73+
params:{
74+
limit:'?'
75+
},
76+
});
77+
expect(response.status()).toBe(400);
78+
const responseBody = await response.json();
79+
expect(responseBody).toMatchObject({
80+
errors: [
81+
{
82+
id: '12345',
83+
code: 'NOTIFY_INVALID_REQUEST',
84+
links: {
85+
about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier'
86+
},
87+
status: '400',
88+
title: 'Invalid request',
89+
detail: 'The limit parameter is not a number'
90+
}
91+
]
92+
});
93+
});
94+
});
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
import { RequestHeaders } from '../../../constants/request_headers';
3+
import { supplierId } from '../../../constants/api_constants';
4+
import { ErrorMessageBody } from '../../../helpers/commonTypes';
5+
6+
export type PatchMessageRequestBody = {
7+
data: {
8+
type: string;
9+
id: string;
10+
attributes: {
11+
reasonCode?: string | number;
12+
reasonText?: string;
13+
status: string;
14+
};
15+
};
16+
};
17+
18+
export type PatchMessageResponseBody = {
19+
data: {
20+
type: string;
21+
id: string;
22+
attributes: {
23+
reasonCode?: number;
24+
reasonText?: string;
25+
status: string;
26+
specificationId:string;
27+
groupId?:string;
28+
};
29+
};
30+
};
31+
32+
export function patchRequestHeaders(): RequestHeaders {
33+
let requestHeaders: RequestHeaders;
34+
requestHeaders = {
35+
headerauth1: process.env.HEADERAUTH || '',
36+
'NHSD-Supplier-ID': supplierId,
37+
'NHSD-Correlation-ID': '12344',
38+
'X-Request-ID': 'requestId1'
39+
};
40+
return requestHeaders;
41+
};
42+
43+
44+
export function patchValidRequestBody (id: string, status: string) : PatchMessageRequestBody{
45+
let requestBody: PatchMessageRequestBody;
46+
47+
requestBody = {
48+
data: {
49+
 attributes: {
50+
  status: status,
51+
},
52+
type: 'Letter',
53+
id: id
54+
}
55+
56+
};
57+
return requestBody;
58+
}
59+
60+
export function patchFailureRequestBody (id: string, status: string) : PatchMessageRequestBody{
61+
let requestBody: PatchMessageRequestBody;
62+
63+
requestBody = {
64+
data: {
65+
attributes: {
66+
status: status,
67+
reasonCode: 123,
68+
reasonText: 'Test Reason'
69+
},
70+
type: 'Letter',
71+
id: id
72+
}
73+
74+
};
75+
return requestBody;
76+
}
77+
78+
export function patch400ErrorResponseBody () : ErrorMessageBody{
79+
let responseBody: ErrorMessageBody;
80+
responseBody = {
81+
errors: [
82+
{
83+
id : '12344',
84+
code : 'NOTIFY_INVALID_REQUEST',
85+
"links": {
86+
"about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier"
87+
},
88+
"status": "400",
89+
"title": "Invalid request",
90+
"detail": "The request body is invalid"
91+
}
92+
]
93+
};
94+
return responseBody;
95+
};
96+
97+
export function patch500ErrorResponseBody (id: string) : ErrorMessageBody{
98+
let responseBody: ErrorMessageBody;
99+
responseBody = {
100+
errors: [
101+
{
102+
id: "12344",
103+
code: "NOTIFY_INTERNAL_SERVER_ERROR",
104+
links: {
105+
"about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier"
106+
},
107+
status: "500",
108+
title: "Internal server error",
109+
detail: `Letter with id ${id} not found for supplier ${supplierId}`
110+
}
111+
]
112+
};
113+
return responseBody;
114+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { test, expect } from '@playwright/test';
2+
import { SUPPLIER_LETTERS, supplierId } from '../../constants/api_constants';
3+
import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper';
4+
import { patch400ErrorResponseBody, patch500ErrorResponseBody, patchFailureRequestBody, patchRequestHeaders, patchValidRequestBody } from './testCases/UpdateLetterStatus';
5+
import { createTestData, deleteLettersBySupplier, getLettersBySupplier } from '../../helpers/generate_fetch_testData';
6+
import { randomUUID } from 'crypto';
7+
import { createInvalidRequestHeaders } from '../../constants/request_headers';
8+
9+
let baseUrl: string;
10+
11+
test.beforeAll(async () => {
12+
baseUrl = await getRestApiGatewayBaseUrl();
13+
});
14+
15+
test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => {
16+
test(`Patch /letters returns 200 and status is updated to ACCEPTED`, async ({ request }) => {
17+
18+
await createTestData(supplierId);
19+
const letters = await getLettersBySupplier(supplierId, 'PENDING', 1);
20+
21+
if (!letters?.length) {
22+
test.fail(true, `No PENDING letters found for supplier ${supplierId}`);
23+
return;
24+
}
25+
const letter = letters[0];
26+
const headers = patchRequestHeaders();
27+
const body = patchValidRequestBody(letter.id, 'ACCEPTED');
28+
29+
const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, {
30+
headers: headers,
31+
data: body
32+
});
33+
34+
const res = await response.json();
35+
expect(response.status()).toBe(200);
36+
expect(res).toMatchObject({
37+
data:{
38+
attributes: {
39+
status: 'ACCEPTED',
40+
specificationId: letter.specificationId,
41+
groupId: letter.groupId,
42+
},
43+
id: letter.id,
44+
type: 'Letter'
45+
}
46+
});
47+
48+
await deleteLettersBySupplier(letter.id);
49+
});
50+
51+
test(`Patch /letters returns 200 and status is updated to REJECTED`, async ({ request }) => {
52+
53+
await createTestData(supplierId);
54+
const letters = await getLettersBySupplier(supplierId, 'PENDING', 1);
55+
56+
if (!letters?.length) {
57+
test.fail(true, `No PENDING letters found for supplier ${supplierId}`);
58+
return;
59+
}
60+
const letter = letters[0];
61+
const headers = patchRequestHeaders();
62+
const body = patchFailureRequestBody(letter.id, 'REJECTED');
63+
64+
const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, {
65+
headers: headers,
66+
data: body
67+
});
68+
69+
const res = await response.json();
70+
expect(response.status()).toBe(200);
71+
expect(res).toMatchObject({
72+
data:{
73+
attributes: {
74+
status: 'REJECTED',
75+
specificationId: letter.specificationId,
76+
groupId: letter.groupId,
77+
},
78+
id: letter.id,
79+
type: 'Letter'
80+
}
81+
});
82+
83+
await deleteLettersBySupplier(letter.id);
84+
});
85+
86+
test(`Patch /letters returns 400 if request Body is invalid`, async ({ request }) => {
87+
88+
const id = randomUUID()
89+
const headers = patchRequestHeaders();
90+
const body = patchValidRequestBody(id, '');
91+
92+
const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, {
93+
headers: headers,
94+
data: body
95+
});
96+
97+
const res = await response.json();
98+
99+
expect(response.status()).toBe(400);
100+
expect(res).toMatchObject(patch400ErrorResponseBody());
101+
});
102+
103+
test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => {
104+
const headers = patchRequestHeaders();
105+
const id = randomUUID()
106+
const body = patchValidRequestBody(id, 'PENDING');
107+
108+
const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, {
109+
headers: headers,
110+
data: body
111+
});
112+
113+
const res = await response.json();
114+
expect(response.status()).toBe(500);
115+
expect(res).toMatchObject(patch500ErrorResponseBody(id));
116+
});
117+
118+
test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => {
119+
const headers = await createInvalidRequestHeaders();
120+
const id = randomUUID()
121+
const body = patchValidRequestBody(id, 'PENDING');
122+
123+
const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, {
124+
headers: headers,
125+
data: body
126+
});
127+
128+
const res = await response.json();
129+
expect(response.status()).toBe(403);
130+
});
131+
});

0 commit comments

Comments
 (0)