Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/acceptance-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ runs:
steps:

- name: Run component tests
if: ${{ inputs.testType != 'e2e' }}
if: ${{ inputs.testType == 'component' }}
uses: ./.github/actions/acceptance-tests-component
with:
testType: ${{ inputs.testType }}
targetEnvironment: ${{ inputs.targetEnvironment }}
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 }}
3 changes: 2 additions & 1 deletion .github/actions/build-proxies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TO REMOVE

1 change: 1 addition & 0 deletions .github/workflows/deploy-dynamic-env-proxy.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Test
name: Deploy dynamic PR environment proxy
run-name: Deploy proxy for PR environment on internal-dev by @${{ github.actor }}

Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ==============================================================================

Expand Down Expand Up @@ -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 \
Expand All @@ -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) \
Expand All @@ -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 \
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
);
Expand Down
2 changes: 1 addition & 1 deletion tests/constants/api-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down
21 changes: 6 additions & 15 deletions tests/e2e-tests/api/data/test_get_letter_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,27 @@
@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)

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
@pytest.mark.prodtest
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
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e-tests/api/headers/test_x_request_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand All @@ -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": ""
})
Expand Down
8 changes: 5 additions & 3 deletions tests/e2e-tests/api/letters/test_get_letter_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ def test_200_get_letter_status(url, authentication_secret):
ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1)
letter_id = ids[0]

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}"


@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)

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}"
Expand Down
1 change: 1 addition & 0 deletions tests/e2e-tests/api/letters/test_get_list_of_letters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 6 additions & 3 deletions tests/e2e-tests/api/letters/test_multiple_letter_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand All @@ -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,
)
Expand All @@ -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,
)
Expand Down
16 changes: 12 additions & 4 deletions tests/e2e-tests/api/letters/test_update_letter_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand All @@ -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,
)
Expand All @@ -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,
)
Expand All @@ -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,
)
Expand Down
8 changes: 5 additions & 3 deletions tests/e2e-tests/lib/letters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -68,17 +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]} 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}"
)
Loading