From 345d6872c466d87c4cceb7a1f8d25f2c28d62a36 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 1 Oct 2025 13:58:34 +0100 Subject: [PATCH 01/24] test --- .../updateLetterStatus.spec.ts | 23 +++++++++++++++++++ tests/package.json | 3 +-- tests/test_Data/updateStatus.json | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts create mode 100644 tests/test_Data/updateStatus.json diff --git a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts new file mode 100644 index 00000000..2e75d61b --- /dev/null +++ b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts @@ -0,0 +1,23 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { json } from 'stream/consumers'; + +test.describe('API Gateway', () => { + test('GET /items should return 200 and list items', async ({ request }) => { + + const response = await request.get( + 'https://8mu4ycde02.execute-api.eu-west-2.amazonaws.com/main/letters',{ + params:{ + limit:'2' + }, + headers: { + 'headerauth1': 'headervalue1', + 'nhsd-supplier-id':'70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'nhsd-correlation-id':'1234', + }, + }); + + expect(response.status()).toBe(200); + const responseBody = await response.json(); + console.log(JSON.stringify(responseBody, null, 2)); + }); +}); diff --git a/tests/package.json b/tests/package.json index 53926dff..52113653 100644 --- a/tests/package.json +++ b/tests/package.json @@ -5,7 +5,6 @@ "charenc": "^0.0.2", "crypt": "^0.0.2", "dotenv": "^17.2.2", - "fsevents": "^2.3.2", "is-buffer": "^1.1.6", "md5": "^2.3.0", "playwright": "^1.54.2", @@ -14,7 +13,7 @@ }, "description": "", "devDependencies": { - "@playwright/test": "^1.55.0", + "@playwright/test": "^1.55.1", "@types/node": "^24.3.1", "allure-commandline": "^2.34.1", "allure-playwright": "^3.3.3" diff --git a/tests/test_Data/updateStatus.json b/tests/test_Data/updateStatus.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/test_Data/updateStatus.json @@ -0,0 +1 @@ +{} From 64e9995933dc6b7448379d0d311619ac7ede3da0 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 9 Oct 2025 09:56:21 +0100 Subject: [PATCH 02/24] Comp tests --- package-lock.json | 1103 +++++++++++++---- package.json | 6 + tests/.gitignore | 1 + .../apiGateway-tests/getLetters.spec.ts | 90 ++ .../testCases/UpdateLetterStatus.ts | 117 ++ .../updateLetterStatus.spec.ts | 28 + .../updateLetterStatus.spec.ts | 23 - tests/config/main.config.ts | 8 +- tests/config/playwright.base.config.ts | 4 +- tests/config/sandbox.config.ts | 19 + tests/constants/api_constants.ts | 6 +- tests/constants/request_headers.ts | 55 + tests/helpers/awsGatewayHelper.ts | 18 + tests/helpers/validateJsonSchema.ts | 39 + tests/package-lock.json | 186 +++ tests/package.json | 3 +- .../get_single_letter/test_401.spec.ts | 7 +- .../get_single_letter/test_success.spec.ts | 20 + .../get_single_letter/test_success.spec.ts | 26 - 19 files changed, 1469 insertions(+), 290 deletions(-) create mode 100644 tests/component-tests/apiGateway-tests/getLetters.spec.ts create mode 100644 tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts create mode 100644 tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts delete mode 100644 tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts create mode 100644 tests/config/sandbox.config.ts create mode 100644 tests/constants/request_headers.ts create mode 100644 tests/helpers/awsGatewayHelper.ts create mode 100644 tests/helpers/validateJsonSchema.ts create mode 100644 tests/package-lock.json rename tests/sandbox/{messages => }/get_single_letter/test_401.spec.ts (62%) create mode 100644 tests/sandbox/get_single_letter/test_success.spec.ts delete mode 100644 tests/sandbox/messages/get_single_letter/test_success.spec.ts diff --git a/package-lock.json b/package-lock.json index 4c9878f5..70a5a18b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,11 @@ "docs" ], "dependencies": { + "@aws-sdk/client-api-gateway": "^3.906.0", + "@playwright/test": "^1.55.1", + "ajv": "^8.17.1", + "js-yaml": "^4.1.0", + "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { @@ -20,6 +25,7 @@ "@redocly/cli": "^1.34.5", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.24.0", @@ -352,6 +358,525 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-api-gateway": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-api-gateway/-/client-api-gateway-3.906.0.tgz", + "integrity": "sha512-7iD94D2OWvP6qTdeUNgfnzk7El+ZurdXXaGx5vSTWRz7Hs32MI+0ZLPKQ/2NCeaMZu12YpxQiDBYO6MZ/H5fTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-node": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-sdk-api-gateway": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sso": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.906.0.tgz", + "integrity": "sha512-GGDwjW2cLzoEF5A1tBlZQZXzhlZzuM6cKNbSxUsCcBXtPAX03eb2GKApVy1SzpD03nTJk5T6GicGAm+BzK+lEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/core": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.906.0.tgz", + "integrity": "sha512-+FuwAcozee8joVfjwly/8kSFNCvQOkcQYjINUckqBkdjO4iCRfOgSaz+0JMpMcYgVPnnyZv62gJ2g0bj0U+YDQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.906.0.tgz", + "integrity": "sha512-vtMDguMci2aXhkgEqg1iqyQ7vVcafpx9uypksM6FQsNr3Cc/8I6HgfBAja6BuPwkaCn9NoMnG0/iuuOWr8P9dg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.906.0.tgz", + "integrity": "sha512-L97N2SUkZp03s1LJZ1sCkUaUZ7m9T72faaadn05wyst/iXonSZKPHYMQVWGYhTC2OtRV0FQvBXIAqFZsNGQD0Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.906.0.tgz", + "integrity": "sha512-r7TbHD80WXo42kTEC5bqa4b87ho3T3yd2VEKo1qbEmOUovocntO8HC3JxHYr0XSeZ82DEYxLARb84akWjabPzg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.906.0.tgz", + "integrity": "sha512-xga127vP0rFxiHjEUjLe6Yf4hQ/AZinOF4AqQr/asWQO+/uwh3aH8nXcS4lkpZNygxMHbuNXm7Xg504GKCMlLQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-ini": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.906.0.tgz", + "integrity": "sha512-P8R4GpDLppe+8mp+SOj1fKaY3AwDULCi/fqMSJjvf8qN6OM+vGGpFP3iXvkjFYyyV+8nRXY+HQCLRoZKpRtzMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.906.0.tgz", + "integrity": "sha512-wYljHU7yNEzt7ngZZ21FWh+RlO16gTpWvXyRqlryuCgIWugHD8bl7JphGnUN1md5/v+mCRuGK58JoFGZq+qrjA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.906.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/token-providers": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.906.0.tgz", + "integrity": "sha512-V9PurepVko8+iyEvI9WAlk5dXJ1uWIW03RPLnNBEmeCqFjjit16HrNaaVvnp9fQbG7CSKSGqK026SjDgtKGKYA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.901.0.tgz", + "integrity": "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-logger": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.901.0.tgz", + "integrity": "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.901.0.tgz", + "integrity": "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.906.0.tgz", + "integrity": "sha512-CMAjq2oCEv5EEvmlFvio8t4KQL2jGORyDQu7oLj4l0a2biPgxbwL3utalbm9yKty1rQM5zKpaa7id7ZG3X1f6A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/nested-clients": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.906.0.tgz", + "integrity": "sha512-0/r0bh/9Bm14lVe+jAzQQB2ufq9S4Vd9Wg5rZn8RhrhKl6y/DC1aRzOo2kJTNu5pCbVfQsd/VXLLnkcbOrDy6A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.901.0.tgz", + "integrity": "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/token-providers": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.906.0.tgz", + "integrity": "sha512-gdxXleCjMUAKnyR/1ksdnv3Fuifr9iuaeEtINRHkwVluwcORabEdOlxW36th2QdkpTTyP1hW35VATz2R6v/i2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-endpoints": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.901.0.tgz", + "integrity": "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", + "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.906.0.tgz", + "integrity": "sha512-9Gaglw80E9UZ5FctCp5pZAzT40/vC4Oo0fcNXsfplLkpWqTU+NTdTRMYe3TMZ1/v1/JZKuGUVyHiuo/xLu3NmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/xml-builder": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.901.0.tgz", + "integrity": "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/@aws-sdk/client-dynamodb": { "version": "3.864.0", "license": "Apache-2.0", @@ -1432,6 +1957,34 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-api-gateway": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-api-gateway/-/middleware-sdk-api-gateway-3.901.0.tgz", + "integrity": "sha512-Bfn8ciaiPdiuZZpIVwTG2aGuLxSXuOKJNFVEFNmIdFhEU51b4yVQGzcdvgFeohfulCz05cL1Uc/f6cxWQoHEaQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-api-gateway/node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/middleware-sdk-s3": { "version": "3.896.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.896.0.tgz", @@ -4013,6 +4566,21 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", + "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.55.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "dev": true, @@ -4269,12 +4837,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", - "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", + "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4307,15 +4875,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", - "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", + "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4323,20 +4891,20 @@ } }, "node_modules/@smithy/core": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.12.0.tgz", - "integrity": "sha512-zJeAgogZfbwlPGL93y4Z/XNeIN37YCreRUd6YMIRvaq+6RnBK8PPYYIQ85Is/GglPh3kNImD5riDCXbVSDpCiQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", + "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/uuid": "^1.0.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.5.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -4344,15 +4912,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", - "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", + "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4430,15 +4998,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", - "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", + "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -4461,14 +5029,14 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", - "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", + "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4490,12 +5058,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", - "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", + "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4503,9 +5071,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", - "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4529,13 +5097,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", - "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", + "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4543,18 +5111,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.4.tgz", - "integrity": "sha512-FZ4hzupOmthm8Q8ujYrd0I+/MHwVMuSTdkDtIQE0xVuvJt9pLT6Q+b0p4/t+slDyrpcf+Wj7SN+ZqT5OryaaZg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", + "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.12.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-middleware": "^4.1.1", + "@smithy/core": "^3.15.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4562,19 +5130,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.3.0.tgz", - "integrity": "sha512-qhEX9745fAxZvtLM4bQJAVC98elWjiMO2OiHl1s6p7hUzS4QfZO1gXUYNwEK8m0J6NoCD5W52ggWxbIDHI0XSg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", + "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/service-error-classification": "^4.1.2", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/uuid": "^1.0.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -4582,13 +5150,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", - "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", + "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4596,12 +5164,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", - "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", + "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4609,14 +5177,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", - "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", + "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4624,15 +5192,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", - "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", + "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4640,12 +5208,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", - "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", + "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4653,12 +5221,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", - "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", + "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4666,13 +5234,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", - "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", + "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-uri-escape": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4680,12 +5248,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", - "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", + "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4693,24 +5261,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.2.tgz", - "integrity": "sha512-Kqd8wyfmBWHZNppZSMfrQFpc3M9Y/kjyN8n8P4DqJJtuwgK1H914R471HTw7+RL+T7+kI1f1gOnL7Vb5z9+NgQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", + "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0" + "@smithy/types": "^4.6.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", - "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", + "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4718,18 +5286,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", - "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", + "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-uri-escape": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4737,17 +5305,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.4.tgz", - "integrity": "sha512-qL7O3VDyfzCSN9r+sdbQXGhaHtrfSJL30En6Jboj0I3bobf2g1/T0eP2L4qxqrEW26gWhJ4THI4ElVVLjYyBHg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", + "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.12.0", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "@smithy/core": "^3.15.0", + "@smithy/middleware-endpoint": "^4.3.1", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4755,9 +5323,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", - "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", + "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4767,13 +5335,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", - "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", + "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/querystring-parser": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4781,13 +5349,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", - "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4795,9 +5363,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", - "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4807,9 +5375,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", - "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4819,12 +5387,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", - "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4832,9 +5400,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", - "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4844,15 +5412,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.4.tgz", - "integrity": "sha512-mLDJ1s4eA3vwOGaQOEPlg5LB4LdZUUMpB5UMOMofeGhWqiS7WR7dTpLiNi9zVn+YziKUd3Af5NLfxDs7NJqmIw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", + "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "bowser": "^2.11.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4860,17 +5427,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.4.tgz", - "integrity": "sha512-pjX2iMTcOASaSanAd7bu6i3fcMMezr3NTr8Rh64etB0uHRZi+Aw86DoCxPESjY4UTIuA06hhqtTtw95o//imYA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", + "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.2.2", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4878,13 +5445,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", - "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", + "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4892,9 +5459,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", - "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4904,12 +5471,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", - "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", + "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4917,13 +5484,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.2.tgz", - "integrity": "sha512-NCgr1d0/EdeP6U5PSZ9Uv5SMR5XRRYoVr1kRVtKZxWL3tixEL3UatrPIMFZSKwHlCcp2zPLDvMubVDULRqeunA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", + "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.1.2", - "@smithy/types": "^4.5.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4931,18 +5498,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.2.tgz", - "integrity": "sha512-Ka+FA2UCC/Q1dEqUanCdpqwxOFdf5Dg2VXtPtB1qxLcSGh5C1HdzklIt18xL504Wiy9nNUKwDMRTVCbKGoK69g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", + "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/fetch-http-handler": "^5.3.1", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4950,9 +5517,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", - "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4962,12 +5529,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", - "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4989,9 +5556,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.0.0.tgz", - "integrity": "sha512-OlA/yZHh0ekYFnbUkmYBDQPE6fGfdrvgz39ktp8Xf+FA6BfxLejPTMDOG0Nfk5/rDySAz1dRbFf24zaAFYVXlQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5196,6 +5763,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/jsdom": { "version": "21.1.7", "dev": true, @@ -5597,9 +6171,9 @@ }, "node_modules/ajv": { "version": "8.17.1", - "dev": true, + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5873,7 +6447,6 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -9268,7 +9841,6 @@ }, "node_modules/fast-uri": { "version": "3.0.6", - "dev": true, "funding": [ { "type": "github", @@ -9279,8 +9851,7 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { "version": "4.5.3", @@ -9517,6 +10088,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "dev": true, @@ -11623,7 +12208,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -12647,6 +13233,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, "node_modules/openapi-sampler": { "version": "1.6.1", "dev": true, @@ -12657,6 +13253,12 @@ "json-pointer": "0.6.2" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, "node_modules/openapi-typescript": { "version": "7.9.1", "dev": true, @@ -13070,6 +13672,36 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", + "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.55.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", + "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/pluralize": { "version": "8.0.0", "dev": true, @@ -15359,6 +15991,21 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "dev": true, diff --git a/package.json b/package.json index ace231f9..b1cf7f65 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,10 @@ { "dependencies": { + "@aws-sdk/client-api-gateway": "^3.906.0", + "@playwright/test": "^1.55.1", + "ajv": "^8.17.1", + "js-yaml": "^4.1.0", + "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { @@ -7,6 +12,7 @@ "@redocly/cli": "^1.34.5", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.24.0", diff --git a/tests/.gitignore b/tests/.gitignore index b85301a8..13fafcc9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -7,3 +7,4 @@ node_modules/ /playwright/.cache/ /allure-results /target +/playwright/.auth/ diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts new file mode 100644 index 00000000..11017ef2 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -0,0 +1,90 @@ +import { test, expect } from '@playwright/test'; +import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; +import { validateApiResponse } from '../../helpers/validateJsonSchema'; +import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; + +let baseUrl: string; + +test.beforeAll(async () => { + const region = AWS_REGION; + baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); +}); + +test.describe('API Gateway Tests To Get List Of Pending ', () => +{ + test('GET /letters should return 200 and list items', async ({ request }) => + { + const header = await createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params: { + limit:'2'}, + }, + ); + + expect(response.status()).toBe(200); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + + test('GET /letters with invalid apikey should return 403', async ({ request }) => { + const header = await createInvalidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + expect(response.status()).toBe(403); + }); + + + test('GET /letters with empty correlationId should return 500', async ({ request }) => { + const header = await createHeaderWithNoCorrelationId(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + expect(response.status()).toBe(500); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + + test('GET /letters with invalid query param return 400', async ({ request }) => { + const header = await createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'?' + }, + }, + ); + expect(response.status()).toBe(400); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + +}); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts new file mode 100644 index 00000000..7f6ca5c1 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -0,0 +1,117 @@ + +import { createValidRequestHeaders, RequestHeaders } from '../../../constants/request_headers'; +import { test } from '@playwright/test'; + +type APIPatchMessageRequestTestCase = { + testCase: string; + id: string, + body?: PatchMessageRequestBody; + expectedStatus: number; + expectedResponse?: PatchMessageResponseBody | PatchErrorMessageBody; +}; + +type PatchMessageRequestBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode: number; + reasonText: string; + status: string; + }; + }; +}; + +type PatchMessageResponseBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode: number; + reasonText: string; + status: string; + specificationId:string; + groupId:string; + }; + }; +}; + +export type ErrorLink = { + about: string; +}; + +type PatchErrorResponse = { + id: string; + code: string; + links: ErrorLink; + status: string; + title: string; + detail: string; +}; + +type PatchErrorMessageBody = { + errors: PatchErrorResponse[]; +}; + + + +export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = [ + { + testCase: '200 response if record is updated and status is REJECTED', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'REJECTED', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'REJECTED', + specificationId:'specification-id', + groupId:'group-id' + }, + } + }, + }, + + { + testCase: '400 response if request body is invalid', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: '', + }, + } + }, + expectedStatus: 400, + expectedResponse: { + errors: [{ + id: '1234', + 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' + }] + }, + }, +]; diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts new file mode 100644 index 00000000..3e7efec0 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -0,0 +1,28 @@ +import { test, expect } from '@playwright/test'; +import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; +import { createValidRequestHeaders } from '../../constants/request_headers'; +import { apiPatchMessageRequestTestData } from './testCases/UpdateLetterStatus'; + +let baseUrl: string; + +test.beforeAll(async () => { + const region = AWS_REGION; + baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); +}); + +test.describe('API Gateway Tests To Verify Patch Status Endpoint ', () => { + apiPatchMessageRequestTestData.forEach(({ testCase, id, body, expectedStatus, expectedResponse }) => { + test(`Patch /letters returns ${testCase}`, async ({ request }) => { + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: await createValidRequestHeaders(), + data: body + }, + ); + const res = await response.json(); + + expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + }); + }); +}); diff --git a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts deleted file mode 100644 index 2e75d61b..00000000 --- a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { test, expect, APIRequestContext } from '@playwright/test'; -import { json } from 'stream/consumers'; - -test.describe('API Gateway', () => { - test('GET /items should return 200 and list items', async ({ request }) => { - - const response = await request.get( - 'https://8mu4ycde02.execute-api.eu-west-2.amazonaws.com/main/letters',{ - params:{ - limit:'2' - }, - headers: { - 'headerauth1': 'headervalue1', - 'nhsd-supplier-id':'70735ec9-3ba5-4fb0-bb01-b56d2df24bc', - 'nhsd-correlation-id':'1234', - }, - }); - - expect(response.status()).toBe(200); - const responseBody = await response.json(); - console.log(JSON.stringify(responseBody, null, 2)); - }); -}); diff --git a/tests/config/main.config.ts b/tests/config/main.config.ts index e25b2f62..f7bc4280 100644 --- a/tests/config/main.config.ts +++ b/tests/config/main.config.ts @@ -3,16 +3,16 @@ import { config as baseConfig } from './playwright.base.config'; import { getReporters } from './reporters'; const localConfig: PlaywrightTestConfig = { + ...baseConfig, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: getReporters('api-test'), - ...baseConfig, //globalSetup: require.resolve('./setup/globalSetup'), //globalTeardown: require.resolve('./setup/globalTeardown'), - testIgnore: [], projects: [ { - name: 'sandbox', - testMatch: 'tests/messages/get_single_letter/*.spec.ts', + name: 'component-tests', + testDir: 'tests/component-tests', + testMatch: '**/*.spec.ts', }, ], }; diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index 8016adc7..473ec325 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -6,8 +6,8 @@ const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; * See https://playwright.dev/docs/test-configuration. */ export const config: PlaywrightTestConfig = { - testDir: '../sandbox/messages/get_single_letter/', - testMatch: '*.spec.ts/', + testDir: '../tests', + testMatch: '**/*.spec.ts', /* Maximum time one test can run for. */ timeout: 60 * 1000, workers: envMaxInstances, diff --git a/tests/config/sandbox.config.ts b/tests/config/sandbox.config.ts new file mode 100644 index 00000000..935ba910 --- /dev/null +++ b/tests/config/sandbox.config.ts @@ -0,0 +1,19 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { config as baseConfig } from './playwright.base.config'; +import { getReporters } from './reporters'; + +const localConfig: PlaywrightTestConfig = { + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: getReporters('api-test'), + ...baseConfig, + //globalSetup: require.resolve('./setup/globalSetup'), + //globalTeardown: require.resolve('./setup/globalTeardown'), + projects: [ + { + name: 'sandbox', + testMatch: '**/sandbox/get_single_letter/*.spec.ts', + }, + ], +}; + +export default localConfig; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index 5eae3ba5..557e3316 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1 +1,5 @@ -export const LETTERS_ENDPOINT = 'letters'; +export const SUPPLIER_API_GATEWAY_NAME = 'nhs-main-supapi'; +export const SUPPLIER_LETTERS = 'letters'; +export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; +export const SUPPLIER_API_URL_SANDBOX = 'https://internal-sandbox.api.service.nhs.uk/nhs-notify-supplier/'; +export const AWS_REGION = 'eu-west-2'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts new file mode 100644 index 00000000..fe29c039 --- /dev/null +++ b/tests/constants/request_headers.ts @@ -0,0 +1,55 @@ +import { randomUUID } from 'node:crypto'; + +export async function createValidRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': 'supplier-id', + 'NHSD-Correlation-ID': '1234', + }; + return requestHeaders; +} + +export async function createInvalidRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: '', + 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Correlation-ID': '1234', + }; + return requestHeaders; +} + +export async function createHeaderWithNoCorrelationId(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Correlation-ID': '', + }; + return requestHeaders; +} + +export async function createValidSandBoxRequestHeaders(): Promise { + let requestHeaders: RequestSandBoxHeaders; + requestHeaders = { + 'X-Request-ID': randomUUID(), + 'Content-Type':'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), + }; + return requestHeaders; +} + +export interface RequestHeaders { + headerauth1: string; + 'NHSD-Supplier-ID': string; + 'NHSD-Correlation-ID': string; + [key: string]: string; +} + +interface RequestSandBoxHeaders { + 'X-Request-ID': string; + 'Content-Type': string; + 'X-Correlation-ID': string; + [key: string]: string; +} diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts new file mode 100644 index 00000000..64eae54e --- /dev/null +++ b/tests/helpers/awsGatewayHelper.ts @@ -0,0 +1,18 @@ +import { APIGatewayClient, GetRestApisCommand } from "@aws-sdk/client-api-gateway"; + +export async function getRestApiGatewayBaseUrl( + apiName: string, + region: string +): Promise { + const client = new APIGatewayClient({ region }); + + + const apis = await client.send(new GetRestApisCommand({})); + const api = apis.items?.find((a) => a.name === apiName); + + if (!api?.id) throw new Error(`API with name "${apiName}" not found.`); + + const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; + console.log(`API Gateway URL: ${url}`); + return url; +} diff --git a/tests/helpers/validateJsonSchema.ts b/tests/helpers/validateJsonSchema.ts new file mode 100644 index 00000000..fab9718d --- /dev/null +++ b/tests/helpers/validateJsonSchema.ts @@ -0,0 +1,39 @@ +import OpenAPIResponseValidator from "openapi-response-validator"; +import openapiDoc from "../../build/notify-supplier.json" assert { type: "json" }; +type ValidationResult = ReturnType; + +/** + * Validate a response against the OpenAPI spec for a given endpoint and method. + * + * @param method - HTTP method in lowercase ('get', 'post', etc.) + * @param path - API path (must match OpenAPI path exactly, e.g., '/items') + * @param status - HTTP status code returned by the API + * @param body - Response body to validate + * @returns ValidationResult or undefined if valid + */ +export function validateApiResponse( + method: string, + path: string, + status: number, + body: any +): ValidationResult { + const pathItem = (openapiDoc.paths as Record)[path]; + + if (!pathItem) throw new Error(`Path ${path} not found in OpenAPI spec`); + + const operation = pathItem[method]; + if (!operation) throw new Error(`Method ${method.toUpperCase()} not defined for ${path}`); + + // Find the response schema for the actual status code + const responseSchema = operation.responses[status] || operation.responses["default"]; + if (!responseSchema) { + throw new Error(`No schema defined for status ${status} at ${method.toUpperCase()} ${path}`); + } + + const validator = new OpenAPIResponseValidator({ + responses: { [status]: responseSchema }, + components: openapiDoc.components, + }); + + return validator.validateResponse(status, body); +} diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 00000000..0d3cc9d5 --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,186 @@ +{ + "lockfileVersion": 3, + "name": "tests", + "packages": { + "": { + "dependencies": { + "allure-js-commons": "^3.3.3", + "charenc": "^0.0.2", + "crypt": "^0.0.2", + "dotenv": "^17.2.2", + "is-buffer": "^1.1.6", + "md5": "^2.3.0", + "playwright": "^1.54.2", + "playwright-core": "^1.54.2", + "undici-types": "^7.10.0" + }, + "devDependencies": { + "@playwright/test": "^1.55.1", + "@types/node": "^24.3.1", + "allure-commandline": "^2.34.1", + "allure-playwright": "^3.3.3" + }, + "license": "ISC", + "name": "tests", + "version": "1.0.0" + }, + "node_modules/@playwright/test": { + "bin": { + "playwright": "cli.js" + }, + "dependencies": { + "playwright": "1.55.1" + }, + "devOptional": true, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/@types/node": { + "dependencies": { + "undici-types": "~7.13.0" + }, + "dev": true, + "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", + "version": "24.6.1" + }, + "node_modules/@types/node/node_modules/undici-types": { + "dev": true, + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "version": "7.13.0" + }, + "node_modules/allure-commandline": { + "bin": { + "allure": "bin/allure" + }, + "dev": true, + "integrity": "sha512-l42csZ2bz7FdtJI1t5zA3IXtOZ0YJaP/+JMRC9gt6aBHRVUIu+6r+3F7KRyshQ79osLz9/MHlGqAjBPRqH0QFw==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.34.1.tgz", + "version": "2.34.1" + }, + "node_modules/allure-js-commons": { + "dependencies": { + "md5": "^2.3.0" + }, + "integrity": "sha512-Lzs91+8fTPUucnLwQULuuLyEJWQCBCxsZl5tX6lMPFZcAVXgwu/Dti0XfzhH9R1Hw8e43k20XAlmyh/ZDqqpYA==", + "license": "Apache-2.0", + "peerDependencies": { + "allure-playwright": "3.4.1" + }, + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.1.tgz", + "version": "3.4.1" + }, + "node_modules/allure-playwright": { + "dependencies": { + "allure-js-commons": "3.4.1" + }, + "devOptional": true, + "integrity": "sha512-vSc+7iBmilejRbSiv0dakl+/EONHFUs3yDmEOydKmF0+aOMczRyMYOBvU42Ob51PrZozi11ExiQj9SCMH0c4bQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@playwright/test": ">=1.53.0" + }, + "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.1.tgz", + "version": "3.4.1" + }, + "node_modules/charenc": { + "engines": { + "node": "*" + }, + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "version": "0.0.2" + }, + "node_modules/crypt": { + "engines": { + "node": "*" + }, + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "version": "0.0.2" + }, + "node_modules/dotenv": { + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + }, + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "version": "17.2.3" + }, + "node_modules/is-buffer": { + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "version": "1.1.6" + }, + "node_modules/md5": { + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + }, + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "version": "2.3.0" + }, + "node_modules/playwright": { + "bin": { + "playwright": "cli.js" + }, + "dependencies": { + "playwright-core": "1.55.1" + }, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "license": "Apache-2.0", + "optionalDependencies": { + "fsevents": "2.3.2" + }, + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/playwright-core": { + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/undici-types": { + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "version": "7.16.0" + } + }, + "requires": true, + "version": "1.0.0" +} diff --git a/tests/package.json b/tests/package.json index 52113653..659bf618 100644 --- a/tests/package.json +++ b/tests/package.json @@ -24,7 +24,8 @@ "name": "tests", "scripts": { "clean": "rimraf $(pwd)/target", - "test": "playwright test tests --config=config/main.config.ts --max-failures=10" + "test": "playwright test tests --config=config/main.config.ts --max-failures=10 --project=component_tests", + "test:sandbox": "playwright test tests --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" }, "version": "1.0.0" } diff --git a/tests/sandbox/messages/get_single_letter/test_401.spec.ts b/tests/sandbox/get_single_letter/test_401.spec.ts similarity index 62% rename from tests/sandbox/messages/get_single_letter/test_401.spec.ts rename to tests/sandbox/get_single_letter/test_401.spec.ts index 98fde1f4..347af42d 100644 --- a/tests/sandbox/messages/get_single_letter/test_401.spec.ts +++ b/tests/sandbox/get_single_letter/test_401.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { LETTERS_ENDPOINT} from '../../../constants/api_constants'; +import { SUPPLIER_LETTERS, SUPPLIER_API_URL_SANDBOX} from '../../constants/api_constants'; // Constants const status = "PENDING"; @@ -11,10 +11,7 @@ test("401 when invalid APIKEY is passed", async ({ request }) => { Authorization: '1234' }; - const API_URL = process.env.DEV_API_GATEWAY_URL; - const API_GATEWAY_URL = `${API_URL}${LETTERS_ENDPOINT}`; - - const response = await request.get(API_GATEWAY_URL, { + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ params: { status: `${status}` }, diff --git a/tests/sandbox/get_single_letter/test_success.spec.ts b/tests/sandbox/get_single_letter/test_success.spec.ts new file mode 100644 index 00000000..78ef96a2 --- /dev/null +++ b/tests/sandbox/get_single_letter/test_success.spec.ts @@ -0,0 +1,20 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../../constants/api_constants'; +import { createValidSandBoxRequestHeaders } from '../../constants/request_headers'; + +test("200 when valid input is passed", async ({ request }) => { + + const header = await createValidSandBoxRequestHeaders(); + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + + + await expect(response.status()).toBe(200); + expect(response.ok()).toBeTruthy(); + }); diff --git a/tests/sandbox/messages/get_single_letter/test_success.spec.ts b/tests/sandbox/messages/get_single_letter/test_success.spec.ts deleted file mode 100644 index 27e04f5a..00000000 --- a/tests/sandbox/messages/get_single_letter/test_success.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { LETTERS_ENDPOINT} from '../../../constants/api_constants'; - -// Constants -const STATUS = 'PENDING'; - -test("200 when valid input is passed", async ({ request }) => { - const headers = { - headerauth1: 'headervalue1', - apikey: process.env.API_KEY!, - Authorization: '1234' - }; - - const API_URL = process.env.DEV_API_GATEWAY_URL; - const API_GATEWAY_URL = `${API_URL}${LETTERS_ENDPOINT}`; - - const response = await request.get(API_GATEWAY_URL, { - params: { - status: `${STATUS}` - }, - headers - }); - - await expect(response.status()).toBe(200); - expect(response.ok()).toBeTruthy(); - }); From d28f7d50b0a5e8e03bf7d74a8dfd05533ea73360 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 9 Oct 2025 09:57:25 +0100 Subject: [PATCH 03/24] Comp tests --- .../apiGateway-tests/getLetters.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 11017ef2..b456ff3f 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -67,7 +67,7 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(validationResult).toBeUndefined(); }); - test('GET /letters with invalid query param return 400', async ({ request }) => { + test('GET /letters with invalid query param return 400', async ({ request }) => { const header = await createValidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, @@ -79,12 +79,12 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(400); const responseBody = await response.json(); - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } - expect(validationResult).toBeUndefined(); + expect(validationResult).toBeUndefined(); }); }); From 8e56956121fdba922c78db9865ec775d1c5f6a36 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Mon, 13 Oct 2025 15:44:16 +0100 Subject: [PATCH 04/24] component tests --- tests/.gitignore | 1 + .../apiGateway-tests/getLetters.spec.ts | 3 +- .../testCases/UpdateLetterStatus.ts | 73 ++++++- tests/config/main.config.ts | 13 +- tests/config/playwright.base.config.ts | 20 +- tests/config/sandbox.config.ts | 4 +- tests/constants/api_constants.ts | 2 +- tests/constants/request_headers.ts | 16 +- tests/helpers/awsGatewayHelper.ts | 1 - tests/package-lock.json | 186 ------------------ tests/package.json | 4 +- tests/sandbox/getLetterStatus.spec.ts | 21 ++ tests/sandbox/getListOfLetters.spec.ts | 27 +++ .../get_single_letter/test_401.spec.ts | 22 --- .../get_single_letter/test_success.spec.ts | 20 -- .../testCases/getLetterStatus_testCases.ts | 137 +++++++++++++ .../testCases/getListOfLetters_testCases.ts | 97 +++++++++ .../testCases/updateLetterStatus_testCases.ts | 121 ++++++++++++ .../updateMultipleStatys_testCases.ts | 126 ++++++++++++ tests/sandbox/updateLetterStatus.spec.ts | 22 +++ .../updateMultipleLetterStatus.spec.ts | 19 ++ tests/test_Data/updateStatus.json | 1 - 22 files changed, 656 insertions(+), 280 deletions(-) delete mode 100644 tests/package-lock.json create mode 100644 tests/sandbox/getLetterStatus.spec.ts create mode 100644 tests/sandbox/getListOfLetters.spec.ts delete mode 100644 tests/sandbox/get_single_letter/test_401.spec.ts delete mode 100644 tests/sandbox/get_single_letter/test_success.spec.ts create mode 100644 tests/sandbox/testCases/getLetterStatus_testCases.ts create mode 100644 tests/sandbox/testCases/getListOfLetters_testCases.ts create mode 100644 tests/sandbox/testCases/updateLetterStatus_testCases.ts create mode 100644 tests/sandbox/testCases/updateMultipleStatys_testCases.ts create mode 100644 tests/sandbox/updateLetterStatus.spec.ts create mode 100644 tests/sandbox/updateMultipleLetterStatus.spec.ts delete mode 100644 tests/test_Data/updateStatus.json diff --git a/tests/.gitignore b/tests/.gitignore index 13fafcc9..57b879f0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -8,3 +8,4 @@ node_modules/ /allure-results /target /playwright/.auth/ +/allure-report diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index b456ff3f..625e09b0 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -74,8 +74,7 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => params:{ limit:'?' }, - }, - ); + }); expect(response.status()).toBe(400); const responseBody = await response.json(); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index 7f6ca5c1..b0e52a10 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -10,28 +10,28 @@ type APIPatchMessageRequestTestCase = { expectedResponse?: PatchMessageResponseBody | PatchErrorMessageBody; }; -type PatchMessageRequestBody = { +export type PatchMessageRequestBody = { data: { type: string; id: string; attributes: { - reasonCode: number; - reasonText: string; + reasonCode?: number; + reasonText?: string; status: string; }; }; }; -type PatchMessageResponseBody = { +export type PatchMessageResponseBody = { data: { type: string; id: string; attributes: { - reasonCode: number; - reasonText: string; + reasonCode?: number; + reasonText?: string; status: string; specificationId:string; - groupId:string; + groupId?:string; }; }; }; @@ -49,7 +49,7 @@ type PatchErrorResponse = { detail: string; }; -type PatchErrorMessageBody = { +export type PatchErrorMessageBody = { errors: PatchErrorResponse[]; }; @@ -57,7 +57,7 @@ type PatchErrorMessageBody = { export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = [ { - testCase: '200 response if record is updated and status is REJECTED', + testCase: '200 response if record is updated with status REJECTED', id: '00c61654-24f0-410e-a77e-04deef7d1eeb', body: { data: { @@ -86,6 +86,34 @@ export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = }, }, + { + testCase: '200 response if record is updated with status ACCEPTED', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + status: 'ACCEPTED', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'ACCEPTED', + specificationId:'specification-id', + groupId:'group-id' + }, + } + }, + }, + { testCase: '400 response if request body is invalid', id: '00c61654-24f0-410e-a77e-04deef7d1eeb', @@ -114,4 +142,31 @@ export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = }] }, }, + + { + testCase: '500 response if id doesnt exist supplierid', + id: '0', + body: { + data: { + type: 'Letter', + id: '0', + attributes: { + status: 'ACCEPTED', + }, + } + }, + expectedStatus: 500, + expectedResponse: { + 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: 'Letter with id 0 not found for supplier supplier-id' + }] + }, + }, ]; diff --git a/tests/config/main.config.ts b/tests/config/main.config.ts index f7bc4280..4c87088a 100644 --- a/tests/config/main.config.ts +++ b/tests/config/main.config.ts @@ -1,20 +1,19 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; -import { config as baseConfig } from './playwright.base.config'; -import { getReporters } from './reporters'; +import {defineConfig, PlaywrightTestConfig } from '@playwright/test'; +import baseConfig from './playwright.base.config'; +import { getReporters } from './reporters'; +import path from 'path'; const localConfig: PlaywrightTestConfig = { ...baseConfig, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: getReporters('api-test'), - //globalSetup: require.resolve('./setup/globalSetup'), - //globalTeardown: require.resolve('./setup/globalTeardown'), projects: [ { name: 'component-tests', - testDir: 'tests/component-tests', + testDir: path.resolve(__dirname, '../component-tests'), testMatch: '**/*.spec.ts', }, ], }; -export default localConfig; +export default defineConfig(localConfig); diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index 473ec325..3395bf7f 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -1,4 +1,4 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; +import { defineConfig, PlaywrightTestConfig } from '@playwright/test'; const baseUrl = process.env.NHSD_APIM_PROXY_URL || 'http://localhost:3000/'; const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; @@ -6,8 +6,6 @@ const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; * See https://playwright.dev/docs/test-configuration. */ export const config: PlaywrightTestConfig = { - testDir: '../tests', - testMatch: '**/*.spec.ts', /* Maximum time one test can run for. */ timeout: 60 * 1000, workers: envMaxInstances, @@ -24,19 +22,5 @@ export const config: PlaywrightTestConfig = { forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, - - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: baseUrl, - ignoreHTTPSErrors: true, - trace: 'on-first-retry', - /* Slows down Playwright operations by the specified amount of milliseconds. */ - launchOptions: { - slowMo: 0, - }, - }, }; -export default config; +export default defineConfig(config); diff --git a/tests/config/sandbox.config.ts b/tests/config/sandbox.config.ts index 935ba910..1990cb93 100644 --- a/tests/config/sandbox.config.ts +++ b/tests/config/sandbox.config.ts @@ -1,6 +1,7 @@ import type { PlaywrightTestConfig } from '@playwright/test'; import { config as baseConfig } from './playwright.base.config'; import { getReporters } from './reporters'; +import path from 'path'; const localConfig: PlaywrightTestConfig = { /* Reporter to use. See https://playwright.dev/docs/test-reporters */ @@ -11,7 +12,8 @@ const localConfig: PlaywrightTestConfig = { projects: [ { name: 'sandbox', - testMatch: '**/sandbox/get_single_letter/*.spec.ts', + testDir: path.resolve(__dirname, '../sandbox'), + testMatch: '*.spec.ts', }, ], }; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index 557e3316..b79204e2 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1,5 +1,5 @@ export const SUPPLIER_API_GATEWAY_NAME = 'nhs-main-supapi'; export const SUPPLIER_LETTERS = 'letters'; export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; -export const SUPPLIER_API_URL_SANDBOX = 'https://internal-sandbox.api.service.nhs.uk/nhs-notify-supplier/'; +export const SUPPLIER_API_URL_SANDBOX = 'https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier'; export const AWS_REGION = 'eu-west-2'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index fe29c039..856e308d 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -30,15 +30,11 @@ export async function createHeaderWithNoCorrelationId(): Promise return requestHeaders; } -export async function createValidSandBoxRequestHeaders(): Promise { - let requestHeaders: RequestSandBoxHeaders; - requestHeaders = { - 'X-Request-ID': randomUUID(), - 'Content-Type':'application/vnd.api+json', - 'X-Correlation-ID': randomUUID(), - }; - return requestHeaders; -} +export const sandBoxHeader: RequestSandBoxHeaders = { + 'X-Request-ID': randomUUID(), + 'Content-Type': 'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), +}; export interface RequestHeaders { headerauth1: string; @@ -47,7 +43,7 @@ export interface RequestHeaders { [key: string]: string; } -interface RequestSandBoxHeaders { +export interface RequestSandBoxHeaders { 'X-Request-ID': string; 'Content-Type': string; 'X-Correlation-ID': string; diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts index 64eae54e..4a912c12 100644 --- a/tests/helpers/awsGatewayHelper.ts +++ b/tests/helpers/awsGatewayHelper.ts @@ -13,6 +13,5 @@ export async function getRestApiGatewayBaseUrl( if (!api?.id) throw new Error(`API with name "${apiName}" not found.`); const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; - console.log(`API Gateway URL: ${url}`); return url; } diff --git a/tests/package-lock.json b/tests/package-lock.json deleted file mode 100644 index 0d3cc9d5..00000000 --- a/tests/package-lock.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "lockfileVersion": 3, - "name": "tests", - "packages": { - "": { - "dependencies": { - "allure-js-commons": "^3.3.3", - "charenc": "^0.0.2", - "crypt": "^0.0.2", - "dotenv": "^17.2.2", - "is-buffer": "^1.1.6", - "md5": "^2.3.0", - "playwright": "^1.54.2", - "playwright-core": "^1.54.2", - "undici-types": "^7.10.0" - }, - "devDependencies": { - "@playwright/test": "^1.55.1", - "@types/node": "^24.3.1", - "allure-commandline": "^2.34.1", - "allure-playwright": "^3.3.3" - }, - "license": "ISC", - "name": "tests", - "version": "1.0.0" - }, - "node_modules/@playwright/test": { - "bin": { - "playwright": "cli.js" - }, - "dependencies": { - "playwright": "1.55.1" - }, - "devOptional": true, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/@types/node": { - "dependencies": { - "undici-types": "~7.13.0" - }, - "dev": true, - "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", - "version": "24.6.1" - }, - "node_modules/@types/node/node_modules/undici-types": { - "dev": true, - "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", - "version": "7.13.0" - }, - "node_modules/allure-commandline": { - "bin": { - "allure": "bin/allure" - }, - "dev": true, - "integrity": "sha512-l42csZ2bz7FdtJI1t5zA3IXtOZ0YJaP/+JMRC9gt6aBHRVUIu+6r+3F7KRyshQ79osLz9/MHlGqAjBPRqH0QFw==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.34.1.tgz", - "version": "2.34.1" - }, - "node_modules/allure-js-commons": { - "dependencies": { - "md5": "^2.3.0" - }, - "integrity": "sha512-Lzs91+8fTPUucnLwQULuuLyEJWQCBCxsZl5tX6lMPFZcAVXgwu/Dti0XfzhH9R1Hw8e43k20XAlmyh/ZDqqpYA==", - "license": "Apache-2.0", - "peerDependencies": { - "allure-playwright": "3.4.1" - }, - "peerDependenciesMeta": { - "allure-playwright": { - "optional": true - } - }, - "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.1.tgz", - "version": "3.4.1" - }, - "node_modules/allure-playwright": { - "dependencies": { - "allure-js-commons": "3.4.1" - }, - "devOptional": true, - "integrity": "sha512-vSc+7iBmilejRbSiv0dakl+/EONHFUs3yDmEOydKmF0+aOMczRyMYOBvU42Ob51PrZozi11ExiQj9SCMH0c4bQ==", - "license": "Apache-2.0", - "peerDependencies": { - "@playwright/test": ">=1.53.0" - }, - "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.1.tgz", - "version": "3.4.1" - }, - "node_modules/charenc": { - "engines": { - "node": "*" - }, - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "version": "0.0.2" - }, - "node_modules/crypt": { - "engines": { - "node": "*" - }, - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "version": "0.0.2" - }, - "node_modules/dotenv": { - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - }, - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "license": "BSD-2-Clause", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "version": "17.2.3" - }, - "node_modules/is-buffer": { - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "version": "1.1.6" - }, - "node_modules/md5": { - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - }, - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "version": "2.3.0" - }, - "node_modules/playwright": { - "bin": { - "playwright": "cli.js" - }, - "dependencies": { - "playwright-core": "1.55.1" - }, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", - "license": "Apache-2.0", - "optionalDependencies": { - "fsevents": "2.3.2" - }, - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/playwright-core": { - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/undici-types": { - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "version": "7.16.0" - } - }, - "requires": true, - "version": "1.0.0" -} diff --git a/tests/package.json b/tests/package.json index 659bf618..05f51203 100644 --- a/tests/package.json +++ b/tests/package.json @@ -24,8 +24,8 @@ "name": "tests", "scripts": { "clean": "rimraf $(pwd)/target", - "test": "playwright test tests --config=config/main.config.ts --max-failures=10 --project=component_tests", - "test:sandbox": "playwright test tests --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" + "test:component": "playwright test --config=config/main.config.ts --max-failures=10 --project=component-tests", + "test:sandbox": "playwright test --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" }, "version": "1.0.0" } diff --git a/tests/sandbox/getLetterStatus.spec.ts b/tests/sandbox/getLetterStatus.spec.ts new file mode 100644 index 00000000..3d10845d --- /dev/null +++ b/tests/sandbox/getLetterStatus.spec.ts @@ -0,0 +1,21 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxGetLetterStatusTestData } from './testCases/getLetterStatus_testCases'; + + +test.describe('Sandbox Tests To Get Letter Status', () => +{ + apiSandboxGetLetterStatusTestData.forEach(({ testCase, header, id, expectedStatus, expectedResponse }) => { + test(`Get Letter Status endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: header + }, + ); + + const res = await response.json(); + expect(res).toEqual(expectedResponse); + + }); + }); +}); diff --git a/tests/sandbox/getListOfLetters.spec.ts b/tests/sandbox/getListOfLetters.spec.ts new file mode 100644 index 00000000..e2bca376 --- /dev/null +++ b/tests/sandbox/getListOfLetters.spec.ts @@ -0,0 +1,27 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxGetLettersRequestTestData } from './testCases/getListOfLetters_testCases'; + + +test.describe('Sandbox Tests To Get List Of Pending Letters ', () => +{ + apiSandboxGetLettersRequestTestData.forEach(({ testCase, header, limit, expectedStatus, expectedResponse }) => { + test(`Get /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit: limit + }, + }, + ); + + const res = await response.json(); + await expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + if (response.status() === 200){ + expect(res.data.length.toString()).toEqual(limit); + } + }); + }); +}); diff --git a/tests/sandbox/get_single_letter/test_401.spec.ts b/tests/sandbox/get_single_letter/test_401.spec.ts deleted file mode 100644 index 347af42d..00000000 --- a/tests/sandbox/get_single_letter/test_401.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS, SUPPLIER_API_URL_SANDBOX} from '../../constants/api_constants'; - -// Constants -const status = "PENDING"; - -test("401 when invalid APIKEY is passed", async ({ request }) => { - const headers = { - headerauth1 : 'headervalue1', - apikey : '', - Authorization: '1234' - }; - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - params: { - status: `${status}` - }, - headers - }); - - await expect(response.status()).toBe(401); - }); diff --git a/tests/sandbox/get_single_letter/test_success.spec.ts b/tests/sandbox/get_single_letter/test_success.spec.ts deleted file mode 100644 index 78ef96a2..00000000 --- a/tests/sandbox/get_single_letter/test_success.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../../constants/api_constants'; -import { createValidSandBoxRequestHeaders } from '../../constants/request_headers'; - -test("200 when valid input is passed", async ({ request }) => { - - const header = await createValidSandBoxRequestHeaders(); - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit:'2' - }, - }, - ); - - - await expect(response.status()).toBe(200); - expect(response.ok()).toBeTruthy(); - }); diff --git a/tests/sandbox/testCases/getLetterStatus_testCases.ts b/tests/sandbox/testCases/getLetterStatus_testCases.ts new file mode 100644 index 00000000..9131f9d5 --- /dev/null +++ b/tests/sandbox/testCases/getLetterStatus_testCases.ts @@ -0,0 +1,137 @@ +import { randomUUID } from "node:crypto"; +import { RequestSandBoxHeaders, sandBoxHeader} from "../../constants/request_headers"; +import { NoRequestIdHeaders, SandboxErrorResponse, SandboxSuccessResponse } from "./getListOfLetters_testCases"; + +type ApiSandboxGetLetterStatusTestCase = { + testCase: string; + id: string, + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: GetLetterStatusResposne | GetLetterStatusErrorResponse | GetRejectedLetterResponse; +}; + +export type GetLetterStatusResposne = { + data: GetLetterData; +}; + +export type GetRejectedLetterResponse = { + data: RejectedLetterData; +}; + +type GetLetterData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + } +}; + +type RejectedLetterData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + reasonCode: number; + reasonText: string; + } +}; + +export type GetLetterStatusErrorResponse = { + errors: ApiErrors[]; +}; + +type ApiErrors = { + code: string; + detail: string; + id: string; + links:{ + about: string; + }, + status: string; + title: string; +}; + +export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCase[] = [ +{ + testCase: '200 response and ACCEPTED record is fetched successfully', + id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'ACCEPTED', + }, + } + } +}, +{ + testCase: '200 response and REJECTED record is fetched successfully', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'REJECTED', + reasonCode: 100, + reasonText: "failed validation", + }, + } + } +}, +{ + testCase: '200 response and CANCELLED record is fetched successfully', + id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'CANCELLED', + reasonCode: 100 + }, + } + } +}, +{ + testCase: '404 response when no record is found for the given id', + id: '24L5eYSWGzCHlGmzNxuqVusP', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + errors: [ + { + status: '404', + title: 'Resource not found', + code:'NOTIFY_RESOURCE_NOT_FOUND', + detail: 'No resource found with that ID', + id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', + links: { + about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier' + } + } + ] + } +}]; diff --git a/tests/sandbox/testCases/getListOfLetters_testCases.ts b/tests/sandbox/testCases/getListOfLetters_testCases.ts new file mode 100644 index 00000000..422786d0 --- /dev/null +++ b/tests/sandbox/testCases/getListOfLetters_testCases.ts @@ -0,0 +1,97 @@ +import { RequestSandBoxHeaders, sandBoxHeader} from '../../constants/request_headers'; +import { randomUUID } from 'node:crypto'; + + +type ApiSandboxGetLettersRequestTestCase = { + testCase: string; + limit: string, + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: SandboxSuccessResponse | SandboxErrorResponse; +}; + +export type SandboxSuccessResponse = { + data: ApiData []; +}; + +type ApiData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + } +}; + +export type SandboxErrorResponse = { + message: string; + errors: ApiErrors[]; +}; + +type ApiErrors = { + path: string; + message: string; + errorCode: string; +}; + +export type NoRequestIdHeaders = Omit; + +const NoRequestIdHeaders: NoRequestIdHeaders = { + 'Content-Type': 'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), +}; + +export const apiSandboxGetLettersRequestTestData: ApiSandboxGetLettersRequestTestCase[] = [ +{ + testCase: '200 response if record is fetched successfully', + limit: '1', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: [ + { + id: 'fcfd849ceec940e8832b41f4fc161e09', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'PENDING', + }, + }] + }, +}, +{ + testCase: '400 response if invalid limit is passed', + limit: 'XX', + header: sandBoxHeader, + expectedStatus: 400, + expectedResponse: { + message: 'request.query.limit should be number', + errors: [ + { + path:'.query.limit', + message:'should be number', + errorCode:'type.openapi.validation' + } + ] + } +}, + +{ + testCase: '400 response if invalid headers are passed', + limit: '2', + header: NoRequestIdHeaders, + expectedStatus: 400, + expectedResponse: { + message: "request.headers should have required property 'x-request-id'", + errors: [ + { + path: '.headers.x-request-id', + message: "should have required property 'x-request-id'", + errorCode:'required.openapi.validation' + } + ] + } +}]; diff --git a/tests/sandbox/testCases/updateLetterStatus_testCases.ts b/tests/sandbox/testCases/updateLetterStatus_testCases.ts new file mode 100644 index 00000000..491c79d5 --- /dev/null +++ b/tests/sandbox/testCases/updateLetterStatus_testCases.ts @@ -0,0 +1,121 @@ + +import { PatchErrorMessageBody, PatchMessageRequestBody, PatchMessageResponseBody } from '../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus'; +import { RequestSandBoxHeaders, sandBoxHeader } from '../../constants/request_headers'; +import { SandboxErrorResponse } from './getListOfLetters_testCases'; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + id: string, + header: RequestSandBoxHeaders; + body?: PatchMessageRequestBody; + expectedStatus: number; + expectedResponse?: PatchMessageResponseBody | SandboxErrorResponse | PatchErrorMessageBody; +}; + +export const apiSandboxUpdateLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = [ + { + testCase: '200 response if record is updated with status PENDING', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'PENDING', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'PENDING', + specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', + }, + } + }, + }, + + { + testCase: '200 response if record is updated with status REJECTED', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'REJECTED', + reasonCode: 100, + reasonText: 'failed validation', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'REJECTED', + specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', + }, + } + }, + }, + { + testCase: '404 response if no resource is found for the given id', + id: '0', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '0', + attributes: { + status: 'PENDING', + }, + } + }, + expectedStatus: 404, + expectedResponse: { + errors: [{ + id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', + code: 'NOTIFY_RESOURCE_NOT_FOUND', + links: { + about: "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: '404', + title: 'Resource not found', + detail: 'No resource found with that ID' + }] + }, + }, + { + testCase: '400 response if request body is invalid', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'NO_STATUS', + }, + } + }, + expectedStatus: 400, + expectedResponse: { + message: 'request.body.data.attributes.status should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', + errors: [{ + path: '.body.data.attributes.status', + message: 'should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', + errorCode: 'enum.openapi.validation' + }] + }, + }, +]; diff --git a/tests/sandbox/testCases/updateMultipleStatys_testCases.ts b/tests/sandbox/testCases/updateMultipleStatys_testCases.ts new file mode 100644 index 00000000..69e31b54 --- /dev/null +++ b/tests/sandbox/testCases/updateMultipleStatys_testCases.ts @@ -0,0 +1,126 @@ +import { PatchMessageRequestBody } from "../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus"; +import { RequestSandBoxHeaders, sandBoxHeader } from "../../constants/request_headers"; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + header: RequestSandBoxHeaders; + body: PostMessageRequestBody; + expectedStatus: number; +}; + +type PostMessageRequestBody = { + data: postRequest [] +} + +type postRequest = { + type: string; + id: string; + attributes: { + reasonCode?: number; + reasonText?: string; + status: string; + } +}; + +export const apiSandboxMultipleLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = +[{ + testCase: '200 response if records are updated', + header: sandBoxHeader, + body:{ + data : + [{ + attributes: { + status: 'PENDING' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + 'status': 'ACCEPTED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'PRINTED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'ENCLOSED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'DISPATCHED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'DELIVERED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'RETURNED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'CANCELLED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'FAILED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'RETURNED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + } + ]}, + expectedStatus: 200 +}, +{ + testCase: '400 response if invalid request is passed', + header: sandBoxHeader, + body:{ + data : + [{ + attributes: { + status: 'PENDING' + }, + id: '1234', + type: 'Letter' + }] + }, + expectedStatus:404, +}]; diff --git a/tests/sandbox/updateLetterStatus.spec.ts b/tests/sandbox/updateLetterStatus.spec.ts new file mode 100644 index 00000000..9e0d9a4d --- /dev/null +++ b/tests/sandbox/updateLetterStatus.spec.ts @@ -0,0 +1,22 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; + + +test.describe('Sandbox Tests To Update Letter Status', () => +{ + apiSandboxUpdateLetterStatusTestData.forEach(({ testCase, header, id, body, expectedStatus, expectedResponse }) => { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.patch(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: header, + data: body + }); + + const res = await response.json(); + expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + + }); + }); +}); diff --git a/tests/sandbox/updateMultipleLetterStatus.spec.ts b/tests/sandbox/updateMultipleLetterStatus.spec.ts new file mode 100644 index 00000000..64f6bb2b --- /dev/null +++ b/tests/sandbox/updateMultipleLetterStatus.spec.ts @@ -0,0 +1,19 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; +import { apiSandboxMultipleLetterStatusTestData } from './testCases/updateMultipleStatys_testCases'; + + +test.describe('Sandbox Tests To Update Multiple Letter Status', () => +{ + apiSandboxMultipleLetterStatusTestData.forEach(({ testCase, header, body, expectedStatus }) => { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.post(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + data: body + }); + expect(response.status()).toBe(expectedStatus); + }); + }); +}); diff --git a/tests/test_Data/updateStatus.json b/tests/test_Data/updateStatus.json deleted file mode 100644 index 0967ef42..00000000 --- a/tests/test_Data/updateStatus.json +++ /dev/null @@ -1 +0,0 @@ -{} From e658df25fe8a9d38f4fd3c47f9e8f6433dd6c774 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 1 Oct 2025 13:58:34 +0100 Subject: [PATCH 05/24] test --- .../updateLetterStatus.spec.ts | 23 +++++++++++++++++++ tests/package.json | 3 +-- tests/test_Data/updateStatus.json | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts create mode 100644 tests/test_Data/updateStatus.json diff --git a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts new file mode 100644 index 00000000..2e75d61b --- /dev/null +++ b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts @@ -0,0 +1,23 @@ +import { test, expect, APIRequestContext } from '@playwright/test'; +import { json } from 'stream/consumers'; + +test.describe('API Gateway', () => { + test('GET /items should return 200 and list items', async ({ request }) => { + + const response = await request.get( + 'https://8mu4ycde02.execute-api.eu-west-2.amazonaws.com/main/letters',{ + params:{ + limit:'2' + }, + headers: { + 'headerauth1': 'headervalue1', + 'nhsd-supplier-id':'70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'nhsd-correlation-id':'1234', + }, + }); + + expect(response.status()).toBe(200); + const responseBody = await response.json(); + console.log(JSON.stringify(responseBody, null, 2)); + }); +}); diff --git a/tests/package.json b/tests/package.json index 53926dff..52113653 100644 --- a/tests/package.json +++ b/tests/package.json @@ -5,7 +5,6 @@ "charenc": "^0.0.2", "crypt": "^0.0.2", "dotenv": "^17.2.2", - "fsevents": "^2.3.2", "is-buffer": "^1.1.6", "md5": "^2.3.0", "playwright": "^1.54.2", @@ -14,7 +13,7 @@ }, "description": "", "devDependencies": { - "@playwright/test": "^1.55.0", + "@playwright/test": "^1.55.1", "@types/node": "^24.3.1", "allure-commandline": "^2.34.1", "allure-playwright": "^3.3.3" diff --git a/tests/test_Data/updateStatus.json b/tests/test_Data/updateStatus.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/test_Data/updateStatus.json @@ -0,0 +1 @@ +{} From 06f62fd9b0e7c24354f998d913ee67f2bf4e59ca Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 9 Oct 2025 09:56:21 +0100 Subject: [PATCH 06/24] Comp tests --- package-lock.json | 1103 +++++++++++++---- package.json | 6 + tests/.gitignore | 1 + .../apiGateway-tests/getLetters.spec.ts | 90 ++ .../testCases/UpdateLetterStatus.ts | 117 ++ .../updateLetterStatus.spec.ts | 28 + .../updateLetterStatus.spec.ts | 23 - tests/config/main.config.ts | 8 +- tests/config/playwright.base.config.ts | 4 +- tests/config/sandbox.config.ts | 19 + tests/constants/api_constants.ts | 6 +- tests/constants/request_headers.ts | 55 + tests/helpers/awsGatewayHelper.ts | 18 + tests/helpers/validateJsonSchema.ts | 39 + tests/package-lock.json | 186 +++ tests/package.json | 3 +- .../get_single_letter/test_401.spec.ts | 7 +- .../get_single_letter/test_success.spec.ts | 20 + .../get_single_letter/test_success.spec.ts | 26 - 19 files changed, 1469 insertions(+), 290 deletions(-) create mode 100644 tests/component-tests/apiGateway-tests/getLetters.spec.ts create mode 100644 tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts create mode 100644 tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts delete mode 100644 tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts create mode 100644 tests/config/sandbox.config.ts create mode 100644 tests/constants/request_headers.ts create mode 100644 tests/helpers/awsGatewayHelper.ts create mode 100644 tests/helpers/validateJsonSchema.ts create mode 100644 tests/package-lock.json rename tests/sandbox/{messages => }/get_single_letter/test_401.spec.ts (62%) create mode 100644 tests/sandbox/get_single_letter/test_success.spec.ts delete mode 100644 tests/sandbox/messages/get_single_letter/test_success.spec.ts diff --git a/package-lock.json b/package-lock.json index 4c9878f5..70a5a18b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,11 @@ "docs" ], "dependencies": { + "@aws-sdk/client-api-gateway": "^3.906.0", + "@playwright/test": "^1.55.1", + "ajv": "^8.17.1", + "js-yaml": "^4.1.0", + "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { @@ -20,6 +25,7 @@ "@redocly/cli": "^1.34.5", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.24.0", @@ -352,6 +358,525 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-api-gateway": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-api-gateway/-/client-api-gateway-3.906.0.tgz", + "integrity": "sha512-7iD94D2OWvP6qTdeUNgfnzk7El+ZurdXXaGx5vSTWRz7Hs32MI+0ZLPKQ/2NCeaMZu12YpxQiDBYO6MZ/H5fTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-node": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-sdk-api-gateway": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sso": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.906.0.tgz", + "integrity": "sha512-GGDwjW2cLzoEF5A1tBlZQZXzhlZzuM6cKNbSxUsCcBXtPAX03eb2GKApVy1SzpD03nTJk5T6GicGAm+BzK+lEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/core": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.906.0.tgz", + "integrity": "sha512-+FuwAcozee8joVfjwly/8kSFNCvQOkcQYjINUckqBkdjO4iCRfOgSaz+0JMpMcYgVPnnyZv62gJ2g0bj0U+YDQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.906.0.tgz", + "integrity": "sha512-vtMDguMci2aXhkgEqg1iqyQ7vVcafpx9uypksM6FQsNr3Cc/8I6HgfBAja6BuPwkaCn9NoMnG0/iuuOWr8P9dg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.906.0.tgz", + "integrity": "sha512-L97N2SUkZp03s1LJZ1sCkUaUZ7m9T72faaadn05wyst/iXonSZKPHYMQVWGYhTC2OtRV0FQvBXIAqFZsNGQD0Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.906.0.tgz", + "integrity": "sha512-r7TbHD80WXo42kTEC5bqa4b87ho3T3yd2VEKo1qbEmOUovocntO8HC3JxHYr0XSeZ82DEYxLARb84akWjabPzg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.906.0.tgz", + "integrity": "sha512-xga127vP0rFxiHjEUjLe6Yf4hQ/AZinOF4AqQr/asWQO+/uwh3aH8nXcS4lkpZNygxMHbuNXm7Xg504GKCMlLQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.906.0", + "@aws-sdk/credential-provider-http": "3.906.0", + "@aws-sdk/credential-provider-ini": "3.906.0", + "@aws-sdk/credential-provider-process": "3.906.0", + "@aws-sdk/credential-provider-sso": "3.906.0", + "@aws-sdk/credential-provider-web-identity": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.906.0.tgz", + "integrity": "sha512-P8R4GpDLppe+8mp+SOj1fKaY3AwDULCi/fqMSJjvf8qN6OM+vGGpFP3iXvkjFYyyV+8nRXY+HQCLRoZKpRtzMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.906.0.tgz", + "integrity": "sha512-wYljHU7yNEzt7ngZZ21FWh+RlO16gTpWvXyRqlryuCgIWugHD8bl7JphGnUN1md5/v+mCRuGK58JoFGZq+qrjA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.906.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/token-providers": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.906.0.tgz", + "integrity": "sha512-V9PurepVko8+iyEvI9WAlk5dXJ1uWIW03RPLnNBEmeCqFjjit16HrNaaVvnp9fQbG7CSKSGqK026SjDgtKGKYA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.901.0.tgz", + "integrity": "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-logger": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.901.0.tgz", + "integrity": "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.901.0.tgz", + "integrity": "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.906.0.tgz", + "integrity": "sha512-CMAjq2oCEv5EEvmlFvio8t4KQL2jGORyDQu7oLj4l0a2biPgxbwL3utalbm9yKty1rQM5zKpaa7id7ZG3X1f6A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/nested-clients": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.906.0.tgz", + "integrity": "sha512-0/r0bh/9Bm14lVe+jAzQQB2ufq9S4Vd9Wg5rZn8RhrhKl6y/DC1aRzOo2kJTNu5pCbVfQsd/VXLLnkcbOrDy6A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.906.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.906.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.901.0.tgz", + "integrity": "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/token-providers": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.906.0.tgz", + "integrity": "sha512-gdxXleCjMUAKnyR/1ksdnv3Fuifr9iuaeEtINRHkwVluwcORabEdOlxW36th2QdkpTTyP1hW35VATz2R6v/i2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.906.0", + "@aws-sdk/nested-clients": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-endpoints": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.901.0.tgz", + "integrity": "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", + "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.906.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.906.0.tgz", + "integrity": "sha512-9Gaglw80E9UZ5FctCp5pZAzT40/vC4Oo0fcNXsfplLkpWqTU+NTdTRMYe3TMZ1/v1/JZKuGUVyHiuo/xLu3NmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.906.0", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/xml-builder": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.901.0.tgz", + "integrity": "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/@aws-sdk/client-dynamodb": { "version": "3.864.0", "license": "Apache-2.0", @@ -1432,6 +1957,34 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-api-gateway": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-api-gateway/-/middleware-sdk-api-gateway-3.901.0.tgz", + "integrity": "sha512-Bfn8ciaiPdiuZZpIVwTG2aGuLxSXuOKJNFVEFNmIdFhEU51b4yVQGzcdvgFeohfulCz05cL1Uc/f6cxWQoHEaQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-api-gateway/node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/middleware-sdk-s3": { "version": "3.896.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.896.0.tgz", @@ -4013,6 +4566,21 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", + "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.55.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "dev": true, @@ -4269,12 +4837,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.1.1.tgz", - "integrity": "sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", + "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4307,15 +4875,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.2.2.tgz", - "integrity": "sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", + "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4323,20 +4891,20 @@ } }, "node_modules/@smithy/core": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.12.0.tgz", - "integrity": "sha512-zJeAgogZfbwlPGL93y4Z/XNeIN37YCreRUd6YMIRvaq+6RnBK8PPYYIQ85Is/GglPh3kNImD5riDCXbVSDpCiQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", + "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/uuid": "^1.0.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.5.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -4344,15 +4912,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.1.2.tgz", - "integrity": "sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", + "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4430,15 +4998,15 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.2.1.tgz", - "integrity": "sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", + "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -4461,14 +5029,14 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.1.1.tgz", - "integrity": "sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", + "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4490,12 +5058,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.1.1.tgz", - "integrity": "sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", + "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4503,9 +5071,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.1.0.tgz", - "integrity": "sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4529,13 +5097,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.1.1.tgz", - "integrity": "sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", + "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4543,18 +5111,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.2.4.tgz", - "integrity": "sha512-FZ4hzupOmthm8Q8ujYrd0I+/MHwVMuSTdkDtIQE0xVuvJt9pLT6Q+b0p4/t+slDyrpcf+Wj7SN+ZqT5OryaaZg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", + "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.12.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-middleware": "^4.1.1", + "@smithy/core": "^3.15.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4562,19 +5130,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.3.0.tgz", - "integrity": "sha512-qhEX9745fAxZvtLM4bQJAVC98elWjiMO2OiHl1s6p7hUzS4QfZO1gXUYNwEK8m0J6NoCD5W52ggWxbIDHI0XSg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", + "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/service-error-classification": "^4.1.2", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/uuid": "^1.0.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, "engines": { @@ -4582,13 +5150,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.1.1.tgz", - "integrity": "sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", + "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4596,12 +5164,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.1.1.tgz", - "integrity": "sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", + "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4609,14 +5177,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.2.2.tgz", - "integrity": "sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", + "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4624,15 +5192,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.2.1.tgz", - "integrity": "sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", + "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/querystring-builder": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4640,12 +5208,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.1.1.tgz", - "integrity": "sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", + "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4653,12 +5221,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.2.1.tgz", - "integrity": "sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", + "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4666,13 +5234,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.1.1.tgz", - "integrity": "sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", + "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-uri-escape": "^4.1.0", + "@smithy/types": "^4.6.0", + "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4680,12 +5248,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.1.1.tgz", - "integrity": "sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", + "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4693,24 +5261,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.1.2.tgz", - "integrity": "sha512-Kqd8wyfmBWHZNppZSMfrQFpc3M9Y/kjyN8n8P4DqJJtuwgK1H914R471HTw7+RL+T7+kI1f1gOnL7Vb5z9+NgQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", + "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0" + "@smithy/types": "^4.6.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.2.0.tgz", - "integrity": "sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", + "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4718,18 +5286,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.2.1.tgz", - "integrity": "sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", + "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-uri-escape": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4737,17 +5305,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.6.4.tgz", - "integrity": "sha512-qL7O3VDyfzCSN9r+sdbQXGhaHtrfSJL30En6Jboj0I3bobf2g1/T0eP2L4qxqrEW26gWhJ4THI4ElVVLjYyBHg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", + "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.12.0", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "@smithy/core": "^3.15.0", + "@smithy/middleware-endpoint": "^4.3.1", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4755,9 +5323,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.5.0.tgz", - "integrity": "sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", + "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4767,13 +5335,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.1.1.tgz", - "integrity": "sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", + "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/querystring-parser": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4781,13 +5349,13 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.1.0.tgz", - "integrity": "sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4795,9 +5363,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.1.0.tgz", - "integrity": "sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4807,9 +5375,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.1.0.tgz", - "integrity": "sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4819,12 +5387,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.1.0.tgz", - "integrity": "sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.1.0", + "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4832,9 +5400,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.1.0.tgz", - "integrity": "sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4844,15 +5412,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.1.4.tgz", - "integrity": "sha512-mLDJ1s4eA3vwOGaQOEPlg5LB4LdZUUMpB5UMOMofeGhWqiS7WR7dTpLiNi9zVn+YziKUd3Af5NLfxDs7NJqmIw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", + "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "bowser": "^2.11.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4860,17 +5427,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.1.4.tgz", - "integrity": "sha512-pjX2iMTcOASaSanAd7bu6i3fcMMezr3NTr8Rh64etB0uHRZi+Aw86DoCxPESjY4UTIuA06hhqtTtw95o//imYA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", + "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.2.2", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.1", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4878,13 +5445,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.1.2.tgz", - "integrity": "sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", + "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4892,9 +5459,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.1.0.tgz", - "integrity": "sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4904,12 +5471,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.1.1.tgz", - "integrity": "sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", + "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4917,13 +5484,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.1.2.tgz", - "integrity": "sha512-NCgr1d0/EdeP6U5PSZ9Uv5SMR5XRRYoVr1kRVtKZxWL3tixEL3UatrPIMFZSKwHlCcp2zPLDvMubVDULRqeunA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", + "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.1.2", - "@smithy/types": "^4.5.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "engines": { @@ -4931,18 +5498,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.3.2.tgz", - "integrity": "sha512-Ka+FA2UCC/Q1dEqUanCdpqwxOFdf5Dg2VXtPtB1qxLcSGh5C1HdzklIt18xL504Wiy9nNUKwDMRTVCbKGoK69g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", + "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-buffer-from": "^4.1.0", - "@smithy/util-hex-encoding": "^4.1.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/fetch-http-handler": "^5.3.1", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4950,9 +5517,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.1.0.tgz", - "integrity": "sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4962,12 +5529,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.1.0.tgz", - "integrity": "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.1.0", + "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4989,9 +5556,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.0.0.tgz", - "integrity": "sha512-OlA/yZHh0ekYFnbUkmYBDQPE6fGfdrvgz39ktp8Xf+FA6BfxLejPTMDOG0Nfk5/rDySAz1dRbFf24zaAFYVXlQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5196,6 +5763,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/jsdom": { "version": "21.1.7", "dev": true, @@ -5597,9 +6171,9 @@ }, "node_modules/ajv": { "version": "8.17.1", - "dev": true, + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5873,7 +6447,6 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -9268,7 +9841,6 @@ }, "node_modules/fast-uri": { "version": "3.0.6", - "dev": true, "funding": [ { "type": "github", @@ -9279,8 +9851,7 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { "version": "4.5.3", @@ -9517,6 +10088,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "dev": true, @@ -11623,7 +12208,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -12647,6 +13233,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, "node_modules/openapi-sampler": { "version": "1.6.1", "dev": true, @@ -12657,6 +13253,12 @@ "json-pointer": "0.6.2" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, "node_modules/openapi-typescript": { "version": "7.9.1", "dev": true, @@ -13070,6 +13672,36 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", + "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.55.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", + "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/pluralize": { "version": "8.0.0", "dev": true, @@ -15359,6 +15991,21 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "dev": true, diff --git a/package.json b/package.json index 25ea45f5..b4388e9e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,10 @@ { "dependencies": { + "@aws-sdk/client-api-gateway": "^3.906.0", + "@playwright/test": "^1.55.1", + "ajv": "^8.17.1", + "js-yaml": "^4.1.0", + "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { @@ -7,6 +12,7 @@ "@redocly/cli": "^1.34.5", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.24.0", diff --git a/tests/.gitignore b/tests/.gitignore index b85301a8..13fafcc9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -7,3 +7,4 @@ node_modules/ /playwright/.cache/ /allure-results /target +/playwright/.auth/ diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts new file mode 100644 index 00000000..11017ef2 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -0,0 +1,90 @@ +import { test, expect } from '@playwright/test'; +import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; +import { validateApiResponse } from '../../helpers/validateJsonSchema'; +import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; + +let baseUrl: string; + +test.beforeAll(async () => { + const region = AWS_REGION; + baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); +}); + +test.describe('API Gateway Tests To Get List Of Pending ', () => +{ + test('GET /letters should return 200 and list items', async ({ request }) => + { + const header = await createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params: { + limit:'2'}, + }, + ); + + expect(response.status()).toBe(200); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + + test('GET /letters with invalid apikey should return 403', async ({ request }) => { + const header = await createInvalidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + expect(response.status()).toBe(403); + }); + + + test('GET /letters with empty correlationId should return 500', async ({ request }) => { + const header = await createHeaderWithNoCorrelationId(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + expect(response.status()).toBe(500); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + + test('GET /letters with invalid query param return 400', async ({ request }) => { + const header = await createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'?' + }, + }, + ); + expect(response.status()).toBe(400); + const responseBody = await response.json(); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + + expect(validationResult).toBeUndefined(); + }); + +}); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts new file mode 100644 index 00000000..7f6ca5c1 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -0,0 +1,117 @@ + +import { createValidRequestHeaders, RequestHeaders } from '../../../constants/request_headers'; +import { test } from '@playwright/test'; + +type APIPatchMessageRequestTestCase = { + testCase: string; + id: string, + body?: PatchMessageRequestBody; + expectedStatus: number; + expectedResponse?: PatchMessageResponseBody | PatchErrorMessageBody; +}; + +type PatchMessageRequestBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode: number; + reasonText: string; + status: string; + }; + }; +}; + +type PatchMessageResponseBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode: number; + reasonText: string; + status: string; + specificationId:string; + groupId:string; + }; + }; +}; + +export type ErrorLink = { + about: string; +}; + +type PatchErrorResponse = { + id: string; + code: string; + links: ErrorLink; + status: string; + title: string; + detail: string; +}; + +type PatchErrorMessageBody = { + errors: PatchErrorResponse[]; +}; + + + +export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = [ + { + testCase: '200 response if record is updated and status is REJECTED', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'REJECTED', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'REJECTED', + specificationId:'specification-id', + groupId:'group-id' + }, + } + }, + }, + + { + testCase: '400 response if request body is invalid', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: '', + }, + } + }, + expectedStatus: 400, + expectedResponse: { + errors: [{ + id: '1234', + 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' + }] + }, + }, +]; diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts new file mode 100644 index 00000000..3e7efec0 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -0,0 +1,28 @@ +import { test, expect } from '@playwright/test'; +import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; +import { createValidRequestHeaders } from '../../constants/request_headers'; +import { apiPatchMessageRequestTestData } from './testCases/UpdateLetterStatus'; + +let baseUrl: string; + +test.beforeAll(async () => { + const region = AWS_REGION; + baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); +}); + +test.describe('API Gateway Tests To Verify Patch Status Endpoint ', () => { + apiPatchMessageRequestTestData.forEach(({ testCase, id, body, expectedStatus, expectedResponse }) => { + test(`Patch /letters returns ${testCase}`, async ({ request }) => { + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: await createValidRequestHeaders(), + data: body + }, + ); + const res = await response.json(); + + expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + }); + }); +}); diff --git a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts b/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts deleted file mode 100644 index 2e75d61b..00000000 --- a/tests/component_tests/apiGateway_tests/updateLetterStatus.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { test, expect, APIRequestContext } from '@playwright/test'; -import { json } from 'stream/consumers'; - -test.describe('API Gateway', () => { - test('GET /items should return 200 and list items', async ({ request }) => { - - const response = await request.get( - 'https://8mu4ycde02.execute-api.eu-west-2.amazonaws.com/main/letters',{ - params:{ - limit:'2' - }, - headers: { - 'headerauth1': 'headervalue1', - 'nhsd-supplier-id':'70735ec9-3ba5-4fb0-bb01-b56d2df24bc', - 'nhsd-correlation-id':'1234', - }, - }); - - expect(response.status()).toBe(200); - const responseBody = await response.json(); - console.log(JSON.stringify(responseBody, null, 2)); - }); -}); diff --git a/tests/config/main.config.ts b/tests/config/main.config.ts index e25b2f62..f7bc4280 100644 --- a/tests/config/main.config.ts +++ b/tests/config/main.config.ts @@ -3,16 +3,16 @@ import { config as baseConfig } from './playwright.base.config'; import { getReporters } from './reporters'; const localConfig: PlaywrightTestConfig = { + ...baseConfig, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: getReporters('api-test'), - ...baseConfig, //globalSetup: require.resolve('./setup/globalSetup'), //globalTeardown: require.resolve('./setup/globalTeardown'), - testIgnore: [], projects: [ { - name: 'sandbox', - testMatch: 'tests/messages/get_single_letter/*.spec.ts', + name: 'component-tests', + testDir: 'tests/component-tests', + testMatch: '**/*.spec.ts', }, ], }; diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index 8016adc7..473ec325 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -6,8 +6,8 @@ const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; * See https://playwright.dev/docs/test-configuration. */ export const config: PlaywrightTestConfig = { - testDir: '../sandbox/messages/get_single_letter/', - testMatch: '*.spec.ts/', + testDir: '../tests', + testMatch: '**/*.spec.ts', /* Maximum time one test can run for. */ timeout: 60 * 1000, workers: envMaxInstances, diff --git a/tests/config/sandbox.config.ts b/tests/config/sandbox.config.ts new file mode 100644 index 00000000..935ba910 --- /dev/null +++ b/tests/config/sandbox.config.ts @@ -0,0 +1,19 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { config as baseConfig } from './playwright.base.config'; +import { getReporters } from './reporters'; + +const localConfig: PlaywrightTestConfig = { + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: getReporters('api-test'), + ...baseConfig, + //globalSetup: require.resolve('./setup/globalSetup'), + //globalTeardown: require.resolve('./setup/globalTeardown'), + projects: [ + { + name: 'sandbox', + testMatch: '**/sandbox/get_single_letter/*.spec.ts', + }, + ], +}; + +export default localConfig; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index 5eae3ba5..557e3316 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1 +1,5 @@ -export const LETTERS_ENDPOINT = 'letters'; +export const SUPPLIER_API_GATEWAY_NAME = 'nhs-main-supapi'; +export const SUPPLIER_LETTERS = 'letters'; +export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; +export const SUPPLIER_API_URL_SANDBOX = 'https://internal-sandbox.api.service.nhs.uk/nhs-notify-supplier/'; +export const AWS_REGION = 'eu-west-2'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts new file mode 100644 index 00000000..fe29c039 --- /dev/null +++ b/tests/constants/request_headers.ts @@ -0,0 +1,55 @@ +import { randomUUID } from 'node:crypto'; + +export async function createValidRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': 'supplier-id', + 'NHSD-Correlation-ID': '1234', + }; + return requestHeaders; +} + +export async function createInvalidRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: '', + 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Correlation-ID': '1234', + }; + return requestHeaders; +} + +export async function createHeaderWithNoCorrelationId(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Correlation-ID': '', + }; + return requestHeaders; +} + +export async function createValidSandBoxRequestHeaders(): Promise { + let requestHeaders: RequestSandBoxHeaders; + requestHeaders = { + 'X-Request-ID': randomUUID(), + 'Content-Type':'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), + }; + return requestHeaders; +} + +export interface RequestHeaders { + headerauth1: string; + 'NHSD-Supplier-ID': string; + 'NHSD-Correlation-ID': string; + [key: string]: string; +} + +interface RequestSandBoxHeaders { + 'X-Request-ID': string; + 'Content-Type': string; + 'X-Correlation-ID': string; + [key: string]: string; +} diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts new file mode 100644 index 00000000..64eae54e --- /dev/null +++ b/tests/helpers/awsGatewayHelper.ts @@ -0,0 +1,18 @@ +import { APIGatewayClient, GetRestApisCommand } from "@aws-sdk/client-api-gateway"; + +export async function getRestApiGatewayBaseUrl( + apiName: string, + region: string +): Promise { + const client = new APIGatewayClient({ region }); + + + const apis = await client.send(new GetRestApisCommand({})); + const api = apis.items?.find((a) => a.name === apiName); + + if (!api?.id) throw new Error(`API with name "${apiName}" not found.`); + + const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; + console.log(`API Gateway URL: ${url}`); + return url; +} diff --git a/tests/helpers/validateJsonSchema.ts b/tests/helpers/validateJsonSchema.ts new file mode 100644 index 00000000..fab9718d --- /dev/null +++ b/tests/helpers/validateJsonSchema.ts @@ -0,0 +1,39 @@ +import OpenAPIResponseValidator from "openapi-response-validator"; +import openapiDoc from "../../build/notify-supplier.json" assert { type: "json" }; +type ValidationResult = ReturnType; + +/** + * Validate a response against the OpenAPI spec for a given endpoint and method. + * + * @param method - HTTP method in lowercase ('get', 'post', etc.) + * @param path - API path (must match OpenAPI path exactly, e.g., '/items') + * @param status - HTTP status code returned by the API + * @param body - Response body to validate + * @returns ValidationResult or undefined if valid + */ +export function validateApiResponse( + method: string, + path: string, + status: number, + body: any +): ValidationResult { + const pathItem = (openapiDoc.paths as Record)[path]; + + if (!pathItem) throw new Error(`Path ${path} not found in OpenAPI spec`); + + const operation = pathItem[method]; + if (!operation) throw new Error(`Method ${method.toUpperCase()} not defined for ${path}`); + + // Find the response schema for the actual status code + const responseSchema = operation.responses[status] || operation.responses["default"]; + if (!responseSchema) { + throw new Error(`No schema defined for status ${status} at ${method.toUpperCase()} ${path}`); + } + + const validator = new OpenAPIResponseValidator({ + responses: { [status]: responseSchema }, + components: openapiDoc.components, + }); + + return validator.validateResponse(status, body); +} diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 00000000..0d3cc9d5 --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,186 @@ +{ + "lockfileVersion": 3, + "name": "tests", + "packages": { + "": { + "dependencies": { + "allure-js-commons": "^3.3.3", + "charenc": "^0.0.2", + "crypt": "^0.0.2", + "dotenv": "^17.2.2", + "is-buffer": "^1.1.6", + "md5": "^2.3.0", + "playwright": "^1.54.2", + "playwright-core": "^1.54.2", + "undici-types": "^7.10.0" + }, + "devDependencies": { + "@playwright/test": "^1.55.1", + "@types/node": "^24.3.1", + "allure-commandline": "^2.34.1", + "allure-playwright": "^3.3.3" + }, + "license": "ISC", + "name": "tests", + "version": "1.0.0" + }, + "node_modules/@playwright/test": { + "bin": { + "playwright": "cli.js" + }, + "dependencies": { + "playwright": "1.55.1" + }, + "devOptional": true, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/@types/node": { + "dependencies": { + "undici-types": "~7.13.0" + }, + "dev": true, + "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", + "version": "24.6.1" + }, + "node_modules/@types/node/node_modules/undici-types": { + "dev": true, + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "version": "7.13.0" + }, + "node_modules/allure-commandline": { + "bin": { + "allure": "bin/allure" + }, + "dev": true, + "integrity": "sha512-l42csZ2bz7FdtJI1t5zA3IXtOZ0YJaP/+JMRC9gt6aBHRVUIu+6r+3F7KRyshQ79osLz9/MHlGqAjBPRqH0QFw==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.34.1.tgz", + "version": "2.34.1" + }, + "node_modules/allure-js-commons": { + "dependencies": { + "md5": "^2.3.0" + }, + "integrity": "sha512-Lzs91+8fTPUucnLwQULuuLyEJWQCBCxsZl5tX6lMPFZcAVXgwu/Dti0XfzhH9R1Hw8e43k20XAlmyh/ZDqqpYA==", + "license": "Apache-2.0", + "peerDependencies": { + "allure-playwright": "3.4.1" + }, + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } + }, + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.1.tgz", + "version": "3.4.1" + }, + "node_modules/allure-playwright": { + "dependencies": { + "allure-js-commons": "3.4.1" + }, + "devOptional": true, + "integrity": "sha512-vSc+7iBmilejRbSiv0dakl+/EONHFUs3yDmEOydKmF0+aOMczRyMYOBvU42Ob51PrZozi11ExiQj9SCMH0c4bQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@playwright/test": ">=1.53.0" + }, + "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.1.tgz", + "version": "3.4.1" + }, + "node_modules/charenc": { + "engines": { + "node": "*" + }, + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "version": "0.0.2" + }, + "node_modules/crypt": { + "engines": { + "node": "*" + }, + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "version": "0.0.2" + }, + "node_modules/dotenv": { + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + }, + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "version": "17.2.3" + }, + "node_modules/is-buffer": { + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "version": "1.1.6" + }, + "node_modules/md5": { + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + }, + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "version": "2.3.0" + }, + "node_modules/playwright": { + "bin": { + "playwright": "cli.js" + }, + "dependencies": { + "playwright-core": "1.55.1" + }, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "license": "Apache-2.0", + "optionalDependencies": { + "fsevents": "2.3.2" + }, + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/playwright-core": { + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", + "version": "1.55.1" + }, + "node_modules/undici-types": { + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "version": "7.16.0" + } + }, + "requires": true, + "version": "1.0.0" +} diff --git a/tests/package.json b/tests/package.json index 52113653..659bf618 100644 --- a/tests/package.json +++ b/tests/package.json @@ -24,7 +24,8 @@ "name": "tests", "scripts": { "clean": "rimraf $(pwd)/target", - "test": "playwright test tests --config=config/main.config.ts --max-failures=10" + "test": "playwright test tests --config=config/main.config.ts --max-failures=10 --project=component_tests", + "test:sandbox": "playwright test tests --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" }, "version": "1.0.0" } diff --git a/tests/sandbox/messages/get_single_letter/test_401.spec.ts b/tests/sandbox/get_single_letter/test_401.spec.ts similarity index 62% rename from tests/sandbox/messages/get_single_letter/test_401.spec.ts rename to tests/sandbox/get_single_letter/test_401.spec.ts index 98fde1f4..347af42d 100644 --- a/tests/sandbox/messages/get_single_letter/test_401.spec.ts +++ b/tests/sandbox/get_single_letter/test_401.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { LETTERS_ENDPOINT} from '../../../constants/api_constants'; +import { SUPPLIER_LETTERS, SUPPLIER_API_URL_SANDBOX} from '../../constants/api_constants'; // Constants const status = "PENDING"; @@ -11,10 +11,7 @@ test("401 when invalid APIKEY is passed", async ({ request }) => { Authorization: '1234' }; - const API_URL = process.env.DEV_API_GATEWAY_URL; - const API_GATEWAY_URL = `${API_URL}${LETTERS_ENDPOINT}`; - - const response = await request.get(API_GATEWAY_URL, { + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ params: { status: `${status}` }, diff --git a/tests/sandbox/get_single_letter/test_success.spec.ts b/tests/sandbox/get_single_letter/test_success.spec.ts new file mode 100644 index 00000000..78ef96a2 --- /dev/null +++ b/tests/sandbox/get_single_letter/test_success.spec.ts @@ -0,0 +1,20 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../../constants/api_constants'; +import { createValidSandBoxRequestHeaders } from '../../constants/request_headers'; + +test("200 when valid input is passed", async ({ request }) => { + + const header = await createValidSandBoxRequestHeaders(); + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit:'2' + }, + }, + ); + + + await expect(response.status()).toBe(200); + expect(response.ok()).toBeTruthy(); + }); diff --git a/tests/sandbox/messages/get_single_letter/test_success.spec.ts b/tests/sandbox/messages/get_single_letter/test_success.spec.ts deleted file mode 100644 index 27e04f5a..00000000 --- a/tests/sandbox/messages/get_single_letter/test_success.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { LETTERS_ENDPOINT} from '../../../constants/api_constants'; - -// Constants -const STATUS = 'PENDING'; - -test("200 when valid input is passed", async ({ request }) => { - const headers = { - headerauth1: 'headervalue1', - apikey: process.env.API_KEY!, - Authorization: '1234' - }; - - const API_URL = process.env.DEV_API_GATEWAY_URL; - const API_GATEWAY_URL = `${API_URL}${LETTERS_ENDPOINT}`; - - const response = await request.get(API_GATEWAY_URL, { - params: { - status: `${STATUS}` - }, - headers - }); - - await expect(response.status()).toBe(200); - expect(response.ok()).toBeTruthy(); - }); From 24cc7193e29cab7bbef7b40259deddcc8aaf0167 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 9 Oct 2025 09:57:25 +0100 Subject: [PATCH 07/24] Comp tests --- .../apiGateway-tests/getLetters.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 11017ef2..b456ff3f 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -67,7 +67,7 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(validationResult).toBeUndefined(); }); - test('GET /letters with invalid query param return 400', async ({ request }) => { + test('GET /letters with invalid query param return 400', async ({ request }) => { const header = await createValidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, @@ -79,12 +79,12 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(400); const responseBody = await response.json(); - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } - expect(validationResult).toBeUndefined(); + expect(validationResult).toBeUndefined(); }); }); From af781ad77a21a79134eb850205cad9be24039395 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Mon, 13 Oct 2025 15:44:16 +0100 Subject: [PATCH 08/24] component tests --- tests/.gitignore | 1 + .../apiGateway-tests/getLetters.spec.ts | 3 +- .../testCases/UpdateLetterStatus.ts | 73 ++++++- tests/config/main.config.ts | 13 +- tests/config/playwright.base.config.ts | 20 +- tests/config/sandbox.config.ts | 4 +- tests/constants/api_constants.ts | 2 +- tests/constants/request_headers.ts | 16 +- tests/helpers/awsGatewayHelper.ts | 1 - tests/package-lock.json | 186 ------------------ tests/package.json | 4 +- tests/sandbox/getLetterStatus.spec.ts | 21 ++ tests/sandbox/getListOfLetters.spec.ts | 27 +++ .../get_single_letter/test_401.spec.ts | 22 --- .../get_single_letter/test_success.spec.ts | 20 -- .../testCases/getLetterStatus_testCases.ts | 137 +++++++++++++ .../testCases/getListOfLetters_testCases.ts | 97 +++++++++ .../testCases/updateLetterStatus_testCases.ts | 121 ++++++++++++ .../updateMultipleStatys_testCases.ts | 126 ++++++++++++ tests/sandbox/updateLetterStatus.spec.ts | 22 +++ .../updateMultipleLetterStatus.spec.ts | 19 ++ tests/test_Data/updateStatus.json | 1 - 22 files changed, 656 insertions(+), 280 deletions(-) delete mode 100644 tests/package-lock.json create mode 100644 tests/sandbox/getLetterStatus.spec.ts create mode 100644 tests/sandbox/getListOfLetters.spec.ts delete mode 100644 tests/sandbox/get_single_letter/test_401.spec.ts delete mode 100644 tests/sandbox/get_single_letter/test_success.spec.ts create mode 100644 tests/sandbox/testCases/getLetterStatus_testCases.ts create mode 100644 tests/sandbox/testCases/getListOfLetters_testCases.ts create mode 100644 tests/sandbox/testCases/updateLetterStatus_testCases.ts create mode 100644 tests/sandbox/testCases/updateMultipleStatys_testCases.ts create mode 100644 tests/sandbox/updateLetterStatus.spec.ts create mode 100644 tests/sandbox/updateMultipleLetterStatus.spec.ts delete mode 100644 tests/test_Data/updateStatus.json diff --git a/tests/.gitignore b/tests/.gitignore index 13fafcc9..57b879f0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -8,3 +8,4 @@ node_modules/ /allure-results /target /playwright/.auth/ +/allure-report diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index b456ff3f..625e09b0 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -74,8 +74,7 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => params:{ limit:'?' }, - }, - ); + }); expect(response.status()).toBe(400); const responseBody = await response.json(); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index 7f6ca5c1..b0e52a10 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -10,28 +10,28 @@ type APIPatchMessageRequestTestCase = { expectedResponse?: PatchMessageResponseBody | PatchErrorMessageBody; }; -type PatchMessageRequestBody = { +export type PatchMessageRequestBody = { data: { type: string; id: string; attributes: { - reasonCode: number; - reasonText: string; + reasonCode?: number; + reasonText?: string; status: string; }; }; }; -type PatchMessageResponseBody = { +export type PatchMessageResponseBody = { data: { type: string; id: string; attributes: { - reasonCode: number; - reasonText: string; + reasonCode?: number; + reasonText?: string; status: string; specificationId:string; - groupId:string; + groupId?:string; }; }; }; @@ -49,7 +49,7 @@ type PatchErrorResponse = { detail: string; }; -type PatchErrorMessageBody = { +export type PatchErrorMessageBody = { errors: PatchErrorResponse[]; }; @@ -57,7 +57,7 @@ type PatchErrorMessageBody = { export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = [ { - testCase: '200 response if record is updated and status is REJECTED', + testCase: '200 response if record is updated with status REJECTED', id: '00c61654-24f0-410e-a77e-04deef7d1eeb', body: { data: { @@ -86,6 +86,34 @@ export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = }, }, + { + testCase: '200 response if record is updated with status ACCEPTED', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + body: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + status: 'ACCEPTED', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '00c61654-24f0-410e-a77e-04deef7d1eeb', + attributes: { + reasonCode: 123, + reasonText: 'Test Reason Text', + status: 'ACCEPTED', + specificationId:'specification-id', + groupId:'group-id' + }, + } + }, + }, + { testCase: '400 response if request body is invalid', id: '00c61654-24f0-410e-a77e-04deef7d1eeb', @@ -114,4 +142,31 @@ export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = }] }, }, + + { + testCase: '500 response if id doesnt exist supplierid', + id: '0', + body: { + data: { + type: 'Letter', + id: '0', + attributes: { + status: 'ACCEPTED', + }, + } + }, + expectedStatus: 500, + expectedResponse: { + 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: 'Letter with id 0 not found for supplier supplier-id' + }] + }, + }, ]; diff --git a/tests/config/main.config.ts b/tests/config/main.config.ts index f7bc4280..4c87088a 100644 --- a/tests/config/main.config.ts +++ b/tests/config/main.config.ts @@ -1,20 +1,19 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; -import { config as baseConfig } from './playwright.base.config'; -import { getReporters } from './reporters'; +import {defineConfig, PlaywrightTestConfig } from '@playwright/test'; +import baseConfig from './playwright.base.config'; +import { getReporters } from './reporters'; +import path from 'path'; const localConfig: PlaywrightTestConfig = { ...baseConfig, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: getReporters('api-test'), - //globalSetup: require.resolve('./setup/globalSetup'), - //globalTeardown: require.resolve('./setup/globalTeardown'), projects: [ { name: 'component-tests', - testDir: 'tests/component-tests', + testDir: path.resolve(__dirname, '../component-tests'), testMatch: '**/*.spec.ts', }, ], }; -export default localConfig; +export default defineConfig(localConfig); diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index 473ec325..3395bf7f 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -1,4 +1,4 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; +import { defineConfig, PlaywrightTestConfig } from '@playwright/test'; const baseUrl = process.env.NHSD_APIM_PROXY_URL || 'http://localhost:3000/'; const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; @@ -6,8 +6,6 @@ const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; * See https://playwright.dev/docs/test-configuration. */ export const config: PlaywrightTestConfig = { - testDir: '../tests', - testMatch: '**/*.spec.ts', /* Maximum time one test can run for. */ timeout: 60 * 1000, workers: envMaxInstances, @@ -24,19 +22,5 @@ export const config: PlaywrightTestConfig = { forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, - - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: baseUrl, - ignoreHTTPSErrors: true, - trace: 'on-first-retry', - /* Slows down Playwright operations by the specified amount of milliseconds. */ - launchOptions: { - slowMo: 0, - }, - }, }; -export default config; +export default defineConfig(config); diff --git a/tests/config/sandbox.config.ts b/tests/config/sandbox.config.ts index 935ba910..1990cb93 100644 --- a/tests/config/sandbox.config.ts +++ b/tests/config/sandbox.config.ts @@ -1,6 +1,7 @@ import type { PlaywrightTestConfig } from '@playwright/test'; import { config as baseConfig } from './playwright.base.config'; import { getReporters } from './reporters'; +import path from 'path'; const localConfig: PlaywrightTestConfig = { /* Reporter to use. See https://playwright.dev/docs/test-reporters */ @@ -11,7 +12,8 @@ const localConfig: PlaywrightTestConfig = { projects: [ { name: 'sandbox', - testMatch: '**/sandbox/get_single_letter/*.spec.ts', + testDir: path.resolve(__dirname, '../sandbox'), + testMatch: '*.spec.ts', }, ], }; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index 557e3316..b79204e2 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1,5 +1,5 @@ export const SUPPLIER_API_GATEWAY_NAME = 'nhs-main-supapi'; export const SUPPLIER_LETTERS = 'letters'; export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; -export const SUPPLIER_API_URL_SANDBOX = 'https://internal-sandbox.api.service.nhs.uk/nhs-notify-supplier/'; +export const SUPPLIER_API_URL_SANDBOX = 'https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier'; export const AWS_REGION = 'eu-west-2'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index fe29c039..856e308d 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -30,15 +30,11 @@ export async function createHeaderWithNoCorrelationId(): Promise return requestHeaders; } -export async function createValidSandBoxRequestHeaders(): Promise { - let requestHeaders: RequestSandBoxHeaders; - requestHeaders = { - 'X-Request-ID': randomUUID(), - 'Content-Type':'application/vnd.api+json', - 'X-Correlation-ID': randomUUID(), - }; - return requestHeaders; -} +export const sandBoxHeader: RequestSandBoxHeaders = { + 'X-Request-ID': randomUUID(), + 'Content-Type': 'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), +}; export interface RequestHeaders { headerauth1: string; @@ -47,7 +43,7 @@ export interface RequestHeaders { [key: string]: string; } -interface RequestSandBoxHeaders { +export interface RequestSandBoxHeaders { 'X-Request-ID': string; 'Content-Type': string; 'X-Correlation-ID': string; diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts index 64eae54e..4a912c12 100644 --- a/tests/helpers/awsGatewayHelper.ts +++ b/tests/helpers/awsGatewayHelper.ts @@ -13,6 +13,5 @@ export async function getRestApiGatewayBaseUrl( if (!api?.id) throw new Error(`API with name "${apiName}" not found.`); const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; - console.log(`API Gateway URL: ${url}`); return url; } diff --git a/tests/package-lock.json b/tests/package-lock.json deleted file mode 100644 index 0d3cc9d5..00000000 --- a/tests/package-lock.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "lockfileVersion": 3, - "name": "tests", - "packages": { - "": { - "dependencies": { - "allure-js-commons": "^3.3.3", - "charenc": "^0.0.2", - "crypt": "^0.0.2", - "dotenv": "^17.2.2", - "is-buffer": "^1.1.6", - "md5": "^2.3.0", - "playwright": "^1.54.2", - "playwright-core": "^1.54.2", - "undici-types": "^7.10.0" - }, - "devDependencies": { - "@playwright/test": "^1.55.1", - "@types/node": "^24.3.1", - "allure-commandline": "^2.34.1", - "allure-playwright": "^3.3.3" - }, - "license": "ISC", - "name": "tests", - "version": "1.0.0" - }, - "node_modules/@playwright/test": { - "bin": { - "playwright": "cli.js" - }, - "dependencies": { - "playwright": "1.55.1" - }, - "devOptional": true, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/@types/node": { - "dependencies": { - "undici-types": "~7.13.0" - }, - "dev": true, - "integrity": "sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.1.tgz", - "version": "24.6.1" - }, - "node_modules/@types/node/node_modules/undici-types": { - "dev": true, - "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", - "version": "7.13.0" - }, - "node_modules/allure-commandline": { - "bin": { - "allure": "bin/allure" - }, - "dev": true, - "integrity": "sha512-l42csZ2bz7FdtJI1t5zA3IXtOZ0YJaP/+JMRC9gt6aBHRVUIu+6r+3F7KRyshQ79osLz9/MHlGqAjBPRqH0QFw==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.34.1.tgz", - "version": "2.34.1" - }, - "node_modules/allure-js-commons": { - "dependencies": { - "md5": "^2.3.0" - }, - "integrity": "sha512-Lzs91+8fTPUucnLwQULuuLyEJWQCBCxsZl5tX6lMPFZcAVXgwu/Dti0XfzhH9R1Hw8e43k20XAlmyh/ZDqqpYA==", - "license": "Apache-2.0", - "peerDependencies": { - "allure-playwright": "3.4.1" - }, - "peerDependenciesMeta": { - "allure-playwright": { - "optional": true - } - }, - "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.1.tgz", - "version": "3.4.1" - }, - "node_modules/allure-playwright": { - "dependencies": { - "allure-js-commons": "3.4.1" - }, - "devOptional": true, - "integrity": "sha512-vSc+7iBmilejRbSiv0dakl+/EONHFUs3yDmEOydKmF0+aOMczRyMYOBvU42Ob51PrZozi11ExiQj9SCMH0c4bQ==", - "license": "Apache-2.0", - "peerDependencies": { - "@playwright/test": ">=1.53.0" - }, - "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.1.tgz", - "version": "3.4.1" - }, - "node_modules/charenc": { - "engines": { - "node": "*" - }, - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "version": "0.0.2" - }, - "node_modules/crypt": { - "engines": { - "node": "*" - }, - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "version": "0.0.2" - }, - "node_modules/dotenv": { - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - }, - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "license": "BSD-2-Clause", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "version": "17.2.3" - }, - "node_modules/is-buffer": { - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "version": "1.1.6" - }, - "node_modules/md5": { - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - }, - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "license": "BSD-3-Clause", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "version": "2.3.0" - }, - "node_modules/playwright": { - "bin": { - "playwright": "cli.js" - }, - "dependencies": { - "playwright-core": "1.55.1" - }, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", - "license": "Apache-2.0", - "optionalDependencies": { - "fsevents": "2.3.2" - }, - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/playwright-core": { - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", - "license": "Apache-2.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", - "version": "1.55.1" - }, - "node_modules/undici-types": { - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "version": "7.16.0" - } - }, - "requires": true, - "version": "1.0.0" -} diff --git a/tests/package.json b/tests/package.json index 659bf618..05f51203 100644 --- a/tests/package.json +++ b/tests/package.json @@ -24,8 +24,8 @@ "name": "tests", "scripts": { "clean": "rimraf $(pwd)/target", - "test": "playwright test tests --config=config/main.config.ts --max-failures=10 --project=component_tests", - "test:sandbox": "playwright test tests --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" + "test:component": "playwright test --config=config/main.config.ts --max-failures=10 --project=component-tests", + "test:sandbox": "playwright test --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" }, "version": "1.0.0" } diff --git a/tests/sandbox/getLetterStatus.spec.ts b/tests/sandbox/getLetterStatus.spec.ts new file mode 100644 index 00000000..3d10845d --- /dev/null +++ b/tests/sandbox/getLetterStatus.spec.ts @@ -0,0 +1,21 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxGetLetterStatusTestData } from './testCases/getLetterStatus_testCases'; + + +test.describe('Sandbox Tests To Get Letter Status', () => +{ + apiSandboxGetLetterStatusTestData.forEach(({ testCase, header, id, expectedStatus, expectedResponse }) => { + test(`Get Letter Status endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: header + }, + ); + + const res = await response.json(); + expect(res).toEqual(expectedResponse); + + }); + }); +}); diff --git a/tests/sandbox/getListOfLetters.spec.ts b/tests/sandbox/getListOfLetters.spec.ts new file mode 100644 index 00000000..e2bca376 --- /dev/null +++ b/tests/sandbox/getListOfLetters.spec.ts @@ -0,0 +1,27 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxGetLettersRequestTestData } from './testCases/getListOfLetters_testCases'; + + +test.describe('Sandbox Tests To Get List Of Pending Letters ', () => +{ + apiSandboxGetLettersRequestTestData.forEach(({ testCase, header, limit, expectedStatus, expectedResponse }) => { + test(`Get /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + params:{ + limit: limit + }, + }, + ); + + const res = await response.json(); + await expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + if (response.status() === 200){ + expect(res.data.length.toString()).toEqual(limit); + } + }); + }); +}); diff --git a/tests/sandbox/get_single_letter/test_401.spec.ts b/tests/sandbox/get_single_letter/test_401.spec.ts deleted file mode 100644 index 347af42d..00000000 --- a/tests/sandbox/get_single_letter/test_401.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS, SUPPLIER_API_URL_SANDBOX} from '../../constants/api_constants'; - -// Constants -const status = "PENDING"; - -test("401 when invalid APIKEY is passed", async ({ request }) => { - const headers = { - headerauth1 : 'headervalue1', - apikey : '', - Authorization: '1234' - }; - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - params: { - status: `${status}` - }, - headers - }); - - await expect(response.status()).toBe(401); - }); diff --git a/tests/sandbox/get_single_letter/test_success.spec.ts b/tests/sandbox/get_single_letter/test_success.spec.ts deleted file mode 100644 index 78ef96a2..00000000 --- a/tests/sandbox/get_single_letter/test_success.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../../constants/api_constants'; -import { createValidSandBoxRequestHeaders } from '../../constants/request_headers'; - -test("200 when valid input is passed", async ({ request }) => { - - const header = await createValidSandBoxRequestHeaders(); - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit:'2' - }, - }, - ); - - - await expect(response.status()).toBe(200); - expect(response.ok()).toBeTruthy(); - }); diff --git a/tests/sandbox/testCases/getLetterStatus_testCases.ts b/tests/sandbox/testCases/getLetterStatus_testCases.ts new file mode 100644 index 00000000..9131f9d5 --- /dev/null +++ b/tests/sandbox/testCases/getLetterStatus_testCases.ts @@ -0,0 +1,137 @@ +import { randomUUID } from "node:crypto"; +import { RequestSandBoxHeaders, sandBoxHeader} from "../../constants/request_headers"; +import { NoRequestIdHeaders, SandboxErrorResponse, SandboxSuccessResponse } from "./getListOfLetters_testCases"; + +type ApiSandboxGetLetterStatusTestCase = { + testCase: string; + id: string, + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: GetLetterStatusResposne | GetLetterStatusErrorResponse | GetRejectedLetterResponse; +}; + +export type GetLetterStatusResposne = { + data: GetLetterData; +}; + +export type GetRejectedLetterResponse = { + data: RejectedLetterData; +}; + +type GetLetterData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + } +}; + +type RejectedLetterData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + reasonCode: number; + reasonText: string; + } +}; + +export type GetLetterStatusErrorResponse = { + errors: ApiErrors[]; +}; + +type ApiErrors = { + code: string; + detail: string; + id: string; + links:{ + about: string; + }, + status: string; + title: string; +}; + +export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCase[] = [ +{ + testCase: '200 response and ACCEPTED record is fetched successfully', + id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'ACCEPTED', + }, + } + } +}, +{ + testCase: '200 response and REJECTED record is fetched successfully', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'REJECTED', + reasonCode: 100, + reasonText: "failed validation", + }, + } + } +}, +{ + testCase: '200 response and CANCELLED record is fetched successfully', + id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: + { + id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'CANCELLED', + reasonCode: 100 + }, + } + } +}, +{ + testCase: '404 response when no record is found for the given id', + id: '24L5eYSWGzCHlGmzNxuqVusP', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + errors: [ + { + status: '404', + title: 'Resource not found', + code:'NOTIFY_RESOURCE_NOT_FOUND', + detail: 'No resource found with that ID', + id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', + links: { + about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier' + } + } + ] + } +}]; diff --git a/tests/sandbox/testCases/getListOfLetters_testCases.ts b/tests/sandbox/testCases/getListOfLetters_testCases.ts new file mode 100644 index 00000000..422786d0 --- /dev/null +++ b/tests/sandbox/testCases/getListOfLetters_testCases.ts @@ -0,0 +1,97 @@ +import { RequestSandBoxHeaders, sandBoxHeader} from '../../constants/request_headers'; +import { randomUUID } from 'node:crypto'; + + +type ApiSandboxGetLettersRequestTestCase = { + testCase: string; + limit: string, + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: SandboxSuccessResponse | SandboxErrorResponse; +}; + +export type SandboxSuccessResponse = { + data: ApiData []; +}; + +type ApiData = +{ + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + } +}; + +export type SandboxErrorResponse = { + message: string; + errors: ApiErrors[]; +}; + +type ApiErrors = { + path: string; + message: string; + errorCode: string; +}; + +export type NoRequestIdHeaders = Omit; + +const NoRequestIdHeaders: NoRequestIdHeaders = { + 'Content-Type': 'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), +}; + +export const apiSandboxGetLettersRequestTestData: ApiSandboxGetLettersRequestTestCase[] = [ +{ + testCase: '200 response if record is fetched successfully', + limit: '1', + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: [ + { + id: 'fcfd849ceec940e8832b41f4fc161e09', + type: 'Letter', + attributes: { + specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + groupId: 'c5d93f917f5546d08beccf770a915d96', + status: 'PENDING', + }, + }] + }, +}, +{ + testCase: '400 response if invalid limit is passed', + limit: 'XX', + header: sandBoxHeader, + expectedStatus: 400, + expectedResponse: { + message: 'request.query.limit should be number', + errors: [ + { + path:'.query.limit', + message:'should be number', + errorCode:'type.openapi.validation' + } + ] + } +}, + +{ + testCase: '400 response if invalid headers are passed', + limit: '2', + header: NoRequestIdHeaders, + expectedStatus: 400, + expectedResponse: { + message: "request.headers should have required property 'x-request-id'", + errors: [ + { + path: '.headers.x-request-id', + message: "should have required property 'x-request-id'", + errorCode:'required.openapi.validation' + } + ] + } +}]; diff --git a/tests/sandbox/testCases/updateLetterStatus_testCases.ts b/tests/sandbox/testCases/updateLetterStatus_testCases.ts new file mode 100644 index 00000000..491c79d5 --- /dev/null +++ b/tests/sandbox/testCases/updateLetterStatus_testCases.ts @@ -0,0 +1,121 @@ + +import { PatchErrorMessageBody, PatchMessageRequestBody, PatchMessageResponseBody } from '../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus'; +import { RequestSandBoxHeaders, sandBoxHeader } from '../../constants/request_headers'; +import { SandboxErrorResponse } from './getListOfLetters_testCases'; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + id: string, + header: RequestSandBoxHeaders; + body?: PatchMessageRequestBody; + expectedStatus: number; + expectedResponse?: PatchMessageResponseBody | SandboxErrorResponse | PatchErrorMessageBody; +}; + +export const apiSandboxUpdateLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = [ + { + testCase: '200 response if record is updated with status PENDING', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'PENDING', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'PENDING', + specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', + }, + } + }, + }, + + { + testCase: '200 response if record is updated with status REJECTED', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'REJECTED', + reasonCode: 100, + reasonText: 'failed validation', + }, + } + }, + expectedStatus: 200, + expectedResponse: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'REJECTED', + specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', + }, + } + }, + }, + { + testCase: '404 response if no resource is found for the given id', + id: '0', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '0', + attributes: { + status: 'PENDING', + }, + } + }, + expectedStatus: 404, + expectedResponse: { + errors: [{ + id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', + code: 'NOTIFY_RESOURCE_NOT_FOUND', + links: { + about: "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + }, + status: '404', + title: 'Resource not found', + detail: 'No resource found with that ID' + }] + }, + }, + { + testCase: '400 response if request body is invalid', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + header: sandBoxHeader, + body: { + data: { + type: 'Letter', + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + attributes: { + status: 'NO_STATUS', + }, + } + }, + expectedStatus: 400, + expectedResponse: { + message: 'request.body.data.attributes.status should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', + errors: [{ + path: '.body.data.attributes.status', + message: 'should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', + errorCode: 'enum.openapi.validation' + }] + }, + }, +]; diff --git a/tests/sandbox/testCases/updateMultipleStatys_testCases.ts b/tests/sandbox/testCases/updateMultipleStatys_testCases.ts new file mode 100644 index 00000000..69e31b54 --- /dev/null +++ b/tests/sandbox/testCases/updateMultipleStatys_testCases.ts @@ -0,0 +1,126 @@ +import { PatchMessageRequestBody } from "../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus"; +import { RequestSandBoxHeaders, sandBoxHeader } from "../../constants/request_headers"; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + header: RequestSandBoxHeaders; + body: PostMessageRequestBody; + expectedStatus: number; +}; + +type PostMessageRequestBody = { + data: postRequest [] +} + +type postRequest = { + type: string; + id: string; + attributes: { + reasonCode?: number; + reasonText?: string; + status: string; + } +}; + +export const apiSandboxMultipleLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = +[{ + testCase: '200 response if records are updated', + header: sandBoxHeader, + body:{ + data : + [{ + attributes: { + status: 'PENDING' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + 'status': 'ACCEPTED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'PRINTED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'ENCLOSED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'DISPATCHED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + status: 'DELIVERED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'RETURNED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'CANCELLED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'FAILED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + }, + { + attributes: { + reasonCode: 100, + reasonText: 'failed validation', + status: 'RETURNED' + }, + id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', + type: 'Letter' + } + ]}, + expectedStatus: 200 +}, +{ + testCase: '400 response if invalid request is passed', + header: sandBoxHeader, + body:{ + data : + [{ + attributes: { + status: 'PENDING' + }, + id: '1234', + type: 'Letter' + }] + }, + expectedStatus:404, +}]; diff --git a/tests/sandbox/updateLetterStatus.spec.ts b/tests/sandbox/updateLetterStatus.spec.ts new file mode 100644 index 00000000..9e0d9a4d --- /dev/null +++ b/tests/sandbox/updateLetterStatus.spec.ts @@ -0,0 +1,22 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; + + +test.describe('Sandbox Tests To Update Letter Status', () => +{ + apiSandboxUpdateLetterStatusTestData.forEach(({ testCase, header, id, body, expectedStatus, expectedResponse }) => { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.patch(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ + headers: header, + data: body + }); + + const res = await response.json(); + expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + + }); + }); +}); diff --git a/tests/sandbox/updateMultipleLetterStatus.spec.ts b/tests/sandbox/updateMultipleLetterStatus.spec.ts new file mode 100644 index 00000000..64f6bb2b --- /dev/null +++ b/tests/sandbox/updateMultipleLetterStatus.spec.ts @@ -0,0 +1,19 @@ +import { test, expect, request } from '@playwright/test'; +import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; +import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; +import { apiSandboxMultipleLetterStatusTestData } from './testCases/updateMultipleStatys_testCases'; + + +test.describe('Sandbox Tests To Update Multiple Letter Status', () => +{ + apiSandboxMultipleLetterStatusTestData.forEach(({ testCase, header, body, expectedStatus }) => { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + + const response = await request.post(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ + headers: header, + data: body + }); + expect(response.status()).toBe(expectedStatus); + }); + }); +}); diff --git a/tests/test_Data/updateStatus.json b/tests/test_Data/updateStatus.json deleted file mode 100644 index 0967ef42..00000000 --- a/tests/test_Data/updateStatus.json +++ /dev/null @@ -1 +0,0 @@ -{} From 84378d66cb4534b7473710d18f7a9f8b3ce1aec9 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 15 Oct 2025 12:48:30 +0100 Subject: [PATCH 09/24] review comments --- tests/package.json | 2 +- tests/sandbox/testCases/getLetterStatus_testCases.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/package.json b/tests/package.json index 05f51203..2a1b72ff 100644 --- a/tests/package.json +++ b/tests/package.json @@ -21,7 +21,7 @@ "keywords": [], "license": "ISC", "main": "index.js", - "name": "tests", + "name": "nhs-notify-supplier-api-tests", "scripts": { "clean": "rimraf $(pwd)/target", "test:component": "playwright test --config=config/main.config.ts --max-failures=10 --project=component-tests", diff --git a/tests/sandbox/testCases/getLetterStatus_testCases.ts b/tests/sandbox/testCases/getLetterStatus_testCases.ts index 9131f9d5..54333de1 100644 --- a/tests/sandbox/testCases/getLetterStatus_testCases.ts +++ b/tests/sandbox/testCases/getLetterStatus_testCases.ts @@ -7,10 +7,10 @@ type ApiSandboxGetLetterStatusTestCase = { id: string, header?: RequestSandBoxHeaders | NoRequestIdHeaders; expectedStatus: number; - expectedResponse?: GetLetterStatusResposne | GetLetterStatusErrorResponse | GetRejectedLetterResponse; + expectedResponse?: GetLetterStatusResponse | GetLetterStatusErrorResponse | GetRejectedLetterResponse; }; -export type GetLetterStatusResposne = { +export type GetLetterStatusResponse = { data: GetLetterData; }; From c17261e9105459207c4026e553389483dc3e286f Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 15 Oct 2025 16:18:37 +0100 Subject: [PATCH 10/24] tests on pipeline --- .github/workflows/stage-4-acceptance.yaml | 6 +++--- Makefile | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index 59ec8012..5549bd36 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -108,7 +108,7 @@ jobs: - name: "Save result" run: | echo "Nothing to save" - test-integration: + test-component: name: "Integration test" runs-on: ubuntu-latest needs: environment-set-up @@ -118,7 +118,7 @@ jobs: uses: actions/checkout@v4 - name: "Run integration test" run: | - make test-integration + make test-component - name: "Save result" run: | echo "Nothing to save" @@ -157,7 +157,7 @@ jobs: [ test-accessibility, test-contract, - test-integration, + test-component, test-load, test-security, test-ui-performance, diff --git a/Makefile b/Makefile index 28fb24c7..91e8d688 100644 --- a/Makefile +++ b/Makefile @@ -98,6 +98,8 @@ config:: _install-dependencies version # Configure development environment (main npm install (cd docs && make install && cd ..) +test-component: + (cd tests && npm install && npm run test:component) version: rm -f .version From e4ac798700adfffedc500b71aaedb2da41a2d0af Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 15 Oct 2025 16:30:48 +0100 Subject: [PATCH 11/24] dependencies --- tests/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/package.json b/tests/package.json index 2a1b72ff..26bdea62 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,12 +1,14 @@ { "author": "", "dependencies": { + "@aws-sdk/client-api-gateway": "^3.906.0", "allure-js-commons": "^3.3.3", "charenc": "^0.0.2", "crypt": "^0.0.2", "dotenv": "^17.2.2", "is-buffer": "^1.1.6", "md5": "^2.3.0", + "openapi-response-validator": "^12.1.3", "playwright": "^1.54.2", "playwright-core": "^1.54.2", "undici-types": "^7.10.0" From aa08f806058b08cf74d93eeaa64ee0482c5d45b2 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 22 Oct 2025 00:40:28 +0100 Subject: [PATCH 12/24] tests --- .github/workflows/stage-4-acceptance.yaml | 3 + .../src/helpers/create_letter_helpers.ts | 3 +- .../apiGateway-tests/getLetters.spec.ts | 4 +- .../testCases/UpdateLetterStatus.ts | 173 ++++-------------- .../updateLetterStatus.spec.ts | 120 ++++++++++-- tests/config/playwright.base.config.ts | 1 + tests/constants/api_constants.ts | 8 +- tests/constants/request_headers.ts | 52 +++--- tests/helpers/awsGatewayHelper.ts | 13 +- tests/helpers/generate_fetch_testData.ts | 70 +++++++ tests/helpers/pnpmHelpers.ts | 76 ++++++++ 11 files changed, 335 insertions(+), 188 deletions(-) create mode 100644 tests/helpers/generate_fetch_testData.ts create mode 100644 tests/helpers/pnpmHelpers.ts diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index 5549bd36..c3b41a51 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -116,6 +116,9 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v4 + - name: "Set API Suffix" + run: | + echo "API_SUFFIX=${{ inputs.pr_number }}" >> $GITHUB_ENV - name: "Run integration test" run: | make test-component diff --git a/scripts/test-data/src/helpers/create_letter_helpers.ts b/scripts/test-data/src/helpers/create_letter_helpers.ts index 6af4d0db..029d30fc 100644 --- a/scripts/test-data/src/helpers/create_letter_helpers.ts +++ b/scripts/test-data/src/helpers/create_letter_helpers.ts @@ -46,6 +46,7 @@ export async function createLetter(params: { const letterRecord = await letterRepository.putLetter(letter); console.log(letterRecord); + return letterRecord; } export function createLetterDto(params: { @@ -75,6 +76,4 @@ export function createLetterDto(params: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; - - return letter; } diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 625e09b0..08a2db73 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; import { validateApiResponse } from '../../helpers/validateJsonSchema'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; @@ -8,7 +8,7 @@ let baseUrl: string; test.beforeAll(async () => { const region = AWS_REGION; - baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); + baseUrl = await getRestApiGatewayBaseUrl(); }); test.describe('API Gateway Tests To Get List Of Pending ', () => diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index b0e52a10..56bdfd96 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -1,14 +1,6 @@ -import { createValidRequestHeaders, RequestHeaders } from '../../../constants/request_headers'; -import { test } from '@playwright/test'; - -type APIPatchMessageRequestTestCase = { - testCase: string; - id: string, - body?: PatchMessageRequestBody; - expectedStatus: number; - expectedResponse?: PatchMessageResponseBody | PatchErrorMessageBody; -}; +import { RequestHeaders } from '../../../constants/request_headers'; +import { supplierId } from '../../../constants/api_constants'; export type PatchMessageRequestBody = { data: { @@ -36,137 +28,48 @@ export type PatchMessageResponseBody = { }; }; -export type ErrorLink = { - about: string; +export async function patchRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': supplierId, + 'NHSD-Correlation-ID': '12344', + }; + return requestHeaders; }; -type PatchErrorResponse = { - id: string; - code: string; - links: ErrorLink; - status: string; - title: string; - detail: string; -}; -export type PatchErrorMessageBody = { - errors: PatchErrorResponse[]; -}; +export async function patchValidRequestBody (id: string, status: string) : Promise{ + let requestBody: PatchMessageRequestBody; + requestBody = { + data: { +  attributes: { +   status: status, + }, + type: 'Letter', + id: id + } + }; + return requestBody; +} -export const apiPatchMessageRequestTestData: APIPatchMessageRequestTestCase[] = [ - { - testCase: '200 response if record is updated with status REJECTED', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - body: { - data: { - type: 'Letter', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - attributes: { - reasonCode: 123, - reasonText: 'Test Reason Text', - status: 'REJECTED', - }, - } - }, - expectedStatus: 200, - expectedResponse: { - data: { - type: 'Letter', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - attributes: { - reasonCode: 123, - reasonText: 'Test Reason Text', - status: 'REJECTED', - specificationId:'specification-id', - groupId:'group-id' - }, - } - }, - }, +export async function patchFailureRequestBody (id: string, status: string) : Promise{ + let requestBody: PatchMessageRequestBody; - { - testCase: '200 response if record is updated with status ACCEPTED', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - body: { - data: { - type: 'Letter', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - attributes: { - status: 'ACCEPTED', - }, - } - }, - expectedStatus: 200, - expectedResponse: { - data: { - type: 'Letter', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - attributes: { - reasonCode: 123, - reasonText: 'Test Reason Text', - status: 'ACCEPTED', - specificationId:'specification-id', - groupId:'group-id' - }, - } - }, - }, + requestBody = { + data: { +  attributes: { +   status: status, + reasonCode: 123, + reasonText: 'Test Reason' - { - testCase: '400 response if request body is invalid', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - body: { - data: { - type: 'Letter', - id: '00c61654-24f0-410e-a77e-04deef7d1eeb', - attributes: { - reasonCode: 123, - reasonText: 'Test Reason Text', - status: '', - }, - } - }, - expectedStatus: 400, - expectedResponse: { - errors: [{ - id: '1234', - 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' - }] - }, - }, + }, + type: 'Letter', + id: id + } - { - testCase: '500 response if id doesnt exist supplierid', - id: '0', - body: { - data: { - type: 'Letter', - id: '0', - attributes: { - status: 'ACCEPTED', - }, - } - }, - expectedStatus: 500, - expectedResponse: { - 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: 'Letter with id 0 not found for supplier supplier-id' - }] - }, - }, -]; + }; + return requestBody; +} diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts index 3e7efec0..2f318be6 100644 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -1,28 +1,118 @@ import { test, expect } from '@playwright/test'; -import { SUPPLIER_API_GATEWAY_NAME, SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { SUPPLIER_LETTERS, supplierId } from '../../constants/api_constants'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { createValidRequestHeaders } from '../../constants/request_headers'; -import { apiPatchMessageRequestTestData } from './testCases/UpdateLetterStatus'; +import { 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'; let baseUrl: string; test.beforeAll(async () => { - const region = AWS_REGION; - baseUrl = await getRestApiGatewayBaseUrl(SUPPLIER_API_GATEWAY_NAME, region); + baseUrl = await getRestApiGatewayBaseUrl(); }); -test.describe('API Gateway Tests To Verify Patch Status Endpoint ', () => { - apiPatchMessageRequestTestData.forEach(({ testCase, id, body, expectedStatus, expectedResponse }) => { - test(`Patch /letters returns ${testCase}`, async ({ request }) => { - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}` ,{ - headers: await createValidRequestHeaders(), +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); + + if (!letters?.length) { + test.fail(true, `No PENDING letters found for supplier ${supplierId}`); + return; + } + const letter = letters[0]; + const headers = await patchRequestHeaders(); + const body = await patchValidRequestBody(letter.id, 'ACCEPTED'); + + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { + headers: headers, data: body - }, - ); - const res = await response.json(); + }); - expect(response.status()).toBe(expectedStatus); - expect(res).toEqual(expectedResponse); + const res = await response.json(); + expect(response.status()).toBe(200); + expect(res).toMatchObject({ + data:{ + attributes: { + status: 'ACCEPTED', + specificationId: 'TestSpecificationID', + groupId: 'TestGroupID', + }, + id: letter.id, + type: 'Letter' + } }); + + await deleteLettersBySupplier(letter.id); }); + + test(`Patch /letters returns 200 and status is updated to REJECTED`, async ({ request }) => { + + await createTestData(supplierId); + 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 = await patchRequestHeaders(); + const body = await patchFailureRequestBody(letter.id, 'REJECTED'); + + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { + headers: headers, + data: body + }); + + const res = await response.json(); + expect(response.status()).toBe(200); + await deleteLettersBySupplier(letter.id); + }); + + test(`Patch /letters returns 400 if request Body is invalid`, async ({ request }) => { + + const id = randomUUID() + const headers = await patchRequestHeaders(); + const body = await patchValidRequestBody(id, ''); + + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers: headers, + data: body + }); + + const res = await response.json(); + + expect(response.status()).toBe(400); + }); + + test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => { + const headers = await patchRequestHeaders(); + const id = randomUUID() + const body = await patchValidRequestBody(id, 'PENDING'); + + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers: headers, + data: body + }); + + const res = await response.json(); + expect(response.status()).toBe(500); + }); + + test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => { + const headers = await createInvalidRequestHeaders(); + const id = randomUUID() + const body = await patchValidRequestBody(id, 'PENDING'); + + const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers: headers, + data: body + }); + + const res = await response.json(); + console.log(res); + expect(response.status()).toBe(403); + }); }); diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index 3395bf7f..c6edb35a 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -1,4 +1,5 @@ import { defineConfig, PlaywrightTestConfig } from '@playwright/test'; +import 'dotenv/config'; const baseUrl = process.env.NHSD_APIM_PROXY_URL || 'http://localhost:3000/'; const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index b79204e2..e32fe74c 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1,5 +1,11 @@ -export const SUPPLIER_API_GATEWAY_NAME = 'nhs-main-supapi'; export const SUPPLIER_LETTERS = 'letters'; export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; 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'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index 856e308d..a3f703e8 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -1,13 +1,23 @@ import { randomUUID } from 'node:crypto'; -export async function createValidRequestHeaders(): Promise { - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': 'supplier-id', - 'NHSD-Correlation-ID': '1234', - }; - return requestHeaders; +export const sandBoxHeader: RequestSandBoxHeaders = { + 'X-Request-ID': randomUUID(), + 'Content-Type': 'application/vnd.api+json', + 'X-Correlation-ID': randomUUID(), +}; + +export interface RequestHeaders { + headerauth1: string; + 'NHSD-Supplier-ID': string; + 'NHSD-Correlation-ID': string; + [key: string]: string; +} + +export interface RequestSandBoxHeaders { + 'X-Request-ID': string; + 'Content-Type': string; + 'X-Correlation-ID': string; + [key: string]: string; } export async function createInvalidRequestHeaders(): Promise { @@ -30,22 +40,12 @@ export async function createHeaderWithNoCorrelationId(): Promise return requestHeaders; } -export const sandBoxHeader: RequestSandBoxHeaders = { - 'X-Request-ID': randomUUID(), - 'Content-Type': 'application/vnd.api+json', - 'X-Correlation-ID': randomUUID(), -}; - -export interface RequestHeaders { - headerauth1: string; - 'NHSD-Supplier-ID': string; - 'NHSD-Correlation-ID': string; - [key: string]: string; -} - -export interface RequestSandBoxHeaders { - 'X-Request-ID': string; - 'Content-Type': string; - 'X-Correlation-ID': string; - [key: string]: string; +export async function createValidRequestHeaders(): Promise { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || '', + 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Correlation-ID': '12345', + }; + return requestHeaders; } diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts index 4a912c12..951e3e9e 100644 --- a/tests/helpers/awsGatewayHelper.ts +++ b/tests/helpers/awsGatewayHelper.ts @@ -1,16 +1,15 @@ import { APIGatewayClient, GetRestApisCommand } from "@aws-sdk/client-api-gateway"; +import { AWS_REGION, API_NAME } from "../constants/api_constants"; -export async function getRestApiGatewayBaseUrl( - apiName: string, - region: string -): Promise { - const client = new APIGatewayClient({ region }); +export async function getRestApiGatewayBaseUrl(): Promise { + const region = AWS_REGION; + const client = new APIGatewayClient({ region }); const apis = await client.send(new GetRestApisCommand({})); - const api = apis.items?.find((a) => a.name === apiName); + const api = apis.items?.find((a) => a.name === API_NAME); - if (!api?.id) throw new Error(`API with name "${apiName}" not found.`); + if (!api?.id) throw new Error(`API with name "${API_NAME}" not found.`); const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; return url; diff --git a/tests/helpers/generate_fetch_testData.ts b/tests/helpers/generate_fetch_testData.ts new file mode 100644 index 00000000..3dafa9a9 --- /dev/null +++ b/tests/helpers/generate_fetch_testData.ts @@ -0,0 +1,70 @@ +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'; + +const ddb = new DynamoDBClient({}); +const docClient = DynamoDBDocumentClient.from(ddb); + +export interface SupplierApiLetters { + supplierId: string, + specificationId: string, + supplierStatus: string, + createdAt: string, + supplierStatusSk: string, + updatedAt: string, + groupId: string, + reasonCode: string, + id: string, + url: string, + ttl: string, + reasonText: string, + status: string +}; + +export async function createTestData(supplierId: string): Promise { + + await runCreateLetter({ + filter: 'nhs-notify-supplier-api-data-generator', + supplierId: supplierId, + environment: envName, + awsAccountId: '820178564574', + groupId: 'TestGroupID', + specificationId: 'TestSpecificationID', + status: 'PENDING', + count: 1, + }); +}; + +export const getLettersBySupplier = async(supplierId: string, status: string, limit: number) => { + + const supplierStatus = `${supplierId}#${status}`; + const params = { + TableName: LETTERSTABLENAME, + IndexName: 'supplierStatus-index', + KeyConditionExpression: 'supplierStatus = :supplierStatus', + ProjectionExpression: + 'id, specificationId, groupId, reasonCode, reasonText', + ExpressionAttributeValues: { + ':supplierStatus': supplierStatus, + }, + Limit: limit, + }; + + const { Items } = await docClient.send(new QueryCommand(params)); + if (!Items || Items.length === 0) { + throw new Error(`Unexpectedly found no data found for ${supplierId}.`); + } + return Items as SupplierApiLetters[]; +}; + +export const deleteLettersBySupplier = async(id: string) => { + const resp = await docClient.send( + new DeleteCommand({ + TableName: LETTERSTABLENAME, + Key: { supplierId, id }, + ReturnValues: 'ALL_OLD', + }) + ) + return resp.Attributes; +} diff --git a/tests/helpers/pnpmHelpers.ts b/tests/helpers/pnpmHelpers.ts new file mode 100644 index 00000000..e0e748c0 --- /dev/null +++ b/tests/helpers/pnpmHelpers.ts @@ -0,0 +1,76 @@ +import { spawn } from 'child_process'; +import path from 'path'; + +/** + * Runs the "create-letter" CLI command via npm. + * + * @param options Command-line options for the script. + */ +export async function runCreateLetter(options: { + filter?: string; + supplierId: string; + environment: string; + awsAccountId: string; + groupId: string; + specificationId: string; + status: string; + count: number; +}) { + const { + filter, + supplierId, + environment, + awsAccountId, + groupId, + specificationId, + status, + count, + } = options; + + const workspaceRoot = path.resolve(__dirname, '../../scripts/test-data'); + const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const root = path.resolve(workspaceRoot); + console.log('Workspace root:', root); + + // Build arguments array + const args = [ + '-w', String(filter), + // '--filter', String(filter), + 'run', + 'cli', + '--', + 'create-letter', + '--supplier-id', + supplierId, + '--environment', + environment, + '--awsAccountId', + awsAccountId, + '--group-id', + groupId, + '--specification-id', + specificationId, + '--status', + status, + '--count', + String(count), + ]; + console.log('🚀 Running:', [cmd, ...args].join(' ')); + + await new Promise((resolve, reject) => { + let output = ''; + const child = spawn(cmd, args, { + stdio: 'inherit', + cwd: root, + shell: false, + }); + child.stdout?.on('id', (id) => { + const text = id.toString(); + output += text; + process.stdout.write(text); + }); + + child.on('close', (code) => code === 0 ? resolve() : reject(new Error(`pnpm exited with ${code}`))); + child.on('error', reject); + }); +} From af33e9ec05174d32142c784f8e9a0bd58dac1e8a Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 22 Oct 2025 13:01:48 +0100 Subject: [PATCH 13/24] revert test data changes --- scripts/test-data/src/helpers/create_letter_helpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/test-data/src/helpers/create_letter_helpers.ts b/scripts/test-data/src/helpers/create_letter_helpers.ts index 029d30fc..6af4d0db 100644 --- a/scripts/test-data/src/helpers/create_letter_helpers.ts +++ b/scripts/test-data/src/helpers/create_letter_helpers.ts @@ -46,7 +46,6 @@ export async function createLetter(params: { const letterRecord = await letterRepository.putLetter(letter); console.log(letterRecord); - return letterRecord; } export function createLetterDto(params: { @@ -76,4 +75,6 @@ export function createLetterDto(params: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; + + return letter; } From 5b8ac879aad201d8fb6f191efcf45929acc245ce Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 22 Oct 2025 15:39:08 +0100 Subject: [PATCH 14/24] fix --- .github/workflows/stage-4-acceptance.yaml | 5 +++++ tests/package.json | 2 ++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index c3b41a51..82a5b9d1 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -52,6 +52,11 @@ jobs: - name: "Deploy application" run: | echo "Deploying application..." + - name: "Build OAS file" + working-directory: . + run: | + make publish-oas + shell: bash test-contract: name: "Contract test" runs-on: ubuntu-latest diff --git a/tests/package.json b/tests/package.json index 26bdea62..78743234 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,6 +2,8 @@ "author": "", "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", "allure-js-commons": "^3.3.3", "charenc": "^0.0.2", "crypt": "^0.0.2", From 406d529e86cd32e0dbd8e50442de41ff13f7583b Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 22 Oct 2025 15:58:54 +0100 Subject: [PATCH 15/24] fix-cicd --- .github/workflows/stage-4-acceptance.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index 82a5b9d1..2d852b1a 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -52,6 +52,10 @@ jobs: - name: "Deploy application" run: | echo "Deploying application..." + - name: "NPM install" + working-directory: . + run: npm ci + shell: bash - name: "Build OAS file" working-directory: . run: | From ed3de2e7ab5cfe7a97730c07a57b66c179029111 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Wed, 22 Oct 2025 17:18:22 +0100 Subject: [PATCH 16/24] fix --- tests/constants/request_headers.ts | 7 ++++--- tests/helpers/validateJsonSchema.ts | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index a3f703e8..43148e31 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -1,4 +1,5 @@ import { randomUUID } from 'node:crypto'; +import { supplierId } from './api_constants'; export const sandBoxHeader: RequestSandBoxHeaders = { 'X-Request-ID': randomUUID(), @@ -24,7 +25,7 @@ export async function createInvalidRequestHeaders(): Promise { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: '', - 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '1234', }; return requestHeaders; @@ -34,7 +35,7 @@ export async function createHeaderWithNoCorrelationId(): Promise let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '', }; return requestHeaders; @@ -44,7 +45,7 @@ export async function createValidRequestHeaders(): Promise { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': '70735ec9-3ba5-4fb0-bb01-b56d2df24bc', + 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '12345', }; return requestHeaders; diff --git a/tests/helpers/validateJsonSchema.ts b/tests/helpers/validateJsonSchema.ts index fab9718d..e8e70cce 100644 --- a/tests/helpers/validateJsonSchema.ts +++ b/tests/helpers/validateJsonSchema.ts @@ -1,7 +1,10 @@ import OpenAPIResponseValidator from "openapi-response-validator"; -import openapiDoc from "../../build/notify-supplier.json" assert { type: "json" }; -type ValidationResult = ReturnType; +import path from 'path'; + +const paths = path.resolve(__dirname, '../../build/notify-supplier.json'); +const openapiDoc = require(paths); +type ValidationResult = ReturnType; /** * Validate a response against the OpenAPI spec for a given endpoint and method. * From 8359886ca9a8e4680cac6c7da4b4c71c637d1e1c Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 09:54:28 +0100 Subject: [PATCH 17/24] to discuss schema --- tests/component-tests/apiGateway-tests/getLetters.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 08a2db73..7055771e 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -26,12 +26,13 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(200); const responseBody = await response.json(); - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + /* const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); if (validationResult) { console.error("API response validation failed:", validationResult); } expect(validationResult).toBeUndefined(); + TBD */ }); test('GET /letters with invalid apikey should return 403', async ({ request }) => { @@ -59,12 +60,13 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(500); const responseBody = await response.json(); - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + /* const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); if (validationResult) { console.error("API response validation failed:", validationResult); } expect(validationResult).toBeUndefined(); + TBD */ }); test('GET /letters with invalid query param return 400', async ({ request }) => { From 765cb829acdffbf9dea5c8cabc8b0a91e794820b Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 10:15:23 +0100 Subject: [PATCH 18/24] to discuss schema --- tests/component-tests/apiGateway-tests/getLetters.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 7055771e..6128acc5 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -80,12 +80,14 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(400); const responseBody = await response.json(); + /* const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); if (validationResult) { console.error("API response validation failed:", validationResult); } expect(validationResult).toBeUndefined(); + */ }); }); From bf015b70fc1cf384103cab10412381276ad289e6 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 12:19:03 +0100 Subject: [PATCH 19/24] Remove test from pipeline --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 91e8d688..e8e1541c 100644 --- a/Makefile +++ b/Makefile @@ -98,8 +98,8 @@ config:: _install-dependencies version # Configure development environment (main npm install (cd docs && make install && cd ..) -test-component: - (cd tests && npm install && npm run test:component) +#test-component: To Be discussed with Infra on AWS credentials @Pipeline +# (cd tests && npm install && npm run test:component) version: rm -f .version From 5d8a6c34e75f9be90f48463258107f47e16ba78e Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 12:34:36 +0100 Subject: [PATCH 20/24] Remove test from pipeline --- .github/workflows/stage-4-acceptance.yaml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index 2d852b1a..ce72fb8d 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -52,15 +52,6 @@ jobs: - name: "Deploy application" run: | echo "Deploying application..." - - name: "NPM install" - working-directory: . - run: npm ci - shell: bash - - name: "Build OAS file" - working-directory: . - run: | - make publish-oas - shell: bash test-contract: name: "Contract test" runs-on: ubuntu-latest @@ -117,7 +108,7 @@ jobs: - name: "Save result" run: | echo "Nothing to save" - test-component: + test-integration: name: "Integration test" runs-on: ubuntu-latest needs: environment-set-up @@ -125,12 +116,9 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v4 - - name: "Set API Suffix" - run: | - echo "API_SUFFIX=${{ inputs.pr_number }}" >> $GITHUB_ENV - name: "Run integration test" run: | - make test-component + make test-integration - name: "Save result" run: | echo "Nothing to save" From f29c91dcfb16a53ebe5f3c41ddbfbdb32b4bedf8 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 12:56:37 +0100 Subject: [PATCH 21/24] amend workflow --- .github/workflows/stage-4-acceptance.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index ce72fb8d..59ec8012 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -157,7 +157,7 @@ jobs: [ test-accessibility, test-contract, - test-component, + test-integration, test-load, test-security, test-ui-performance, From 20af9075fa8395fd8451fff3d116e667682962f9 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Thu, 23 Oct 2025 16:21:43 +0100 Subject: [PATCH 22/24] Review Changes --- Makefile | 4 +-- .../apiGateway-tests/getLetters.spec.ts | 27 +---------------- .../testCases/UpdateLetterStatus.ts | 30 +++++++++++++++---- .../updateLetterStatus.spec.ts | 5 ++-- tests/config/sandbox.config.ts | 2 -- tests/constants/request_headers.ts | 3 ++ ...s.ts => updateMultipleStatus_testCases.ts} | 3 +- .../updateMultipleLetterStatus.spec.ts | 3 +- 8 files changed, 34 insertions(+), 43 deletions(-) rename tests/sandbox/testCases/{updateMultipleStatys_testCases.ts => updateMultipleStatus_testCases.ts} (95%) diff --git a/Makefile b/Makefile index e8e1541c..91e8d688 100644 --- a/Makefile +++ b/Makefile @@ -98,8 +98,8 @@ config:: _install-dependencies version # Configure development environment (main npm install (cd docs && make install && cd ..) -#test-component: To Be discussed with Infra on AWS credentials @Pipeline -# (cd tests && npm install && npm run test:component) +test-component: + (cd tests && npm install && npm run test:component) version: rm -f .version diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 6128acc5..95127433 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -25,17 +25,9 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => expect(response.status()).toBe(200); const responseBody = await response.json(); - - /* const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } - - expect(validationResult).toBeUndefined(); - TBD */ }); - test('GET /letters with invalid apikey should return 403', async ({ request }) => { + test('GET /letters with invalid authentication should return 403', async ({ request }) => { const header = await createInvalidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, @@ -59,14 +51,6 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => ); expect(response.status()).toBe(500); const responseBody = await response.json(); - - /* const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } - - expect(validationResult).toBeUndefined(); - TBD */ }); test('GET /letters with invalid query param return 400', async ({ request }) => { @@ -79,15 +63,6 @@ test.describe('API Gateway Tests To Get List Of Pending ', () => }); expect(response.status()).toBe(400); const responseBody = await response.json(); - - /* - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } - - expect(validationResult).toBeUndefined(); - */ }); }); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index 56bdfd96..60ec10f1 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -7,13 +7,31 @@ export type PatchMessageRequestBody = { type: string; id: string; attributes: { - reasonCode?: number; + reasonCode?: string | number; reasonText?: string; status: string; }; }; }; +export type ErrorLink = { + about: string; +}; + +type PatchErrorResponse = { + id: string; + code: string; + links: ErrorLink; + status: string; + title: string; + detail: string; +}; + +export type PatchErrorMessageBody = { + errors: PatchErrorResponse[]; +}; + + export type PatchMessageResponseBody = { data: { type: string; @@ -34,6 +52,7 @@ export async function patchRequestHeaders(): Promise { headerauth1: process.env.HEADERAUTH || '', 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '12344', + 'X-Request-ID': 'requestId1' }; return requestHeaders; }; @@ -60,11 +79,10 @@ export async function patchFailureRequestBody (id: string, status: string) : Pro requestBody = { data: { -  attributes: { -   status: status, - reasonCode: 123, - reasonText: 'Test Reason' - + attributes: { + status: status, + reasonCode: 123, + reasonText: 'Test Reason' }, type: 'Letter', id: id diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts index 2f318be6..2fdbccee 100644 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -37,8 +37,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { data:{ attributes: { status: 'ACCEPTED', - specificationId: 'TestSpecificationID', - groupId: 'TestGroupID', + specificationId: letter.specificationId, + groupId: letter.groupId, }, id: letter.id, type: 'Letter' @@ -112,7 +112,6 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { }); const res = await response.json(); - console.log(res); expect(response.status()).toBe(403); }); }); diff --git a/tests/config/sandbox.config.ts b/tests/config/sandbox.config.ts index 1990cb93..c7295100 100644 --- a/tests/config/sandbox.config.ts +++ b/tests/config/sandbox.config.ts @@ -7,8 +7,6 @@ const localConfig: PlaywrightTestConfig = { /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: getReporters('api-test'), ...baseConfig, - //globalSetup: require.resolve('./setup/globalSetup'), - //globalTeardown: require.resolve('./setup/globalTeardown'), projects: [ { name: 'sandbox', diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index 43148e31..9d9b90a9 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -27,6 +27,7 @@ export async function createInvalidRequestHeaders(): Promise { headerauth1: '', 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '1234', + 'X-Request-ID': 'requestId1' }; return requestHeaders; } @@ -37,6 +38,7 @@ export async function createHeaderWithNoCorrelationId(): Promise headerauth1: process.env.HEADERAUTH || '', 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '', + 'X-Request-ID': 'requestId1' }; return requestHeaders; } @@ -47,6 +49,7 @@ export async function createValidRequestHeaders(): Promise { headerauth1: process.env.HEADERAUTH || '', 'NHSD-Supplier-ID': supplierId, 'NHSD-Correlation-ID': '12345', + 'X-Request-ID': 'requestId1' }; return requestHeaders; } diff --git a/tests/sandbox/testCases/updateMultipleStatys_testCases.ts b/tests/sandbox/testCases/updateMultipleStatus_testCases.ts similarity index 95% rename from tests/sandbox/testCases/updateMultipleStatys_testCases.ts rename to tests/sandbox/testCases/updateMultipleStatus_testCases.ts index 69e31b54..84d89a6f 100644 --- a/tests/sandbox/testCases/updateMultipleStatys_testCases.ts +++ b/tests/sandbox/testCases/updateMultipleStatus_testCases.ts @@ -1,4 +1,3 @@ -import { PatchMessageRequestBody } from "../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus"; import { RequestSandBoxHeaders, sandBoxHeader } from "../../constants/request_headers"; export type ApiSandboxUpdateLetterStatusTestData = { @@ -16,7 +15,7 @@ type postRequest = { type: string; id: string; attributes: { - reasonCode?: number; + reasonCode?: string | number; reasonText?: string; status: string; } diff --git a/tests/sandbox/updateMultipleLetterStatus.spec.ts b/tests/sandbox/updateMultipleLetterStatus.spec.ts index 64f6bb2b..524229bf 100644 --- a/tests/sandbox/updateMultipleLetterStatus.spec.ts +++ b/tests/sandbox/updateMultipleLetterStatus.spec.ts @@ -1,7 +1,6 @@ import { test, expect, request } from '@playwright/test'; import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; -import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; -import { apiSandboxMultipleLetterStatusTestData } from './testCases/updateMultipleStatys_testCases'; +import { apiSandboxMultipleLetterStatusTestData } from './testCases/updateMultipleStatus_testCases'; test.describe('Sandbox Tests To Update Multiple Letter Status', () => From 4d5d4e703c06d6f31b1a984e8acfa21cbffc0282 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Mon, 27 Oct 2025 09:26:52 +0000 Subject: [PATCH 23/24] review fix --- .../apiGateway-tests/getLetters.spec.ts | 2 +- .../testCases/UpdateLetterStatus.ts | 24 +++---------------- .../updateLetterStatus.spec.ts | 18 +++++++------- tests/helpers/commonTypes.ts | 16 +++++++++++++ .../testCases/updateLetterStatus_testCases.ts | 6 +++-- 5 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 tests/helpers/commonTypes.ts diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 95127433..543b6ee4 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -11,7 +11,7 @@ test.beforeAll(async () => { baseUrl = await getRestApiGatewayBaseUrl(); }); -test.describe('API Gateway Tests To Get List Of Pending ', () => +test.describe('API Gateway Tests To Get List Of Pending Letters', () => { test('GET /letters should return 200 and list items', async ({ request }) => { diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index 60ec10f1..5502d54b 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -14,24 +14,6 @@ export type PatchMessageRequestBody = { }; }; -export type ErrorLink = { - about: string; -}; - -type PatchErrorResponse = { - id: string; - code: string; - links: ErrorLink; - status: string; - title: string; - detail: string; -}; - -export type PatchErrorMessageBody = { - errors: PatchErrorResponse[]; -}; - - export type PatchMessageResponseBody = { data: { type: string; @@ -46,7 +28,7 @@ export type PatchMessageResponseBody = { }; }; -export async function patchRequestHeaders(): Promise { +export function patchRequestHeaders(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', @@ -58,7 +40,7 @@ export async function patchRequestHeaders(): Promise { }; -export async function patchValidRequestBody (id: string, status: string) : Promise{ +export function patchValidRequestBody (id: string, status: string) : PatchMessageRequestBody{ let requestBody: PatchMessageRequestBody; requestBody = { @@ -74,7 +56,7 @@ export async function patchValidRequestBody (id: string, status: string) : Promi return requestBody; } -export async function patchFailureRequestBody (id: string, status: string) : Promise{ +export function patchFailureRequestBody (id: string, status: string) : PatchMessageRequestBody{ let requestBody: PatchMessageRequestBody; requestBody = { diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts index 2fdbccee..ad9c9562 100644 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -23,8 +23,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { return; } const letter = letters[0]; - const headers = await patchRequestHeaders(); - const body = await patchValidRequestBody(letter.id, 'ACCEPTED'); + const headers = patchRequestHeaders(); + const body = patchValidRequestBody(letter.id, 'ACCEPTED'); const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { headers: headers, @@ -58,8 +58,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { return; } const letter = letters[0]; - const headers = await patchRequestHeaders(); - const body = await patchFailureRequestBody(letter.id, 'REJECTED'); + const headers = patchRequestHeaders(); + const body = patchFailureRequestBody(letter.id, 'REJECTED'); const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { headers: headers, @@ -74,8 +74,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { test(`Patch /letters returns 400 if request Body is invalid`, async ({ request }) => { const id = randomUUID() - const headers = await patchRequestHeaders(); - const body = await patchValidRequestBody(id, ''); + const headers = patchRequestHeaders(); + const body = patchValidRequestBody(id, ''); const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { headers: headers, @@ -88,9 +88,9 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { }); test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => { - const headers = await patchRequestHeaders(); + const headers = patchRequestHeaders(); const id = randomUUID() - const body = await patchValidRequestBody(id, 'PENDING'); + const body = patchValidRequestBody(id, 'PENDING'); const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { headers: headers, @@ -104,7 +104,7 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => { const headers = await createInvalidRequestHeaders(); const id = randomUUID() - const body = await patchValidRequestBody(id, 'PENDING'); + const body = patchValidRequestBody(id, 'PENDING'); const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { headers: headers, diff --git a/tests/helpers/commonTypes.ts b/tests/helpers/commonTypes.ts new file mode 100644 index 00000000..465ea0ff --- /dev/null +++ b/tests/helpers/commonTypes.ts @@ -0,0 +1,16 @@ +export type ErrorLink = { + about: string; +}; + +type ErrorResponse = { + id: string; + code: string; + links: ErrorLink; + status: string; + title: string; + detail: string; +}; + +export type ErrorMessageBody = { + errors: ErrorResponse[]; +}; diff --git a/tests/sandbox/testCases/updateLetterStatus_testCases.ts b/tests/sandbox/testCases/updateLetterStatus_testCases.ts index 491c79d5..35765676 100644 --- a/tests/sandbox/testCases/updateLetterStatus_testCases.ts +++ b/tests/sandbox/testCases/updateLetterStatus_testCases.ts @@ -1,15 +1,17 @@ -import { PatchErrorMessageBody, PatchMessageRequestBody, PatchMessageResponseBody } from '../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus'; +import { PatchMessageRequestBody, PatchMessageResponseBody } from '../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus'; import { RequestSandBoxHeaders, sandBoxHeader } from '../../constants/request_headers'; +import { ErrorMessageBody } from '../../helpers/commonTypes'; import { SandboxErrorResponse } from './getListOfLetters_testCases'; + export type ApiSandboxUpdateLetterStatusTestData = { testCase: string; id: string, header: RequestSandBoxHeaders; body?: PatchMessageRequestBody; expectedStatus: number; - expectedResponse?: PatchMessageResponseBody | SandboxErrorResponse | PatchErrorMessageBody; + expectedResponse?: PatchMessageResponseBody | SandboxErrorResponse | ErrorMessageBody; }; export const apiSandboxUpdateLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = [ From 77a6544dcbf18071dcb8b1fbac15b883f1faafa4 Mon Sep 17 00:00:00 2001 From: "namitha.prabhu" Date: Mon, 27 Oct 2025 12:01:59 +0000 Subject: [PATCH 24/24] review fixes --- .../apiGateway-tests/getLetters.spec.ts | 44 +++++++++++++++---- .../testCases/UpdateLetterStatus.ts | 39 ++++++++++++++++ .../updateLetterStatus.spec.ts | 18 +++++++- tests/constants/api_constants.ts | 1 - tests/constants/request_headers.ts | 6 +-- tests/helpers/awsGatewayHelper.ts | 3 +- .../updateMultipleStatus_testCases.ts | 2 +- 7 files changed, 95 insertions(+), 18 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts index 543b6ee4..08b30252 100644 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ b/tests/component-tests/apiGateway-tests/getLetters.spec.ts @@ -1,13 +1,13 @@ import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS, AWS_REGION } from '../../constants/api_constants'; +import { SUPPLIER_LETTERS } from '../../constants/api_constants'; import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; -import { validateApiResponse } from '../../helpers/validateJsonSchema'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; +import { validateApiResponse } from '../../helpers/validateJsonSchema'; +import { link } from 'fs'; let baseUrl: string; test.beforeAll(async () => { - const region = AWS_REGION; baseUrl = await getRestApiGatewayBaseUrl(); }); @@ -15,7 +15,7 @@ test.describe('API Gateway Tests To Get List Of Pending Letters', () => { test('GET /letters should return 200 and list items', async ({ request }) => { - const header = await createValidRequestHeaders(); + const header = createValidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, params: { @@ -25,10 +25,17 @@ test.describe('API Gateway Tests To Get List Of Pending Letters', () => expect(response.status()).toBe(200); const responseBody = await response.json(); + expect(responseBody.data.length.toString()).toEqual('2'); + + const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); + if (validationResult) { + console.error("API response validation failed:", validationResult); + } + expect(validationResult).toBeUndefined(); }); test('GET /letters with invalid authentication should return 403', async ({ request }) => { - const header = await createInvalidRequestHeaders(); + const header = createInvalidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, params:{ @@ -37,11 +44,14 @@ test.describe('API Gateway Tests To Get List Of Pending Letters', () => }, ); expect(response.status()).toBe(403); + const responseBody = await response.json(); + expect(responseBody).toMatchObject({ + Message : 'User is not authorized to access this resource with an explicit deny in an identity-based policy' } + ); }); - test('GET /letters with empty correlationId should return 500', async ({ request }) => { - const header = await createHeaderWithNoCorrelationId(); + const header = createHeaderWithNoCorrelationId(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, params:{ @@ -51,10 +61,13 @@ test.describe('API Gateway Tests To Get List Of Pending Letters', () => ); expect(response.status()).toBe(500); const responseBody = await response.json(); + expect(responseBody.errors[0].code).toBe('NOTIFY_INTERNAL_SERVER_ERROR'); + expect(responseBody.errors[0].detail).toBe("The request headers don't contain the APIM correlation id"); + }); test('GET /letters with invalid query param return 400', async ({ request }) => { - const header = await createValidRequestHeaders(); + const header = createValidRequestHeaders(); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ headers: header, params:{ @@ -63,6 +76,19 @@ test.describe('API Gateway Tests To Get List Of Pending Letters', () => }); expect(response.status()).toBe(400); const responseBody = await response.json(); + expect(responseBody).toMatchObject({ + 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 limit parameter is not a number' + } + ] + }); }); - }); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts index 5502d54b..f747f03e 100644 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts @@ -1,6 +1,7 @@ import { RequestHeaders } from '../../../constants/request_headers'; import { supplierId } from '../../../constants/api_constants'; +import { ErrorMessageBody } from '../../../helpers/commonTypes'; export type PatchMessageRequestBody = { data: { @@ -73,3 +74,41 @@ export function patchFailureRequestBody (id: string, status: string) : PatchMess }; return requestBody; } + +export function patch400ErrorResponseBody () : ErrorMessageBody{ + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id : '12344', + 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 patch500ErrorResponseBody (id: string) : ErrorMessageBody{ + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: "12344", + 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; +} diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts index ad9c9562..c7211ca9 100644 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/test'; import { SUPPLIER_LETTERS, supplierId } from '../../constants/api_constants'; import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { 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'; @@ -68,6 +68,18 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { const res = await response.json(); expect(response.status()).toBe(200); + expect(res).toMatchObject({ + data:{ + attributes: { + status: 'REJECTED', + specificationId: letter.specificationId, + groupId: letter.groupId, + }, + id: letter.id, + type: 'Letter' + } + }); + await deleteLettersBySupplier(letter.id); }); @@ -85,7 +97,8 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { const res = await response.json(); expect(response.status()).toBe(400); - }); + expect(res).toMatchObject(patch400ErrorResponseBody()); + }); test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => { const headers = patchRequestHeaders(); @@ -99,6 +112,7 @@ test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { const res = await response.json(); expect(response.status()).toBe(500); + expect(res).toMatchObject(patch500ErrorResponseBody(id)); }); test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => { diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts index e32fe74c..fc7f3487 100644 --- a/tests/constants/api_constants.ts +++ b/tests/constants/api_constants.ts @@ -1,5 +1,4 @@ export const SUPPLIER_LETTERS = 'letters'; -export const SUPPLIER_API_URL = 'https://internal-dev.api.service.nhs.uk/nhs-notify-supplier/'; export const SUPPLIER_API_URL_SANDBOX = 'https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier'; export const AWS_REGION = 'eu-west-2'; diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts index 9d9b90a9..02e2d33b 100644 --- a/tests/constants/request_headers.ts +++ b/tests/constants/request_headers.ts @@ -21,7 +21,7 @@ export interface RequestSandBoxHeaders { [key: string]: string; } -export async function createInvalidRequestHeaders(): Promise { +export function createInvalidRequestHeaders(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: '', @@ -32,7 +32,7 @@ export async function createInvalidRequestHeaders(): Promise { return requestHeaders; } -export async function createHeaderWithNoCorrelationId(): Promise { +export function createHeaderWithNoCorrelationId(): RequestHeaders { let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', @@ -43,7 +43,7 @@ export async function createHeaderWithNoCorrelationId(): Promise return requestHeaders; } -export async function createValidRequestHeaders(): Promise { +export function createValidRequestHeaders(): RequestHeaders{ let requestHeaders: RequestHeaders; requestHeaders = { headerauth1: process.env.HEADERAUTH || '', diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/awsGatewayHelper.ts index 951e3e9e..9e0db8bb 100644 --- a/tests/helpers/awsGatewayHelper.ts +++ b/tests/helpers/awsGatewayHelper.ts @@ -11,6 +11,5 @@ export async function getRestApiGatewayBaseUrl(): Promise { if (!api?.id) throw new Error(`API with name "${API_NAME}" not found.`); - const url = `https://${api.id}.execute-api.${region}.amazonaws.com/main`; - return url; + return `https://${api.id}.execute-api.${region}.amazonaws.com/main`; } diff --git a/tests/sandbox/testCases/updateMultipleStatus_testCases.ts b/tests/sandbox/testCases/updateMultipleStatus_testCases.ts index 84d89a6f..f94a17e7 100644 --- a/tests/sandbox/testCases/updateMultipleStatus_testCases.ts +++ b/tests/sandbox/testCases/updateMultipleStatus_testCases.ts @@ -109,7 +109,7 @@ export const apiSandboxMultipleLetterStatusTestData: ApiSandboxUpdateLetterStatu expectedStatus: 200 }, { - testCase: '400 response if invalid request is passed', + testCase: '404 response if invalid request is passed', header: sandBoxHeader, body:{ data :