From aac06e1c3e03471dd30f75a06142c6e8e4acd3e3 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 5 May 2026 15:28:59 +0000 Subject: [PATCH 1/9] test pr proxy e2e tests --- .github/workflows/deploy-dynamic-env-proxy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-dynamic-env-proxy.yaml b/.github/workflows/deploy-dynamic-env-proxy.yaml index 5c4607ae0..7a4313ad1 100644 --- a/.github/workflows/deploy-dynamic-env-proxy.yaml +++ b/.github/workflows/deploy-dynamic-env-proxy.yaml @@ -1,3 +1,4 @@ +# Test name: Deploy dynamic PR environment proxy run-name: Deploy proxy for PR environment on internal-dev by @${{ github.actor }} From 9882e8dac612a55d747f9e48021416f1b9d8944b Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 6 May 2026 17:05:35 +0000 Subject: [PATCH 2/9] point to internal branch workflow --- .github/workflows/stage-4-acceptance.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index bc49ebbf0..cef7bbfac 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -81,4 +81,5 @@ jobs: --targetEnvironment "$ENVIRONMENT" \ --targetAccountGroup "nhs-notify-supplier-api-dev" \ --targetComponent "api" \ - --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' + --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' \ + --internalRef "feature/CCM-17012" # TO BE REMOVED - used to trigger workflow until internal branch merges From b26685522ba5eee2e492eed9b0186ba1fb1d97fa Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 6 May 2026 17:52:54 +0000 Subject: [PATCH 3/9] actually point to internal branch workflow for build proxies and run e2e on pr too --- .github/actions/acceptance-tests/action.yml | 4 ++-- .github/actions/build-proxies/action.yml | 3 ++- .github/workflows/stage-4-acceptance.yaml | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/acceptance-tests/action.yml b/.github/actions/acceptance-tests/action.yml index dcf3ddb29..0ac177dab 100644 --- a/.github/actions/acceptance-tests/action.yml +++ b/.github/actions/acceptance-tests/action.yml @@ -25,7 +25,7 @@ runs: steps: - name: Run component tests - if: ${{ inputs.testType != 'e2e' }} + if: ${{ inputs.testType == 'component' }} uses: ./.github/actions/acceptance-tests-component with: testType: ${{ inputs.testType }} @@ -33,7 +33,7 @@ runs: targetComponent: ${{ inputs.targetComponent }} - name: Run e2e tests - if: ${{ inputs.testType == 'e2e' && inputs.targetEnvironment == 'main' }} + if: ${{ inputs.testType == 'e2e' }} uses: ./.github/actions/acceptance-tests-e2e with: targetEnvironment: ${{ inputs.targetEnvironment }} diff --git a/.github/actions/build-proxies/action.yml b/.github/actions/build-proxies/action.yml index 728edf4bc..23c5005c3 100644 --- a/.github/actions/build-proxies/action.yml +++ b/.github/actions/build-proxies/action.yml @@ -118,4 +118,5 @@ runs: --apimEnvironment "${{ env.APIM_ENV }}" \ --boundedContext "notify-supplier" \ --targetDomain "$TARGET_DOMAIN" \ - --version "${{ inputs.version }}" + --version "${{ inputs.version }}" \ + --internalRef "feature/CCM-17012" # TO BE REMOVED - used to trigger workflow until internal branch merges diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index cef7bbfac..bc49ebbf0 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -81,5 +81,4 @@ jobs: --targetEnvironment "$ENVIRONMENT" \ --targetAccountGroup "nhs-notify-supplier-api-dev" \ --targetComponent "api" \ - --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' \ - --internalRef "feature/CCM-17012" # TO BE REMOVED - used to trigger workflow until internal branch merges + --extraSecretNames '["/dev/e2e/keys/apim/main","/dev/e2e/keys/apim/pr","/dev/e2e/keys/apim/status","/dev/e2e/keys/private"]' From 90d944dce27d45baeff0d92f028c1dade3841ae3 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Thu, 7 May 2026 15:19:08 +0000 Subject: [PATCH 4/9] try to fix tests --- .../integration-tests/urgent-letter-priority.spec.ts | 10 +--------- tests/e2e-tests/api/data/test_get_letter_data.py | 3 ++- tests/e2e-tests/api/letters/test_get_letter_status.py | 2 +- tests/e2e-tests/lib/letters.py | 1 + 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/component-tests/integration-tests/urgent-letter-priority.spec.ts b/tests/component-tests/integration-tests/urgent-letter-priority.spec.ts index 2a2f2858c..f4cb2c65c 100644 --- a/tests/component-tests/integration-tests/urgent-letter-priority.spec.ts +++ b/tests/component-tests/integration-tests/urgent-letter-priority.spec.ts @@ -1,7 +1,6 @@ import { expect, test } from "@playwright/test"; import getRestApiGatewayBaseUrl from "tests/helpers/aws-gateway-helper"; import { pollForLetterStatus } from "tests/helpers/poll-for-letters-helper"; -import { getLettersFromQueueViaIndex } from "tests/helpers/generate-fetch-test-data"; import { getVariantsWithUrgency, sendEventsForVariants, @@ -44,12 +43,6 @@ test.describe("Urgent Letter Priority Tests", () => { await verifyAllocationLogsContainPriority(urgencyNineLetterIds, 9); await verifyAllocationLogsContainPriority(urgencyTenLetterIds, 10); - const lettersFromQueue = await getLettersFromQueueViaIndex(supplier); - - const letterIdsFromQueue = lettersFromQueue.map( - (letter) => letter.letterId, - ); - const header = createValidRequestHeaders(supplier); const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}`, { headers: header, @@ -63,10 +56,9 @@ test.describe("Urgent Letter Priority Tests", () => { GetLettersResponseSchema.parse(responseBody); const letterIds = getLettersResponse.data.map((letter) => letter.id); - expect(letterIds).toEqual(letterIdsFromQueue); verifyIndexPositionOfLetterVariants( - letterIdsFromQueue, + letterIds, urgencyTenLetterIds, urgencyNineLetterIds, ); diff --git a/tests/e2e-tests/api/data/test_get_letter_data.py b/tests/e2e-tests/api/data/test_get_letter_data.py index 42bfc0bef..080e4e07e 100644 --- a/tests/e2e-tests/api/data/test_get_letter_data.py +++ b/tests/e2e-tests/api/data/test_get_letter_data.py @@ -11,10 +11,11 @@ @pytest.mark.devtest @pytest.mark.inttest @pytest.mark.prodtest -def test_200_get_letter_status(url, authentication_secret): +def test_200_get_letter_data(url, authentication_secret): headers = Generators.generate_valid_headers(authentication_secret) ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) + print(f"calling GET {url}/{LETTERS_ENDPOINT}/{ids[0]}/data with headers {headers}") get_letter_data = requests.get(f"{url}/{LETTERS_ENDPOINT}/{ids[0]}/data", headers=headers) ErrorHandler.handle_retry(get_letter_data) diff --git a/tests/e2e-tests/api/letters/test_get_letter_status.py b/tests/e2e-tests/api/letters/test_get_letter_status.py index c55af8820..add29d0b3 100644 --- a/tests/e2e-tests/api/letters/test_get_letter_status.py +++ b/tests/e2e-tests/api/letters/test_get_letter_status.py @@ -16,12 +16,12 @@ def test_200_get_letter_status(url, authentication_secret): ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) letter_id = ids[0] + print(f"calling GET {url}/{LETTERS_ENDPOINT}/{letter_id} with headers {headers}") get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}", headers=headers) ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" - @pytest.mark.test @pytest.mark.devtest @pytest.mark.inttest diff --git a/tests/e2e-tests/lib/letters.py b/tests/e2e-tests/lib/letters.py index cabd8f458..574281fbf 100644 --- a/tests/e2e-tests/lib/letters.py +++ b/tests/e2e-tests/lib/letters.py @@ -75,6 +75,7 @@ def get_pending_letter_ids( response.raise_for_status() data.extend(response.json().get("data", [])) if len(data) >= limit: + print(f"Created and found letters with IDs {[item.get('id') for item in data]}") return [item.get("id") for item in data] time.sleep(interval_s) From 7d4c6733c631fe00c9c0002644c45fe698ffad33 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Fri, 8 May 2026 15:20:11 +0000 Subject: [PATCH 5/9] Fix tests --- Makefile | 9 ++++++--- tests/constants/api-constants.ts | 2 +- .../api/data/test_get_letter_data.py | 20 +++++-------------- .../api/headers/test_x_request_id.py | 4 ++-- .../api/letters/test_get_letter_status.py | 8 +++++--- .../api/letters/test_get_list_of_letters.py | 1 + .../letters/test_multiple_letter_status.py | 9 ++++++--- .../api/letters/test_update_letter_status.py | 16 +++++++++++---- tests/e2e-tests/lib/letters.py | 9 +++++---- 9 files changed, 43 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index c7915eb31..a0368e2f0 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ # the project as automated steps to be executed on locally and in the CD pipeline. include scripts/init.mk +-include .env # Load environment variables from .env file if it exists # ============================================================================== @@ -130,11 +131,14 @@ ${VERBOSE}.SILENT: \ # E2E Test commands # ##################### +# https://pytest-xdist.readthedocs.io/en/stable/known-limitations.html#output-stdout-and-stderr-from-workers means pytest won't print to stdout even with -s +PYTEST_WORKERS := 4 # set to 0 to see stdout/stderr when debugging e2e tests + TEST_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \ STATUS_ENDPOINT_API_KEY="$(STATUS_ENDPOINT_API_KEY)" \ PYTHONPATH=. poetry run pytest --disable-warnings -vv \ --color=yes \ - -n 4 \ + -n $(PYTEST_WORKERS) \ --api-name=nhs-notify-supplier \ --proxy-name="$(PROXY_NAME)" \ -s \ @@ -145,7 +149,6 @@ TEST_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \ --only-rerun 'AssertionError: Unexpected 502' \ --junitxml=test-report.xml - .internal-dev-test: @cd tests/e2e-tests && \ $(TEST_CMD) \ @@ -161,7 +164,7 @@ TEST_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \ PROD_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \ PYTHONPATH=. poetry run pytest --disable-warnings -vv \ --color=yes \ - -n 4 \ + -n $(PYTEST_WORKERS) \ --api-name=nhs-notify-supplier \ --proxy-name="$(PROXY_NAME)" \ -s \ diff --git a/tests/constants/api-constants.ts b/tests/constants/api-constants.ts index 608fb75d8..0afe02ca3 100644 --- a/tests/constants/api-constants.ts +++ b/tests/constants/api-constants.ts @@ -5,7 +5,7 @@ export const AWS_REGION = "eu-west-2"; export const envName = process.env.TARGET_ENVIRONMENT ?? "main"; export const API_NAME = `nhs-${envName}-supapi`; export const LETTERSTABLENAME = `nhs-${envName}-supapi-letters`; -export const SUPPLIERID = "TestSupplier1"; +export const SUPPLIERID = "supplier1"; export const MI_ENDPOINT = "mi"; export const SUPPLIERTABLENAME = `nhs-${envName}-supapi-suppliers`; export const UPSERT_LETTER_LAMBDA_ARN = `arn:aws:lambda:eu-west-2:820178564574:function:nhs-${envName}-supapi-upsertletter`; diff --git a/tests/e2e-tests/api/data/test_get_letter_data.py b/tests/e2e-tests/api/data/test_get_letter_data.py index 080e4e07e..94b865079 100644 --- a/tests/e2e-tests/api/data/test_get_letter_data.py +++ b/tests/e2e-tests/api/data/test_get_letter_data.py @@ -15,25 +15,13 @@ def test_200_get_letter_data(url, authentication_secret): headers = Generators.generate_valid_headers(authentication_secret) ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) - print(f"calling GET {url}/{LETTERS_ENDPOINT}/{ids[0]}/data with headers {headers}") - get_letter_data = requests.get(f"{url}/{LETTERS_ENDPOINT}/{ids[0]}/data", headers=headers) + print(f"calling GET {url}{LETTERS_ENDPOINT}/{ids[0]}/data with headers {headers}") + get_letter_data = requests.get(f"{url}{LETTERS_ENDPOINT}/{ids[0]}/data", headers=headers) ErrorHandler.handle_retry(get_letter_data) assert get_letter_data.status_code == 200, f"Response: {get_letter_data.status_code}: {get_letter_data.text}" assert get_letter_data.headers.get("Content-Type") == "application/pdf" -@pytest.mark.test -@pytest.mark.devtest -@pytest.mark.inttest -@pytest.mark.prodtest -def test_404_letter_does_not_exist(url, authentication_secret): - headers = Generators.generate_valid_headers(authentication_secret) - get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/xx", headers=headers) - - ErrorHandler.handle_retry(get_message_response) - assert get_message_response.status_code == 404 - assert get_message_response.json().get("errors")[0].get("detail") == "No resource found with that ID" - @pytest.mark.test @pytest.mark.devtest @pytest.mark.inttest @@ -41,7 +29,9 @@ def test_404_letter_does_not_exist(url, authentication_secret): def test_404_letter_does_not_exist(url, authentication_secret): letter_id = uuid.uuid4().hex headers = Generators.generate_valid_headers(authentication_secret) - get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}/data", headers=headers) + + print(f"calling GET {url}{LETTERS_ENDPOINT}/{letter_id}/data with headers {headers}") + get_message_response = requests.get(f"{url}{LETTERS_ENDPOINT}/{letter_id}/data", headers=headers) ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 404 diff --git a/tests/e2e-tests/api/headers/test_x_request_id.py b/tests/e2e-tests/api/headers/test_x_request_id.py index 827b8549d..7adaa7dcb 100644 --- a/tests/e2e-tests/api/headers/test_x_request_id.py +++ b/tests/e2e-tests/api/headers/test_x_request_id.py @@ -20,7 +20,7 @@ def test_header_letters_endpoint( ): auth_header = {"apikey": authentication_secret.value} if authentication_secret.auth_type == "apikey" \ else {"Authorization": authentication_secret.value} - resp = getattr(requests, method)(f"{url}/{endpoints}", headers={ + resp = getattr(requests, method)(f"{url}{endpoints}", headers={ **auth_header, "X-Request-ID": None }) @@ -38,7 +38,7 @@ def test_header_mi_endpoint( ): auth_header = {"apikey": authentication_secret.value} if authentication_secret.auth_type == "apikey" \ else {"Authorization": authentication_secret.value} - resp = getattr(requests, "post")(f"{url}/{MI_ENDPOINT}", headers={ + resp = getattr(requests, "post")(f"{url}{MI_ENDPOINT}", headers={ **auth_header, "X-Request-ID": "" }) diff --git a/tests/e2e-tests/api/letters/test_get_letter_status.py b/tests/e2e-tests/api/letters/test_get_letter_status.py index add29d0b3..9cfa22175 100644 --- a/tests/e2e-tests/api/letters/test_get_letter_status.py +++ b/tests/e2e-tests/api/letters/test_get_letter_status.py @@ -16,8 +16,8 @@ def test_200_get_letter_status(url, authentication_secret): ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) letter_id = ids[0] - print(f"calling GET {url}/{LETTERS_ENDPOINT}/{letter_id} with headers {headers}") - get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}", headers=headers) + print(f"calling GET {url}{LETTERS_ENDPOINT}/{letter_id} with headers {headers}") + get_message_response = requests.get(f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers) ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" @@ -28,7 +28,9 @@ def test_200_get_letter_status(url, authentication_secret): @pytest.mark.prodtest def test_404_letter_does_not_exist(url, authentication_secret): headers = Generators.generate_valid_headers(authentication_secret) - get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/xx", headers=headers) + + print(f"calling GET {url}{LETTERS_ENDPOINT}/xx with headers {headers}") + get_message_response = requests.get(f"{url}{LETTERS_ENDPOINT}/xx", headers=headers) ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 404, f"Response: {get_message_response.status_code}: {get_message_response.text}" diff --git a/tests/e2e-tests/api/letters/test_get_list_of_letters.py b/tests/e2e-tests/api/letters/test_get_list_of_letters.py index 275d255a6..1c67edb91 100644 --- a/tests/e2e-tests/api/letters/test_get_list_of_letters.py +++ b/tests/e2e-tests/api/letters/test_get_list_of_letters.py @@ -12,5 +12,6 @@ def test_200_get_letters(url, authentication_secret): headers = Generators.generate_valid_headers(authentication_secret) ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) + assert ids, "Expected at least one PENDING letter" assert len(ids) == 1 diff --git a/tests/e2e-tests/api/letters/test_multiple_letter_status.py b/tests/e2e-tests/api/letters/test_multiple_letter_status.py index 744fbb8d4..51fe72ddf 100644 --- a/tests/e2e-tests/api/letters/test_multiple_letter_status.py +++ b/tests/e2e-tests/api/letters/test_multiple_letter_status.py @@ -18,8 +18,9 @@ def test_202_with_valid_headers(url, authentication_secret): ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=2) data = Generators.generate_multiple_valid_request(ids) + print(f"calling POST {url}{LETTERS_ENDPOINT} with headers {headers} and body {data}") update_letter_status = requests.post( - f"{url}/{LETTERS_ENDPOINT}", + f"{url}{LETTERS_ENDPOINT}", headers=headers, json=data, ) @@ -37,8 +38,9 @@ def test_400_duplicates_in_request_body(url, authentication_secret): ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=2) data = Generators.generate_duplicate_request(ids) + print(f"calling POST {url}{LETTERS_ENDPOINT} with headers {headers} and body {data}") update_letter_status = requests.post( - f"{url}/{LETTERS_ENDPOINT}", + f"{url}{LETTERS_ENDPOINT}", headers=headers, json=data, ) @@ -57,8 +59,9 @@ def test_400_invalid_status_in_request_body(url, authentication_secret): ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=3) data = Generators.generate_invalid_status_request(ids) + print(f"calling POST {url}{LETTERS_ENDPOINT} with headers {headers} and body {data}") update_letter_status = requests.post( - f"{url}/{LETTERS_ENDPOINT}", + f"{url}{LETTERS_ENDPOINT}", headers=headers, json=data, ) diff --git a/tests/e2e-tests/api/letters/test_update_letter_status.py b/tests/e2e-tests/api/letters/test_update_letter_status.py index 28a61c3ff..98200ec22 100644 --- a/tests/e2e-tests/api/letters/test_update_letter_status.py +++ b/tests/e2e-tests/api/letters/test_update_letter_status.py @@ -19,8 +19,10 @@ def test_202_with_valid_headers(url, authentication_secret): letter_id = ids[0] data = Generators.generate_valid_message_body("ACCEPTED", letter_id) + + print(f"calling PATCH {url}{LETTERS_ENDPOINT}/{letter_id} with headers {headers} and body {data}") update_letter_status = requests.patch( - f"{url}/{LETTERS_ENDPOINT}/{letter_id}", + f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers, json=data, ) @@ -35,8 +37,10 @@ def test_202_with_rejected_status(url, authentication_secret): letter_id = ids[0] data = Generators.generate_valid_message_rejected("REJECTED", letter_id) + + print(f"calling PATCH {url}{LETTERS_ENDPOINT}/{letter_id} with headers {headers} and body {data}") update_letter_status = requests.patch( - f"{url}/{LETTERS_ENDPOINT}/{letter_id}", + f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers, json=data, ) @@ -55,8 +59,10 @@ def test_400_with_invalid_status(url, authentication_secret): letter_id = ids[0] data = Generators.generate_valid_message_body("", letter_id) + + print(f"calling PATCH {url}{LETTERS_ENDPOINT}/{letter_id} with headers {headers} and body {data}") update_letter_status = requests.patch( - f"{url}/{LETTERS_ENDPOINT}/{letter_id}", + f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers, json=data, ) @@ -75,8 +81,10 @@ def test_400_id_mismatch_with_request(url, authentication_secret): letter_id = ids[0] data = Generators.generate_valid_message_body("ACCEPTED", "letter1") + + print(f"calling PATCH {url}{LETTERS_ENDPOINT}/{letter_id} with headers {headers} and body {data}") update_letter_status = requests.patch( - f"{url}/{LETTERS_ENDPOINT}/{letter_id}", + f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers, json=data, ) diff --git a/tests/e2e-tests/lib/letters.py b/tests/e2e-tests/lib/letters.py index 574281fbf..4a1774ecb 100644 --- a/tests/e2e-tests/lib/letters.py +++ b/tests/e2e-tests/lib/letters.py @@ -8,7 +8,7 @@ _REPO_ROOT = pathlib.Path(__file__).resolve().parents[3] _CLI_WORKSPACE = "nhs-notify-supplier-api-letter-test-data-utility" -_SUPPLIER_ID = "TestSupplier1" +_SUPPLIER_ID = "supplier1" # This should be the same id registered in the Apigee App to which the proxy will be associated def create_test_data(count: int = 10) -> None: @@ -68,18 +68,19 @@ def get_pending_letter_ids( deadline = time.monotonic() + timeout_s data = [] while time.monotonic() < deadline: + # Retrieves letters based on the supplier registered in the Apigee App response = requests.get( - f"{url}/{letters_endpoint}?limit={limit}", headers=headers + f"{url}{letters_endpoint}?limit={limit}", headers=headers ) ErrorHandler.handle_retry(response) response.raise_for_status() data.extend(response.json().get("data", [])) if len(data) >= limit: - print(f"Created and found letters with IDs {[item.get('id') for item in data]}") + print(f"Created and found letters with IDs {[item.get('id') for item in data]} for supplier registered in the Apigee App, to which the proxy is associated") return [item.get("id") for item in data] time.sleep(interval_s) raise TimeoutError( f"Timed out after {retries} retries waiting for {limit} PENDING letter(s) at " - f"{url}/{letters_endpoint}" + f"{url}{letters_endpoint}" ) From a213110789d129f5ceb95e44c38c582fc64e5f76 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Mon, 11 May 2026 14:28:48 +0000 Subject: [PATCH 6/9] Fix pact tests --- package-lock.json | 366 ++++++++++-------- package.json | 8 +- tests/package.json | 4 +- ...ter-request-prepared.consumer.pact.test.ts | 2 +- ...ter-request-prepared.provider.pact.test.ts | 2 +- 5 files changed, 209 insertions(+), 173 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d9347d30..f3b0abe38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,12 @@ "tests", "tests/contracts/*" ], + "dependencies": { + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.2", + "@nhsdigital/notify-digital-letters-consumer-contracts": "^1.0.1", + "@pact-foundation/pact": "^16.4.0", + "@pact-foundation/pact-core": "^19.2.0" + }, "devDependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", "@aws-sdk/client-kinesis": "^3.939.0", @@ -259,6 +265,15 @@ "zod": "^4.1.11" } }, + "lambdas/supplier-allocator/node_modules/@nhsdigital/nhs-notify-event-schemas-letter-rendering": { + "version": "2.0.1", + "resolved": "https://npm.pkg.github.com/download/@nhsdigital/nhs-notify-event-schemas-letter-rendering/2.0.1/23a5011fb0addd3da400f798bb1e4340440d62a5", + "integrity": "sha512-U2AWQEBcTDSxA3RX29fdmwjaOPQvwrCQP5rVCgLgtlPGes4Wl695VLw7tDxgpyLY1p9ct3HHrx3Wc6k/QGeW7g==", + "license": "MIT", + "dependencies": { + "zod": "^4.0.17" + } + }, "lambdas/supplier-allocator/node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", @@ -326,6 +341,15 @@ "zod": "^4.1.11" } }, + "lambdas/upsert-letter/node_modules/@nhsdigital/nhs-notify-event-schemas-letter-rendering": { + "version": "2.0.1", + "resolved": "https://npm.pkg.github.com/download/@nhsdigital/nhs-notify-event-schemas-letter-rendering/2.0.1/23a5011fb0addd3da400f798bb1e4340440d62a5", + "integrity": "sha512-U2AWQEBcTDSxA3RX29fdmwjaOPQvwrCQP5rVCgLgtlPGes4Wl695VLw7tDxgpyLY1p9ct3HHrx3Wc6k/QGeW7g==", + "license": "MIT", + "dependencies": { + "zod": "^4.0.17" + } + }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "11.9.3", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", @@ -5131,9 +5155,9 @@ } }, "node_modules/@nhsdigital/nhs-notify-event-schemas-letter-rendering": { - "version": "2.0.1", - "resolved": "https://npm.pkg.github.com/download/@nhsdigital/nhs-notify-event-schemas-letter-rendering/2.0.1/23a5011fb0addd3da400f798bb1e4340440d62a5", - "integrity": "sha512-U2AWQEBcTDSxA3RX29fdmwjaOPQvwrCQP5rVCgLgtlPGes4Wl695VLw7tDxgpyLY1p9ct3HHrx3Wc6k/QGeW7g==", + "version": "2.0.2", + "resolved": "https://npm.pkg.github.com/download/@nhsdigital/nhs-notify-event-schemas-letter-rendering/2.0.2/a72687375c465d68104b52e58ec67ed59bd99447", + "integrity": "sha512-iSXx98XLFUdz7R4ffy+WoMPi2GEZdJOMA3WHgfR5w+VNPd4aN73oNfNMwqXLViNA6M9HC1JPmudZUueqJQ2KxQ==", "license": "MIT", "dependencies": { "zod": "^4.0.17" @@ -5612,12 +5636,12 @@ } }, "node_modules/@pact-foundation/pact": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-16.2.0.tgz", - "integrity": "sha512-PFedoP49sR9EKEvhREsXiDTb+rS3yv016DIlufey6pXC6ssQKhgq78ngZDRGl7mc6PqLJdOBCZUDPUrR890k2A==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-16.4.0.tgz", + "integrity": "sha512-nf+xA6cvWlIpwsJ8gDAw69lGRlkqbiU9Ui6ie+ALH8N6+12Ukj7idYod0Zl643FCsActBVWytr5j28dEG0rEQg==", "license": "MIT", "dependencies": { - "@pact-foundation/pact-core": "^18.1.0", + "@pact-foundation/pact-core": "^19.2.0", "axios": "^1.12.2", "body-parser": "^2.2.0", "chalk": "4.1.2", @@ -5638,9 +5662,9 @@ } }, "node_modules/@pact-foundation/pact-core": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core/-/pact-core-17.1.0.tgz", - "integrity": "sha512-0yAUBpLP9ggibw3uX8FW8gHj6zbxCiGNDh1K9oG9b6opzqD3ZsGD8YaKYOHOLTQSoQRsB//Kkeztvi4IfWO3iQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core/-/pact-core-19.2.0.tgz", + "integrity": "sha512-7EB2e850hmBzGnwOYTRj4TCFCJQa9zaOy53bK+ybzEDx801olf0gVlYqMYHt1EsHUZzmtS5uDybpr9UMcZQxgg==", "cpu": [ "x64", "ia32", @@ -5658,25 +5682,25 @@ "node-gyp-build": "^4.6.0", "pino": "^10.0.0", "pino-pretty": "^13.1.1", - "underscore": "1.13.7" + "underscore": "1.13.8" }, "engines": { "node": ">=20" }, "optionalDependencies": { - "@pact-foundation/pact-core-darwin-arm64": "17.1.0", - "@pact-foundation/pact-core-darwin-x64": "17.1.0", - "@pact-foundation/pact-core-linux-arm64-glibc": "17.1.0", - "@pact-foundation/pact-core-linux-arm64-musl": "17.1.0", - "@pact-foundation/pact-core-linux-x64-glibc": "17.1.0", - "@pact-foundation/pact-core-linux-x64-musl": "17.1.0", - "@pact-foundation/pact-core-windows-x64": "17.1.0" + "@pact-foundation/pact-core-darwin-arm64": "19.2.0", + "@pact-foundation/pact-core-darwin-x64": "19.2.0", + "@pact-foundation/pact-core-linux-arm64-glibc": "19.2.0", + "@pact-foundation/pact-core-linux-arm64-musl": "19.2.0", + "@pact-foundation/pact-core-linux-x64-glibc": "19.2.0", + "@pact-foundation/pact-core-linux-x64-musl": "19.2.0", + "@pact-foundation/pact-core-windows-x64": "19.2.0" } }, "node_modules/@pact-foundation/pact-core-darwin-arm64": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-arm64/-/pact-core-darwin-arm64-17.1.0.tgz", - "integrity": "sha512-S4+VgqpuG2/0V7JRdDA9HvdOh38h45mEGr0m5Dqdh23hOvhRQHF25f3ylBpem6of+LacNIqJ+eyBEm4k/0H8MQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-arm64/-/pact-core-darwin-arm64-19.2.0.tgz", + "integrity": "sha512-cQvvWfJKS7iMzkunzh/kdgmyVLhAxyr/BQ8fOnMco2M/1+GHR+sOXvhrR1tes+NAYd1xjE3fbg1K2Flno8aDBw==", "cpu": [ "arm64" ], @@ -5687,9 +5711,9 @@ ] }, "node_modules/@pact-foundation/pact-core-darwin-x64": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-x64/-/pact-core-darwin-x64-17.1.0.tgz", - "integrity": "sha512-Ex7kykXXq4kyu9NHxvKzwV6yItXZduTF+Ui4dR316xaw7hzTQ+WWnHs0fPFlYFroO/LbWCFBzO5zUUkdU1UknQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-x64/-/pact-core-darwin-x64-19.2.0.tgz", + "integrity": "sha512-UltPXDZ+h1r3JqgIpEBP16bQzB90otd1QbcoeM3XakF9khCrYhW4y9llSuaosyqPFYwCk+mLaZTl/4iitJJaow==", "cpu": [ "x64" ], @@ -5700,9 +5724,9 @@ ] }, "node_modules/@pact-foundation/pact-core-linux-arm64-glibc": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-glibc/-/pact-core-linux-arm64-glibc-17.1.0.tgz", - "integrity": "sha512-bz34LVZz9DNJWUCwIkq71ZBkSSstjr4febDpnOy/JXPvxuDbVZ6OIy8L8vV24c6JJNTxC1E7z194yxz/zuGAKw==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-glibc/-/pact-core-linux-arm64-glibc-19.2.0.tgz", + "integrity": "sha512-amiv4COurH1FRa7DSH3ks4UPQCb5J3x4GKrArf8UsTOkNhVGFUWVl83LOELj9Gtk67GwJ96iF7/fQps8I/iFIA==", "cpu": [ "arm64" ], @@ -5713,9 +5737,9 @@ ] }, "node_modules/@pact-foundation/pact-core-linux-arm64-musl": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-musl/-/pact-core-linux-arm64-musl-17.1.0.tgz", - "integrity": "sha512-NE+1rEMhheBNo8UbUi2bUfjvLwhV9QkY+k/6M+VUvGMVoeNhTXAYBMqz13lWkCcMh4IUbjCaVm6GQrMnV6DV5g==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-musl/-/pact-core-linux-arm64-musl-19.2.0.tgz", + "integrity": "sha512-tGWF7dP72Ho1A1PApyUqUMRI/e+gfbPxfaXxwWMXNh8JLK2jQQnP4bWymGwHQ4vxL7wn8ayx8I5y6x1vk/2+rA==", "cpu": [ "arm64" ], @@ -5726,9 +5750,9 @@ ] }, "node_modules/@pact-foundation/pact-core-linux-x64-glibc": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-glibc/-/pact-core-linux-x64-glibc-17.1.0.tgz", - "integrity": "sha512-FgTeIVV+/2fCZaKEQN7MCXTNuHzBAT0d8TBGwiQXt6AzxNG9WvqqxpJIzH0mzjVmot3Q+8K4pySiSin/n4Y5CA==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-glibc/-/pact-core-linux-x64-glibc-19.2.0.tgz", + "integrity": "sha512-/ZNuWKuFJSBRZH09t7FXqKLy5koCaMIZ4RdbockLYlNo8uWF2ALfRCllj+SbXPLK+T+lVnBrs5s94X2cu4Rc7A==", "cpu": [ "x64" ], @@ -5739,9 +5763,9 @@ ] }, "node_modules/@pact-foundation/pact-core-linux-x64-musl": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-musl/-/pact-core-linux-x64-musl-17.1.0.tgz", - "integrity": "sha512-2wB65MO1QxH6HvXSVjj8Ii6nRrvEh5Y2O5b7vy7xHAPCbXBR67A/mUw9cxTZLdomAZEaOZN/34p+KXAoTA9JHg==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-musl/-/pact-core-linux-x64-musl-19.2.0.tgz", + "integrity": "sha512-n0m39CzVkq8J2alir1redm0rAdUVQzkKJqYBQdRmhEIa+QeGjUrJq+gmakUysoJxfSY4UthnJ9jS8SoyiZ+k6A==", "cpu": [ "x64" ], @@ -5752,136 +5776,9 @@ ] }, "node_modules/@pact-foundation/pact-core-windows-x64": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-windows-x64/-/pact-core-windows-x64-17.1.0.tgz", - "integrity": "sha512-iKpoKzUkcMUcdc5AbwLJDGNTv64DC0hZEh1xlyysIw6dbQkCcmgAx0Sjw8j7nBH/VQNxrCOaaN54fHzYgVmL9g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core/-/pact-core-18.1.0.tgz", - "integrity": "sha512-QWdntTsTT32r3SOTDaKjB9QyEbbgfsshsY2X/OwBJaNq28jKYEw50t2lYrITN+SdhkgfkEZJ9Y0XNfxtOUDVnA==", - "cpu": [ - "x64", - "ia32", - "arm64" - ], - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "check-types": "11.2.3", - "detect-libc": "^2.0.3", - "node-gyp-build": "^4.6.0", - "pino": "^10.0.0", - "pino-pretty": "^13.1.1", - "underscore": "1.13.7" - }, - "engines": { - "node": ">=20" - }, - "optionalDependencies": { - "@pact-foundation/pact-core-darwin-arm64": "18.1.0", - "@pact-foundation/pact-core-darwin-x64": "18.1.0", - "@pact-foundation/pact-core-linux-arm64-glibc": "18.1.0", - "@pact-foundation/pact-core-linux-arm64-musl": "18.1.0", - "@pact-foundation/pact-core-linux-x64-glibc": "18.1.0", - "@pact-foundation/pact-core-linux-x64-musl": "18.1.0", - "@pact-foundation/pact-core-windows-x64": "18.1.0" - } - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-darwin-arm64": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-arm64/-/pact-core-darwin-arm64-18.1.0.tgz", - "integrity": "sha512-j1GSx7zN011xOveWeBP8RJ77bryFSLHIhe1ZcepfhMiB6VoUjGq1BNQ9rbhO4FSF2HBUCddMAw2/Xa+scHQJvQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-darwin-x64": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-x64/-/pact-core-darwin-x64-18.1.0.tgz", - "integrity": "sha512-KV08M7WJm/uuXv2qES4s8oJw8uTgFjQXmHLq4tKGT8HkaYCuSoQAhWYafxBJ/sMHeZ1LoGfv4nJkUr8bNakCYg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-linux-arm64-glibc": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-glibc/-/pact-core-linux-arm64-glibc-18.1.0.tgz", - "integrity": "sha512-BeJqmtBR6IdOg2IU0Y+N1NsH1pzr5H3PeOJ+vTSgNiTnV/wYkKk6GZDT1dzwUVi+OjfarGbK86022EoHk/WpkQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-linux-arm64-musl": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-musl/-/pact-core-linux-arm64-musl-18.1.0.tgz", - "integrity": "sha512-twakwextRNwkAKntYnSBBAs3yugORGDZwgihVm9p+eYqded/agFTu2v4/E7xksLEotGoFlewnAvLSCTvyNf0uw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-linux-x64-glibc": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-glibc/-/pact-core-linux-x64-glibc-18.1.0.tgz", - "integrity": "sha512-rhR5iZS77Ie0ocJmtoTub82lyMQmjjn15UPhD5Tv1i2kYkbLCVPSYZpTrKak/OCDm5/AM0Lb7YqIZlOipMm/mA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-linux-x64-musl": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-musl/-/pact-core-linux-x64-musl-18.1.0.tgz", - "integrity": "sha512-u0N5uU3hwupGCkSmIiehR4yw5Foln+qqkUWXjtKdnttsj8Dz065K8osNGy18jmPRWr5XfRZurnnmw0+tFyaZPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pact-foundation/pact/node_modules/@pact-foundation/pact-core-windows-x64": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-windows-x64/-/pact-core-windows-x64-18.1.0.tgz", - "integrity": "sha512-ST7XNlI78c2MCvWvqv7on9M+DmNGlrihiD3Uk5jwZIAWEXeXEVinpfnM/Z5qFik4m3UCDe/Zu/1aNNiY1RN3dQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-windows-x64/-/pact-core-windows-x64-19.2.0.tgz", + "integrity": "sha512-WZSDBL1Sn8y5+YJTK+HDiS/Fn9th1M4QJW2dflQ8UFwoaIh6yb0TrJjtuWbDGIwNeD4Sv8CJ5jJOf1nyNjFLfg==", "cpu": [ "x64" ], @@ -23278,9 +23175,9 @@ "@aws-sdk/client-lambda": "^3.986.0", "@aws-sdk/client-sns": "^3.984.0", "@aws-sdk/lib-dynamodb": "^3.1008.0", - "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.1", + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.2", "@nhsdigital/notify-digital-letters-consumer-contracts": "^1.0.1", - "@pact-foundation/pact": "^16.0.4", + "@pact-foundation/pact": "^16.4.0", "@pact-foundation/pact-core": "^17.1.0", "@playwright/test": "^1.57.0", "allure-js-commons": "^3.3.3", @@ -23312,6 +23209,133 @@ "@pact-foundation/pact": "^16.0.4" } }, + "tests/node_modules/@pact-foundation/pact-core": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core/-/pact-core-17.1.0.tgz", + "integrity": "sha512-0yAUBpLP9ggibw3uX8FW8gHj6zbxCiGNDh1K9oG9b6opzqD3ZsGD8YaKYOHOLTQSoQRsB//Kkeztvi4IfWO3iQ==", + "cpu": [ + "x64", + "ia32", + "arm64" + ], + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "check-types": "11.2.3", + "detect-libc": "^2.0.3", + "node-gyp-build": "^4.6.0", + "pino": "^10.0.0", + "pino-pretty": "^13.1.1", + "underscore": "1.13.7" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@pact-foundation/pact-core-darwin-arm64": "17.1.0", + "@pact-foundation/pact-core-darwin-x64": "17.1.0", + "@pact-foundation/pact-core-linux-arm64-glibc": "17.1.0", + "@pact-foundation/pact-core-linux-arm64-musl": "17.1.0", + "@pact-foundation/pact-core-linux-x64-glibc": "17.1.0", + "@pact-foundation/pact-core-linux-x64-musl": "17.1.0", + "@pact-foundation/pact-core-windows-x64": "17.1.0" + } + }, + "tests/node_modules/@pact-foundation/pact-core-darwin-arm64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-arm64/-/pact-core-darwin-arm64-17.1.0.tgz", + "integrity": "sha512-S4+VgqpuG2/0V7JRdDA9HvdOh38h45mEGr0m5Dqdh23hOvhRQHF25f3ylBpem6of+LacNIqJ+eyBEm4k/0H8MQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-darwin-x64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-x64/-/pact-core-darwin-x64-17.1.0.tgz", + "integrity": "sha512-Ex7kykXXq4kyu9NHxvKzwV6yItXZduTF+Ui4dR316xaw7hzTQ+WWnHs0fPFlYFroO/LbWCFBzO5zUUkdU1UknQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-linux-arm64-glibc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-glibc/-/pact-core-linux-arm64-glibc-17.1.0.tgz", + "integrity": "sha512-bz34LVZz9DNJWUCwIkq71ZBkSSstjr4febDpnOy/JXPvxuDbVZ6OIy8L8vV24c6JJNTxC1E7z194yxz/zuGAKw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-linux-arm64-musl": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-musl/-/pact-core-linux-arm64-musl-17.1.0.tgz", + "integrity": "sha512-NE+1rEMhheBNo8UbUi2bUfjvLwhV9QkY+k/6M+VUvGMVoeNhTXAYBMqz13lWkCcMh4IUbjCaVm6GQrMnV6DV5g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-linux-x64-glibc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-glibc/-/pact-core-linux-x64-glibc-17.1.0.tgz", + "integrity": "sha512-FgTeIVV+/2fCZaKEQN7MCXTNuHzBAT0d8TBGwiQXt6AzxNG9WvqqxpJIzH0mzjVmot3Q+8K4pySiSin/n4Y5CA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-linux-x64-musl": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-musl/-/pact-core-linux-x64-musl-17.1.0.tgz", + "integrity": "sha512-2wB65MO1QxH6HvXSVjj8Ii6nRrvEh5Y2O5b7vy7xHAPCbXBR67A/mUw9cxTZLdomAZEaOZN/34p+KXAoTA9JHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "tests/node_modules/@pact-foundation/pact-core-windows-x64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-windows-x64/-/pact-core-windows-x64-17.1.0.tgz", + "integrity": "sha512-iKpoKzUkcMUcdc5AbwLJDGNTv64DC0hZEh1xlyysIw6dbQkCcmgAx0Sjw8j7nBH/VQNxrCOaaN54fHzYgVmL9g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "tests/node_modules/dotenv": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", @@ -23323,6 +23347,12 @@ "funding": { "url": "https://dotenvx.com" } + }, + "tests/node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 747493a93..6f1faf765 100644 --- a/package.json +++ b/package.json @@ -135,5 +135,11 @@ "scripts/utilities/*", "tests", "tests/contracts/*" - ] + ], + "dependencies": { + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.2", + "@nhsdigital/notify-digital-letters-consumer-contracts": "^1.0.1", + "@pact-foundation/pact": "^16.4.0", + "@pact-foundation/pact-core": "^19.2.0" + } } diff --git a/tests/package.json b/tests/package.json index c096908da..fba34ec03 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,9 +7,9 @@ "@aws-sdk/client-lambda": "^3.986.0", "@aws-sdk/client-sns": "^3.984.0", "@aws-sdk/lib-dynamodb": "^3.1008.0", - "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.1", + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.2", "@nhsdigital/notify-digital-letters-consumer-contracts": "^1.0.1", - "@pact-foundation/pact": "^16.0.4", + "@pact-foundation/pact": "^16.4.0", "@pact-foundation/pact-core": "^17.1.0", "@playwright/test": "^1.57.0", "allure-js-commons": "^3.3.3", diff --git a/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts b/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts index b73de11f3..c4c2a51cf 100644 --- a/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts +++ b/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts @@ -37,7 +37,7 @@ describe("Pact Message Consumer - LetterRequestPrepared Event", () => { datacontenttype: "application/json", dataschema: MatchersV3.regex( /^https:\/\/notify\.nhs\.uk\/cloudevents\/schemas\/letter-rendering\/letter-request\.prepared\.2\.\d+\.\d+\.schema\.json$/, - "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.2.0.1.schema.json", + "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.2.0.2.schema.json", ), dataschemaversion: MatchersV3.regex(/\d+\.\d+\.\d+/, "2.0.0"), traceparent: MatchersV3.string( diff --git a/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts b/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts index 9fbf33842..d9c2d4b65 100644 --- a/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts +++ b/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts @@ -1,6 +1,6 @@ import path from "node:path"; import { MessageProviderPact } from "@pact-foundation/pact"; -import LetterRequestPreparedEvent from "@nhsdigital/nhs-notify-event-schemas-letter-rendering/examples/LetterRequestPrepared/v2.0.1.json"; +import LetterRequestPreparedEvent from "@nhsdigital/nhs-notify-event-schemas-letter-rendering/examples/LetterRequestPrepared/v2.0.2.json"; describe("Letter rendering message provider tests", () => { test("verify pacts", async () => { From 64f50150b6cfcbc1222754a99618ceea8a17a6c1 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 12 May 2026 09:51:39 +0000 Subject: [PATCH 7/9] create-letter-batch logs letterIds, e2e read and wait for queue --- .env.template | 5 ++- .../letter-test-data/src/cli/index.ts | 3 ++ tests/e2e-tests/lib/letters.py | 36 +++++++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.env.template b/.env.template index 69e2590a1..55e341c3d 100644 --- a/.env.template +++ b/.env.template @@ -1,7 +1,10 @@ ENVIRONMENT=$ENV_NAME API_KEY= HEADERAUTH= -PR_NUMBER=prxx # remove if needs to run against main +# remove if needs to run against main +PR_NUMBER=prxx +# remove if needs to run against main +TARGET_ENVIRONMENT=prxx NHSD_APIM_TOKEN= PROXY_NAME= # * nhs-notify-supplier--internal-dev--nhs-notify-supplier diff --git a/scripts/utilities/letter-test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts index a8ddc8ffb..0b6c32371 100644 --- a/scripts/utilities/letter-test-data/src/cli/index.ts +++ b/scripts/utilities/letter-test-data/src/cli/index.ts @@ -229,6 +229,9 @@ async function main() { await letterRepository.unsafePutLetterBatch(letterDtos); console.log(`Created batch ${batchId} of ${letterDtos.length} letters`); + console.log( + `LETTER_IDS:${JSON.stringify(letterDtos.map(({ id }) => id))}`, + ); }, ) .demandCommand(1) diff --git a/tests/e2e-tests/lib/letters.py b/tests/e2e-tests/lib/letters.py index 4a1774ecb..ecadca468 100644 --- a/tests/e2e-tests/lib/letters.py +++ b/tests/e2e-tests/lib/letters.py @@ -2,6 +2,7 @@ import subprocess import pathlib import time +import json import requests from lib.errorhandler import ErrorHandler @@ -11,11 +12,13 @@ _SUPPLIER_ID = "supplier1" # This should be the same id registered in the Apigee App to which the proxy will be associated -def create_test_data(count: int = 10) -> None: +def create_test_data(count: int = 10) -> list[str]: """Seed PENDING letters by delegating to the shared letter-test-data CLI. Mirrors createTestData() in tests/helpers/generate-fetch-test-data.ts so both test suites seed data through the same tool. + + Returns a list of letter IDs created by the CLI. """ environment = os.environ.get("TARGET_ENVIRONMENT", "main") aws_account_id = os.environ.get("AWS_ACCOUNT_ID", "820178564574") @@ -38,6 +41,7 @@ def create_test_data(count: int = 10) -> None: "--test-letter", "test-letter-standard", ] + print(f"Creating test data by running CLI command: {' '.join(cmd)}") result = subprocess.run(cmd, cwd=_REPO_ROOT, capture_output=True, text=True) if result.returncode != 0: raise RuntimeError( @@ -46,6 +50,22 @@ def create_test_data(count: int = 10) -> None: f"stderr:\n{result.stderr}" ) + ids_prefix = "LETTER_IDS:" + for line in result.stdout.splitlines(): + if line.startswith(ids_prefix): + payload = line[len(ids_prefix):] + data = json.loads(payload) + if isinstance(data, list): + res = [str(item) for item in data] + print(f"The following letter IDs were created: {res}") + return res + + raise RuntimeError( + "create-letter-batch CLI completed successfully but did not output LETTER_IDS.\n" + f"stdout:\n{result.stdout}\n" + f"stderr:\n{result.stderr}" + ) + def get_pending_letter_ids( url: str, @@ -60,24 +80,26 @@ def get_pending_letter_ids( visible via the letters endpoint. Retries to account for other tests running in parallel stealing the letters Returns a list of letter ID strings. - Raises TimeoutError if fewer than `limit` letters are returned after all retries are exhausted. + Raises TimeoutError if the expected number of pending letters do not appear within the timeout period. """ for _ in range(retries): - create_test_data(limit) + letterIds = create_test_data(limit) deadline = time.monotonic() + timeout_s data = [] while time.monotonic() < deadline: # Retrieves letters based on the supplier registered in the Apigee App response = requests.get( - f"{url}{letters_endpoint}?limit={limit}", headers=headers + f"{url}{letters_endpoint}", headers=headers ) ErrorHandler.handle_retry(response) response.raise_for_status() data.extend(response.json().get("data", [])) - if len(data) >= limit: - print(f"Created and found letters with IDs {[item.get('id') for item in data]} for supplier registered in the Apigee App, to which the proxy is associated") - return [item.get("id") for item in data] + idsFound = [ item.get("id") for item in data ] + if set(letterIds).issubset(idsFound): + print(f"Found all created letter IDs: {letterIds}") + return letterIds + print(f"Expected letter IDs {letterIds} not found in response. Retrying in {interval_s} seconds...") time.sleep(interval_s) raise TimeoutError( From ef8440fd2955444e7236bcee2fd4ba1ea9ab3490 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 13 May 2026 17:14:13 +0000 Subject: [PATCH 8/9] env template cleanup --- .env.template | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.env.template b/.env.template index 87eca9f1b..9bd5f13fa 100644 --- a/.env.template +++ b/.env.template @@ -1,19 +1,18 @@ -# remove if needs to run against main -PR_NUMBER=prxx # Your github Personal Access Token (PAT) GITHUB_TOKEN= -# The variables below are used for End to End tests -# information about the proxy name can be found in the tests/e2e-tests/README.md -# e.g. # nhs-notify-supplier--internal-dev--nhs-notify-supplier # nhs-notify-supplier--internal-dev--nhs-notify-supplier-PR-XX # nhs-notify-supplier--ref--nhs-notify-supplier PROXY_NAME= + +#A PIM env to run tests against, other options are: ref, int, prod API_ENVIRONMENT=internal-dev -# 820178564574 Supplier Dev + +# 820178564574 Supplier Dev is the default if removed # See AWS access portal for others AWS_ACCOUNT_ID=820178564574 + # remove if needs to run against main TARGET_ENVIRONMENT=prxx @@ -37,3 +36,7 @@ export NON_PROD_PRIVATE_KEY=xxx export INTEGRATION_PRIVATE_KEY=xxx # private key path used to generate authentication for tests ran against the prod environment export PRODUCTION_PRIVATE_KEY=xxx + +# E2E Test Variables +# ======== +# To set variables for running E2E tests locally see tests/e2e-tests/README.md From cd0fcac6d891380717add5f116d42c155a5613ef Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 13 May 2026 17:36:58 +0000 Subject: [PATCH 9/9] env template cleanup 2 --- .env.template | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.template b/.env.template index 9bd5f13fa..6aed7e69a 100644 --- a/.env.template +++ b/.env.template @@ -1,18 +1,18 @@ # Your github Personal Access Token (PAT) GITHUB_TOKEN= -# nhs-notify-supplier--internal-dev--nhs-notify-supplier +# Apigee proxy name to be used for test execution # nhs-notify-supplier--internal-dev--nhs-notify-supplier-PR-XX -# nhs-notify-supplier--ref--nhs-notify-supplier PROXY_NAME= -#A PIM env to run tests against, other options are: ref, int, prod +# APIM env to run tests against, other options are: ref, int, prod API_ENVIRONMENT=internal-dev # 820178564574 Supplier Dev is the default if removed # See AWS access portal for others AWS_ACCOUNT_ID=820178564574 +# Resource namespace used to resolve AWS resource names for tests (main, pr123) # remove if needs to run against main TARGET_ENVIRONMENT=prxx