Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
6b0f825
Move towards using URL-based env var names.
davidhamill1-nhs Apr 15, 2026
a4675a8
Resolve this TODO later.
davidhamill1-nhs Apr 15, 2026
0202c90
Broaden vocabulary.
davidhamill1-nhs Apr 15, 2026
8d2f8a1
Log the client details.
davidhamill1-nhs Apr 16, 2026
dea2c64
Use env vars and pass those through an .env file in the make deploy c…
davidhamill1-nhs Apr 16, 2026
4204308
Make number go up.
davidhamill1-nhs Apr 16, 2026
899ba50
Make number go up.
davidhamill1-nhs Apr 16, 2026
4f47fa9
Make number go up.
davidhamill1-nhs Apr 16, 2026
d80e86e
Make explicit environment variables being passed to container.
davidhamill1-nhs Apr 16, 2026
67ef071
Make mypy happy again.
davidhamill1-nhs Apr 16, 2026
b289500
Place env vars in to app config.
davidhamill1-nhs Apr 16, 2026
69a2929
Pass URLs to controller.
davidhamill1-nhs Apr 16, 2026
5268215
Remove default URLs
davidhamill1-nhs Apr 16, 2026
3cdecdb
Tidy up.
davidhamill1-nhs Apr 16, 2026
ba67227
Improve make commands to build up env files for different environments.
davidhamill1-nhs Apr 16, 2026
c54bb04
Merge remote-tracking branch 'origin' into debt/GPCAPIM-359-sort-out-…
davidhamill1-nhs Apr 16, 2026
5b41732
FLASK env vars are not actually variable. Leave them hardcoded.
davidhamill1-nhs Apr 20, 2026
a2866c5
Enable test environment to be set up easily.
davidhamill1-nhs Apr 21, 2026
a0a47d3
Define shell.
davidhamill1-nhs Apr 21, 2026
e6e1c14
Inject env correctly and safely.
davidhamill1-nhs Apr 21, 2026
78663fa
Remove duplicate/unused make commands.
davidhamill1-nhs Apr 21, 2026
15528f8
Add simple logging to clients to demonstrate URLs being used.
davidhamill1-nhs Apr 21, 2026
629c53c
Remove unused pipeline env var.
davidhamill1-nhs Apr 21, 2026
405a7af
Merge remote-tracking branch 'origin' into debt/GPCAPIM-359-sort-out-…
davidhamill1-nhs Apr 21, 2026
71cf237
Don't need to do some TODOs
davidhamill1-nhs Apr 21, 2026
0c29fb2
Resolve some TODOs
davidhamill1-nhs Apr 22, 2026
3dd80ff
Handle unit test enviroment variables better.
davidhamill1-nhs Apr 22, 2026
c82811f
Simplify local vs remote paths.
davidhamill1-nhs Apr 22, 2026
247881f
Always overwrite.
davidhamill1-nhs Apr 22, 2026
d850d0e
Update docs. Improve env-ci output.
davidhamill1-nhs Apr 22, 2026
c3d633f
Tidy up.
davidhamill1-nhs Apr 22, 2026
2aafbee
Place the test user in to the env variables.
davidhamill1-nhs Apr 22, 2026
928d629
Tidy up.
davidhamill1-nhs Apr 22, 2026
9290a0a
Merge remote-tracking branch 'origin' into debt/GPCAPIM-359-sort-out-…
davidhamill1-nhs Apr 22, 2026
04092fd
Make linter happy.
davidhamill1-nhs Apr 22, 2026
0b7e583
More tightly define type.
davidhamill1-nhs Apr 22, 2026
bda2bb6
Doesn't seem to 'omit'
davidhamill1-nhs Apr 22, 2026
55a234b
Resolved to do.
davidhamill1-nhs Apr 22, 2026
bab15e1
Place make env file eariler in commands to run.
davidhamill1-nhs Apr 22, 2026
cff2eff
Update creation of .env.test. steps.
davidhamill1-nhs Apr 22, 2026
5fc19a2
Add an orangebox environment that would reflect env-organebox behaviour.
davidhamill1-nhs Apr 22, 2026
4082b47
Correctly print out logs.
davidhamill1-nhs Apr 22, 2026
f9be003
Make orange box test patient FHIR compliant.
davidhamill1-nhs Apr 22, 2026
5ded7c5
Update FHIR Parameters resource to include required name field.
davidhamill1-nhs Apr 22, 2026
03aff72
Add explicit return statements.
davidhamill1-nhs Apr 22, 2026
80bedaf
Add a default case for the test user - none. It should only be needed…
davidhamill1-nhs Apr 22, 2026
762b10b
Add local secret handling for int
davidhamill1-nhs Apr 23, 2026
f9f1319
Removing noise from "make deploy" output
davidhamill1-nhs Apr 23, 2026
5c99f53
Only log pertinent, non-sensitive env vars.
davidhamill1-nhs Apr 23, 2026
dd2c50b
Do not log sensitive headers.
davidhamill1-nhs Apr 23, 2026
b5445c5
clean up.
davidhamill1-nhs Apr 23, 2026
519efda
Remove env vars that did not exist before fiddling with the env vars.
davidhamill1-nhs Apr 23, 2026
057a4c4
Options not supported at this level.
davidhamill1-nhs Apr 23, 2026
548c49d
Do not log sensitive headers.
davidhamill1-nhs Apr 23, 2026
158f4c0
Stub for int environment, until 397 is worked.
davidhamill1-nhs Apr 23, 2026
c22c719
Provide useful error messages if env files do not exist.
davidhamill1-nhs Apr 23, 2026
9d39d0e
Merge remote-tracking branch 'origin' into debt/GPCAPIM-359-sort-out-…
davidhamill1-nhs Apr 23, 2026
d043161
Provide explanation around the expected secrets.
davidhamill1-nhs Apr 23, 2026
7a4fc64
Correct env file check.
davidhamill1-nhs Apr 23, 2026
f84e63e
Refine logger setup; test url that instantiates the client is being u…
davidhamill1-nhs Apr 23, 2026
5d2266e
Rename class to better reflect behaviour.
davidhamill1-nhs Apr 23, 2026
3775cb5
Test url that instantiates the controller is being used on the GET re…
davidhamill1-nhs Apr 23, 2026
c98e561
Remove unused fixture.
davidhamill1-nhs Apr 23, 2026
8c5c2fe
Provider a more helpful warning.
davidhamill1-nhs Apr 23, 2026
1212296
Explain what this decorator is doing.
davidhamill1-nhs Apr 23, 2026
b02ac58
Update fixture name.
davidhamill1-nhs Apr 23, 2026
71ea523
Remove toggling of sandbox/int services
davidhamill1-nhs Apr 23, 2026
4032bbc
Merge branch 'debt/GPCAPIM-359-sort-out-env-vars' into feature/GPCAPI…
davidhamill1-nhs Apr 23, 2026
f04a489
Remove toggling of sandbox/int services
davidhamill1-nhs Apr 23, 2026
99c4328
Merge branch 'debt/GPCAPIM-359-sort-out-env-vars' into feature/GPCAPI…
davidhamill1-nhs Apr 23, 2026
1901406
As of pytest 4, ini_options is no longer needed.
davidhamill1-nhs Apr 27, 2026
2c563f1
Merge remote-tracking branch 'origin' into feature/GPCAPIM-397-set-up…
davidhamill1-nhs Apr 27, 2026
7c5494d
Remove redundant only file in redundant folder.
davidhamill1-nhs Apr 27, 2026
fbfa9e1
Need to find a way to trust the CA for EMIS INT's certs.
davidhamill1-nhs Apr 27, 2026
d46ae9e
Enable easy test config for int.
davidhamill1-nhs Apr 27, 2026
23379f8
Seed EMIS INT patient into stub, to allow for testing while INT PDS A…
davidhamill1-nhs Apr 27, 2026
1372326
Update env make commands to allow for testing INT.
davidhamill1-nhs Apr 27, 2026
4f8706c
Update vocab.
davidhamill1-nhs Apr 27, 2026
96434d3
Handle additional kwargs that can be passed in a requests.post method.
davidhamill1-nhs Apr 27, 2026
2f54b36
Reduce friction in adding new endpoints to the stub.
davidhamill1-nhs Apr 27, 2026
ae1e161
Add body to provider response logging, in order to understand/confirm…
davidhamill1-nhs Apr 27, 2026
2ef3bd1
Provide value that are valid for INT environment requests - full end-…
davidhamill1-nhs Apr 27, 2026
b5a274a
Make it easier to build INT env file.
davidhamill1-nhs Apr 27, 2026
361c6e2
Update docs.
davidhamill1-nhs Apr 27, 2026
e905a71
Remove duplicated make command.
davidhamill1-nhs Apr 28, 2026
655a1eb
For the moment, run int tests against a locally deployed app.
davidhamill1-nhs Apr 28, 2026
1d249a4
Tidy up test helpers.
davidhamill1-nhs Apr 28, 2026
a030982
Use ODS code that has a valid looking ASID in the SDS stub.
davidhamill1-nhs Apr 28, 2026
89a5821
Improve layout.
davidhamill1-nhs Apr 28, 2026
efb7a1b
Fixture no longer exists - remove docs for it.
davidhamill1-nhs Apr 28, 2026
7f7c693
Add explanation on testing locally deployed app pointing at INT.
davidhamill1-nhs Apr 28, 2026
e2b3eca
Merge remote-tracking branch 'origin' into feature/GPCAPIM-397-set-up…
davidhamill1-nhs Apr 30, 2026
7b0a15a
Certs will be handled by the HSCN proxy. For now, disable cert verifi…
davidhamill1-nhs Apr 30, 2026
b6ac22b
Give the test patient the ODS code that wil lead to the EMIS endpoint…
davidhamill1-nhs Apr 30, 2026
d060c2f
Remove unreference test data file.
davidhamill1-nhs Apr 30, 2026
39d9338
Ensure there is a consumer-system ODS code available for testing agai…
davidhamill1-nhs Apr 30, 2026
fe92f5e
Improve loading expected responses.
davidhamill1-nhs Apr 30, 2026
6003175
INT env already merged from Ian's work.
davidhamill1-nhs Apr 30, 2026
128bd58
Tidy up.
davidhamill1-nhs Apr 30, 2026
17f61b9
Update docs.
davidhamill1-nhs Apr 30, 2026
6878efe
Tidy up.
davidhamill1-nhs Apr 30, 2026
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
2 changes: 2 additions & 0 deletions .vscode/cspell-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dpkg
DSTU
eamodio
errstr
EMIS
Farsley
fhir
getfixturevalue
Expand All @@ -47,6 +48,7 @@ gpprovider
HAPI
healthcheck
htmlcov
HSCN
igorshubovych
isort
jdkato
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ _Note: `FLASK_HOST` and `FLASK_PORT` are hardcoded in to the Dockerfile. These a
| `BASE_URL` | Set if targeting a locally deployed application; otherwise, leave unset. |
| `TARGET_ENV` | Either `local` or `remote`, to inform the HTTP client used by the tests how to behave - e.g. add auth headers, etc. |
| `REMOTE_TEST_USERNAME` | The test user through which the tests will be authenticated against when run against a remote target. |
| `TEST_NHS_NUMBER` | The NHS number to be sent in the request payload. Environments will have a limited set of NHS numbers that are able to form a full end-to-end flow. |
| `CONSUMER_ODS_CODE` | The consumer ODS code to send in the header. ODS codes may only work in some environments. |

