Skip to content

Commit a1c77ae

Browse files
squash and sign (#340)
1 parent fe8ce2e commit a1c77ae

24 files changed

Lines changed: 2061 additions & 0 deletions

.env.template

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,27 @@ API_KEY=
33
HEADERAUTH=
44
PR_NUMBER=prxx # remove if needs to run against main
55
NHSD_APIM_TOKEN=
6+
PROXY_NAME=
7+
# * nhs-notify-supplier--internal-dev--nhs-notify-supplier
8+
# * nhs-notify-supplier--internal-dev--nhs-notify-supplier-PR-XX
9+
# * nhs-notify-supplier--ref--nhs-notify-supplier -- ref env
10+
11+
# API Keys
12+
# ========
13+
# In order to find out the value of an environments given API key, follow these steps
14+
# 1. Log in to Non-Prod
15+
# 2. Navigate to 'Publish' > 'Apps' and search for the app linked to authentication
16+
# 3. Copy the "key" from the Credentials related to the app
17+
# Note: For INT and higher environments use developer portal https://identity.prod.api.platform.nhs.uk/
18+
export NON_PROD_API_KEY=xxx
19+
export INTEGRATION_API_KEY=xxx
20+
export PRODUCTION_API_KEY=xxx
21+
22+
# Private Keys
23+
# ============
24+
# private key used to generate authentication for tests ran against the internal-dev and internal-qa
25+
export NON_PROD_PRIVATE_KEY=xxx # path to the private key file
26+
# private key used to generate authentication for tests ran against the int environment
27+
export INTEGRATION_PRIVATE_KEY=xxx
28+
# private key used to generate authentication for tests ran against the prod environment
29+
export PRODUCTION_PRIVATE_KEY=xxx

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ dist
3232
/specification/api/components/security/security.yml
3333
/specification/api/components/parameters/authorization/authorization.yml
3434
/scripts/JWT/*.pem
35+
/tests/e2e-tests/test-report.xml
3536

3637
# ignore PACTS
3738
.pacts
39+
40+
# python venvs
41+
.venv/
42+
venv/
43+
env/
44+
ENV/

Makefile

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,54 @@ ${VERBOSE}.SILENT: \
121121
config \
122122
dependencies \
123123
deploy \
124+
125+
#####################
126+
# E2E Test commands #
127+
#####################
128+
129+
TEST_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \
130+
PYTHONPATH=. poetry run pytest --disable-warnings -vv \
131+
--color=yes \
132+
-n 4 \
133+
--api-name=nhs-notify-supplier \
134+
--proxy-name="$(PROXY_NAME)" \
135+
-s \
136+
--reruns 5 \
137+
--reruns-delay 5 \
138+
--only-rerun 'AssertionError: Unexpected 429' \
139+
--only-rerun 'AssertionError: Unexpected 504' \
140+
--only-rerun 'AssertionError: Unexpected 502' \
141+
--junitxml=test-report.xml
142+
143+
144+
.internal-dev-test:
145+
@cd tests/e2e-tests && \
146+
$(TEST_CMD) \
147+
api \
148+
-m devtest
149+
150+
.integration-test:
151+
$(TEST_CMD) \
152+
tests/api \
153+
-m inttest
154+
155+
156+
PROD_CMD := APIGEE_ACCESS_TOKEN="$(APIGEE_ACCESS_TOKEN)" \
157+
PYTHONPATH=. poetry run pytest --disable-warnings -vv \
158+
--color=yes \
159+
-n 4 \
160+
--api-name=nhs-notify-supplier \
161+
--proxy-name="$(PROXY_NAME)" \
162+
-s \
163+
--reruns 5 \
164+
--reruns-delay 5 \
165+
--only-rerun 'AssertionError: Unexpected 429' \
166+
--only-rerun 'AssertionError: Unexpected 504' \
167+
--only-rerun 'AssertionError: Unexpected 502' \
168+
--junitxml=test-report.xml
169+
170+
.prod-test:
171+
@cd tests/e2e-tests && \
172+
$(PROD_CMD) \
173+
tests/api \
174+
-m prodtest

pytest.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[pytest]
2+
pythonpath =
3+
tests/e2e-tests/api
4+
python_files = *_tests.py test_*.py
5+
addopts = --strict-markers
6+
markers =
7+
smoketest: suitable to run against all environments even production
8+
mtlstest: suitable to run against periodically against environments
9+
sandboxtest: suitable to run against sandbox environment
10+
devtest: suitable to run against internal-dev environments
11+
inttest: suitable to run against integration environment
12+
prodtest: suitable to run against production environment
13+
uattest: suitable to run against uat environment
14+
test: suitable to run against all environments

tests/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ node_modules/
99
/target
1010
/playwright/.auth/
1111
/allure-report
12+
13+
*/__pycache__/
14+
**/__pycache__/
15+
__pycache__/
16+
*/__pycache__/
17+
**/__pycache__/
18+
*.pyc

tests/e2e-tests/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# E2E Tests
2+
3+
## Generate An Apigee Access Token
4+
5+
To generate authentication using Apigee, you must have access to an Apigee account and use `get_token` via the command line and generate an Apigee access token.
6+
7+
**Tokens expire once per day and require refreshing.**
8+
9+
* Install [`get_token`](https://docs.apigee.com/api-platform/system-administration/auth-tools#install)
10+
* Run the following command and log in with your Apigee credentials when prompted:
11+
12+
```shell
13+
export APIGEE_ACCESS_TOKEN=$(SSO_LOGIN_URL=https://login.apigee.com get_token)
14+
```
15+
16+
* If your token does not refresh, try clearing the cache:
17+
18+
```shell
19+
export APIGEE_ACCESS_TOKEN=$(SSO_LOGIN_URL=https://login.apigee.com get_token --clear-sso-cache)
20+
```
21+
22+
### Set Proxy Name
23+
24+
Set the `PROXY_NAME` environment variable to specify the environment for test execution. You can find the proxy name by logging into [Apigee](https://apigee.com/edge), navigating to 'API Proxies' and searching for 'supplier-api' for lower environments like internal-dev.
25+
26+
```shell
27+
export PROXY_NAME=nhs-notify-supplier--internal-dev--nhs-notify-supplier
28+
```
29+
30+
Available values for `PROXY_NAME` include:
31+
32+
* `nhs-notify-supplier--internal-dev--nhs-notify-supplier`
33+
* `nhs-notify-supplier--internal-dev--nhs-notify-supplier-pr<num>`
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import requests
2+
import pytest
3+
from lib.constants import METHODS, VALID_ENDPOINT_LETTERS
4+
from lib.fixtures import *
5+
from lib.errorhandler import ErrorHandler
6+
7+
@pytest.mark.test
8+
@pytest.mark.devtest
9+
@pytest.mark.inttest
10+
@pytest.mark.prodtest
11+
@pytest.mark.parametrize("method", METHODS)
12+
@pytest.mark.parametrize("endpoints", VALID_ENDPOINT_LETTERS)
13+
def test_401_invalid(url, method, endpoints):
14+
resp = getattr(requests, method)(f"{url}{endpoints}", headers={
15+
"Authorization": "invalid",
16+
})
17+
18+
ErrorHandler.handle_retry(resp)
19+
assert resp.status_code == 401, f"Response: {resp.status_code}: {resp.text}"
20+
21+
@pytest.mark.test
22+
@pytest.mark.nhsd_apim_authorization({"access": "application", "level": "level0"})
23+
@pytest.mark.parametrize("method", METHODS)
24+
@pytest.mark.parametrize("endpoints", VALID_ENDPOINT_LETTERS)
25+
def test_401_invalid_level(nhsd_apim_proxy_url, nhsd_apim_auth_headers, method, endpoints):
26+
print(nhsd_apim_proxy_url)
27+
28+
resp = getattr(requests, method)(f"{nhsd_apim_proxy_url}{endpoints}", headers={
29+
**nhsd_apim_auth_headers
30+
})
31+
32+
ErrorHandler.handle_retry(resp)
33+
assert resp.status_code == 401, f"Response: {resp.status_code}: {resp.text}"
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import uuid
2+
import requests
3+
import pytest
4+
from lib.fixtures import * # NOSONAR
5+
from lib.constants import LETTERS_ENDPOINT
6+
from lib.generators import Generators
7+
from lib.errorhandler import ErrorHandler
8+
9+
@pytest.mark.test
10+
@pytest.mark.devtest
11+
@pytest.mark.inttest
12+
@pytest.mark.prodtest
13+
def test_200_get_letter_status(url, bearer_token):
14+
headers = Generators.generate_valid_headers(bearer_token.value)
15+
get_letter_id = requests.get(f"{url}/{LETTERS_ENDPOINT}/", headers=headers)
16+
17+
letter_id = get_letter_id.json().get("data")[0].get("id")
18+
get_letter_data = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}/data", headers=headers)
19+
20+
ErrorHandler.handle_retry(get_letter_data)
21+
assert get_letter_data.status_code == 200, f"Response: {get_letter_data.status_code}: {get_letter_data.text}"
22+
assert get_letter_data.headers.get("Content-Type") == "application/pdf"
23+
24+
@pytest.mark.test
25+
@pytest.mark.devtest
26+
@pytest.mark.inttest
27+
@pytest.mark.prodtest
28+
def test_404_letter_does_not_exist(url, bearer_token):
29+
headers = Generators.generate_valid_headers(bearer_token.value)
30+
get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/xx", headers=headers)
31+
32+
ErrorHandler.handle_retry(get_message_response)
33+
assert get_message_response.status_code == 404
34+
assert get_message_response.json().get("errors")[0].get("detail") == "No resource found with that ID"
35+
36+
@pytest.mark.test
37+
@pytest.mark.devtest
38+
@pytest.mark.inttest
39+
@pytest.mark.prodtest
40+
def test_404_letter_does_not_exist(url, bearer_token):
41+
letter_id = uuid.uuid4().hex
42+
headers = Generators.generate_valid_headers(bearer_token.value)
43+
get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}/data", headers=headers)
44+
45+
ErrorHandler.handle_retry(get_message_response)
46+
assert get_message_response.status_code == 404
47+
assert get_message_response.json().get("errors")[0].get("detail") == "No resource found with that ID"
48+
49+
@pytest.mark.test
50+
@pytest.mark.devtest
51+
@pytest.mark.inttest
52+
@pytest.mark.prodtest
53+
def test_500_letter_does_not_exist(url, bearer_token):
54+
letter_id = "00000000-0000-0000-0000-000000000000"
55+
headers = Generators.generate_valid_headers(bearer_token.value)
56+
get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}/data", headers=headers)
57+
58+
ErrorHandler.handle_retry(get_message_response)
59+
assert get_message_response.status_code == 500
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import requests
2+
import pytest
3+
from lib.fixtures import * # NOSONAR
4+
from lib.constants import VALID_ENDPOINT_LETTERS, MI_ENDPOINT
5+
from lib.generators import Generators
6+
7+
METHODS = ["get", "post"]
8+
9+
10+
@pytest.mark.test
11+
@pytest.mark.devtest
12+
@pytest.mark.inttest
13+
@pytest.mark.prodtest
14+
@pytest.mark.parametrize("method", METHODS)
15+
@pytest.mark.parametrize("endpoints", VALID_ENDPOINT_LETTERS)
16+
def test_header_letters_endpoint(
17+
url,
18+
method,
19+
endpoints,
20+
bearer_token
21+
):
22+
resp = getattr(requests, method)(f"{url}/{endpoints}", headers={
23+
"Authorization": bearer_token.value,
24+
"X-Request-ID": None
25+
})
26+
27+
assert resp.status_code == 500
28+
29+
30+
@pytest.mark.test
31+
@pytest.mark.devtest
32+
@pytest.mark.inttest
33+
@pytest.mark.prodtest
34+
def test_header_mi_endpoint(
35+
url,
36+
bearer_token
37+
):
38+
resp = getattr(requests, "post")(f"{url}/{MI_ENDPOINT}", headers={
39+
"Authorization": bearer_token.value,
40+
"X-Request-ID": ""
41+
})
42+
43+
assert resp.status_code == 500
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import requests
2+
import pytest
3+
from lib.fixtures import * # NOSONAR
4+
from lib.constants import LETTERS_ENDPOINT
5+
from lib.generators import Generators
6+
from lib.errorhandler import ErrorHandler
7+
8+
@pytest.mark.test
9+
@pytest.mark.devtest
10+
@pytest.mark.inttest
11+
@pytest.mark.prodtest
12+
def test_200_get_letter_status(url, bearer_token):
13+
headers = Generators.generate_valid_headers(bearer_token.value)
14+
get_letters = requests.get(f"{url}/{LETTERS_ENDPOINT}/", headers=headers)
15+
16+
letter_id = get_letters.json().get("data")[0].get("id")
17+
get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/{letter_id}", headers=headers)
18+
19+
ErrorHandler.handle_retry(get_message_response)
20+
assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}"
21+
22+
23+
@pytest.mark.test
24+
@pytest.mark.devtest
25+
@pytest.mark.inttest
26+
@pytest.mark.prodtest
27+
def test_404_letter_does_not_exist(url, bearer_token):
28+
headers = Generators.generate_valid_headers(bearer_token.value)
29+
get_message_response = requests.get(f"{url}/{LETTERS_ENDPOINT}/xx", headers=headers)
30+
31+
ErrorHandler.handle_retry(get_message_response)
32+
assert get_message_response.status_code == 404, f"Response: {get_message_response.status_code}: {get_message_response.text}"
33+
assert get_message_response.json().get("errors")[0].get("detail") == "No resource found with that ID"

0 commit comments

Comments
 (0)