## Testing

Expand Down
2 changes: 1 addition & 1 deletion gateway-api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ dev = [
[tool.mypy]
strict = true

[tool.pytest.ini_options]
[tool.pytest]
bdd_features_base_dir = "tests/acceptance/features"
markers = [
"status_auth_headers",
Expand Down
9 changes: 5 additions & 4 deletions gateway-api/src/gateway_api/provider/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,18 @@ def access_structured_record(
base_endpoint = self.provider_endpoint.rstrip("/") + "/"
url = urljoin(base_endpoint, self.endpoint_path)

# INT provider system certs are not accepted by requests library,
# so we disable SSL verification for the integration environment for now.
verify_certs = os.environ.get("VERIFY_PROVIDER_CERTS", "true").lower() == "true"
log_details = {
"description": "GPProvider FHIR API request",
"url": url,
"verify_certs": verify_certs,
}
_logger.info(log_details)

response = post(
url,
headers=headers,
data=body,
timeout=TIMEOUT,
url, headers=headers, data=body, timeout=TIMEOUT, verify=verify_certs
)
log_details = {
"description": "GPProvider FHIR API response received",
Expand Down
2 changes: 1 addition & 1 deletion gateway-api/src/gateway_api/provider/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _fake_post(
url: str,
headers: CaseInsensitiveDict[str],
data: str,
timeout: int, # NOQA ARG001 (unused in stub)
**_kwargs: str,
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

**_kwargs: str is too restrictive for a requests.post-like stub. The production call now passes verify (bool) and timeout (int|None), so this annotation will fail type-checking under strict mypy. Use **_kwargs: Any (or a typed **kwargs: object) to match the real signature more accurately.

Suggested change
**_kwargs: str,
**_kwargs: Any,

Copilot uses AI. Check for mistakes.
) -> Response:
"""A fake requests.post implementation."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"type": "Organization",
"identifier": {
"system": "https://fhir.nhs.uk/Id/ods-organization-code",
"value": "A20047",
"value": "B86047",
"period": {"start": "2020-01-01", "end": "9999-12-31"}
}
}
Expand Down
4 changes: 2 additions & 2 deletions gateway-api/stubs/stubs/sds/stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def _seed_default_devices(self) -> None:
{
"org_ods": "CONSUMER",
"device_id": "C0C0E921-92CA-4A88-A550-2DBB36F703AF",
"asid": "asid_CONS",
"asid": "210987654321",
"display": "Example Consumer Organisation",
},
{
Expand Down Expand Up @@ -419,7 +419,7 @@ def _seed_default_endpoints(self) -> None:
{
"org_ods": "CONSUMER",
"endpoint_id": "E1E1E921-92CA-4A88-A550-2DBB36F703AF",
"asid": "asid_CONS",
"asid": "210987654321",
"address": "https://consumer.example.com/fhir",
},
{
Expand Down
28 changes: 26 additions & 2 deletions gateway-api/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Use the following commands to create the appropriate `.env.test` file for the ta
* `make env-test-ci` to write a `.env.test` file for testing a locally deployed application, from outside the dev container.
* `make env-test-pr-<pr number>` to write a `.env.test` file for testing an application deployed behind a PR proxy.
* `make env-test-alpha-int` to write a `.env.test` file for testing an application deployed to the "alpha integration" environment.
* `make env-test-int` to write a `.env.test` file for testing an application that sends requests to PDS, SDS and Provider integration environments.

_Note: Unit tests require the `.env` file, as these tests do not test a deployed application`_

Expand Down Expand Up @@ -213,10 +214,9 @@ Consumer tests generate the pact contract files in `tests/contract/pacts/` (e.g.
Shared fixtures in `tests/conftest.py` are available across all test types:

* **`base_url`**: The base URL of the deployed Lambda function (from `BASE_URL` environment variable highlighted above)
* **`host`**: The hostname of the deployed application (from `HOST` environment variable highlighted above)
* **`client`**: An HTTP client instance for sending requests to the APIs

### Load Testing with Locust ('load/')
## Load Testing with Locust ('load/')

Load testing is performed using Locust. You have the option of running via the CLI or UI.
You will need [Proxygen setup](../../README.md#proxygen) to run the load tests.
Expand Down Expand Up @@ -350,3 +350,27 @@ The GitHub Actions workflow (`.github/workflows/stage-2-test.yaml`) orchestrates
│ 3. Enforce thresholds │
└───────────────────────┘
```

## Testing INT locally

To test a locally deployed application against the integration environments of external services, PDS, SDS and Provider systems:

* Set up secrets in `.secrets` as per the instructions in `.secrets/README.md`
* `make env-int` - writes the `.env` file to point the application at the integration environments for each external service.
* `make deploy` - will deploy the application, feeding the `.env` values in to its container environment.

### Manual test

To manually test the application against the integration environments, run the Bruno collection using the `localInt.yml` environment.
Comment on lines +356 to +364
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This section states make env-int points the application at PDS/SDS/Provider integration environments, but the current env scripts still configure PDS as stub for int. Either update the scripts to actually target PDS INT or adjust this documentation to reflect the current behaviour so local INT testing expectations are correct.

Suggested change
To test a locally deployed application against the integration environments of external services, PDS, SDS and Provider systems:
* Set up secrets in `.secrets` as per the instructions in `.secrets/README.md`
* `make env-int` - writes the `.env` file to point the application at the integration environments for each external service.
* `make deploy` - will deploy the application, feeding the `.env` values in to its container environment.
### Manual test
To manually test the application against the integration environments, run the Bruno collection using the `localInt.yml` environment.
To test a locally deployed application with the current INT-style configuration for external services:
* Set up secrets in `.secrets` as per the instructions in `.secrets/README.md`
* `make env-int` - writes the `.env` file to point SDS and Provider at their integration environments; PDS remains configured as `stub`.
* `make deploy` - will deploy the application, feeding those generated `.env` values in to its container environment.
### Manual test
To manually test the application with this configuration, run the Bruno collection using the `localInt.yml` environment.

Copilot uses AI. Check for mistakes.

### Automated tests

To run a subset the automated test suite, run the following commands
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

Grammar: "To run a subset the automated test suite" is missing "of" ("subset of the automated test suite").

Suggested change
To run a subset the automated test suite, run the following commands
To run a subset of the automated test suite, run the following commands

Copilot uses AI. Check for mistakes.

```bash
make env-test-int
source .env.test
set -a && source .env && set +a
cd gateway-api
poetry run pytest tests/integration/test_get_structured_record.py -k happy_path
```
34 changes: 30 additions & 4 deletions gateway-api/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Pytest configuration and shared fixtures for gateway API tests."""

import copy
import os
from collections.abc import Callable
from datetime import timedelta
from typing import Any, cast
from uuid import uuid4

import pytest
import requests
Expand All @@ -13,7 +13,7 @@
DEFAULT_REQUEST_HEADERS = {
"Content-Type": "application/fhir+json",
"Ods-from": "CONSUMER",
"Ssp-TraceID": "test-trace-id",
"Ssp-TraceID": str(uuid4()),
}


Expand Down Expand Up @@ -80,8 +80,25 @@ def send_health_check(self) -> requests.Response:


@pytest.fixture
def simple_request_payload() -> dict[str, Any]:
return copy.deepcopy(SIMPLE_PAYLOAD)
def nhs_number() -> str:
return _fetch_env_variable("TEST_NHS_NUMBER", str)


@pytest.fixture
def simple_request_payload(nhs_number: str) -> dict[str, Any]:
payload = {
"resourceType": "Parameters",
"parameter": [
{
"name": "patientNHSNumber",
"valueIdentifier": {
"system": FHIRSystem.NHS_NUMBER,
"value": nhs_number,
},
},
],
}
return payload


@pytest.fixture
Expand All @@ -100,6 +117,15 @@ def headers(env: str, request: pytest.FixtureRequest) -> dict[str, str]:
return nhsd_headers | apikey_headers


@pytest.fixture
def ods_header() -> dict[str, str]:
try:
ods_code = _fetch_env_variable("CONSUMER_ODS_CODE", str)
except ValueError:
ods_code = "CONSUMER"
return {"Ods-from": ods_code}


@pytest.fixture
def client(
base_url: str,
Expand Down
14 changes: 2 additions & 12 deletions gateway-api/tests/contract/stub/test_sds_stub_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,22 +238,12 @@ def test_endpoint_bundle_matches_expected_response(
assert body["type"] == "searchset"
assert body["total"] == len(body["entry"])

assert len(body["entry"]) == 4
endpoint_ids = [
"E0E0E921-92CA-4A88-A550-2DBB36F703AF",
"E1E1E921-92CA-4A88-A550-2DBB36F703AF",
"E2E2E921-92CA-4A88-A550-2DBB36F703AF",
"E3E3E921-92CA-4A88-A550-2DBB36F703AF",
]
for i in range(len(endpoint_ids)):
entry = body["entry"][i]

endpoint_id = endpoint_ids[i]
for entry in body["entry"]:
endpoint_id = entry["resource"]["id"]
assert (
entry["fullUrl"]
== f"https://sandbox.api.service.nhs.uk/spine-directory/FHIR/R4/Endpoint/{endpoint_id}"
)
assert entry["resource"]["id"] == endpoint_id
assert entry["search"]["mode"] == "match"

def test_x_correlation_id_echoed_back_when_provided(
Expand Down
56 changes: 56 additions & 0 deletions gateway-api/tests/integration/data/alice_jones_9999999999.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"resourceType": "Bundle",
"type": "collection",
"meta": {
"profile": [
"https://fhir.nhs.uk/STU3/StructureDefinition/GPConnect-StructuredRecord-Bundle-1"
]
},
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "9999999999",
"meta": {
"versionId": "1",
"lastUpdated": "2020-01-01T00:00:00Z"
},
"identifier": [
{
"system": "https://fhir.nhs.uk/Id/nhs-number",
"value": "9999999999"
}
],
"name": [
{
"use": "official",
"family": "Jones",
"given": [
"Alice"
],
"period": {
"start": "1900-01-01",
"end": "9999-12-31"
}
}
],
"gender": "female",
"birthDate": "1980-01-01",
"generalPractitioner": [
{
"id": "1",
"type": "Organization",
"identifier": {
"system": "https://fhir.nhs.uk/Id/ods-organization-code",
"value": "A12345",
"period": {
"start": "2020-01-01",
"end": "9999-12-31"
}
}
}
]
}
}
]
}
Loading
Loading