diff --git a/gateway-api/tests/manual-test/README.md b/gateway-api/tests/manual-test/README.md new file mode 100644 index 0000000..5ac58ca --- /dev/null +++ b/gateway-api/tests/manual-test/README.md @@ -0,0 +1,101 @@ +# Manual API Test – Steel Thread + +This folder contains a **manual API test** used to support the **steel thread** for the Clinical Data Gateway (CDG). + +The intent of this test is to: + +- Validate that a response is returned using the **expected structure** +- Support early preparation and understanding only + +This is **not automated testing** and **does not prove functional completeness**. + +## Steel Thread Scope + +For the steel thread, CDG supports **reading a patient record for a single patient** using the following structure: + +```bash +POST https://[CDG_server]/FHIR/STU3/patient/$gpc.getstructuredrecord +``` + +- FHIR version: **STU3** +- Format: **FHIR JSON** +- Operation: **custom GP Connect FHIR operation** +- Scope: **single patient** + +No error handling, authentication edge cases, or non-happy paths are covered. + +## Tooling + +This manual test uses **usebruno** as the API testing tool. + +Bruno is: + +- Free and open source +- Installed locally + +The Bruno collection for this test lives **inside this repository**, so no external workspace or account is required. + +## Installing Bruno (macOS) + +Bruno can be installed using Homebrew. + +1. Install Homebrew: + + ```bash + https://brew.sh/ + ``` + +2. Install Bruno: + + ```bash + brew install bruno + ``` + +3. Launch Bruno: + + ```bash + bruno + ``` + +## Opening the Bruno Collection + +To add it in Bruno: + +1. Open Bruno +2. Select **Open Collection** +3. Navigate to: + + ```text + clinical-data-gateway-api/gateway-api/tests/manual-test/api-test + ``` + +4. Open the folder + +The collection is now loaded and ready to use. + +## Running the Manual Test + +1. Select the **Retrieve Patient Record** request in the collection +2. Set Bruno to use the local environment +3. In terminal run `node mock-response.js`. Output should read `Mock Retrieve Patient Record server running at http://localhost:8080` +4. Send the request + +A successful response should return a **FHIR STU3 response** that aligns with the expected steel-thread response shape. + +```json +POST {{environment}}/FHIR/STU3/patient/$gpc.getstructuredrecord +Content-Type: application/fhir+json +Accept: application/fhir+json +{ + "resourceType": "Parameters", + "parameter": [ + { + "name": "patientNHSNumber", + "valueIdentifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9999999999" + } + } + ] +} +``` diff --git a/gateway-api/tests/manual-test/api-test/api-gateway/Retrieve-Patient-Record.bru b/gateway-api/tests/manual-test/api-test/api-gateway/Retrieve-Patient-Record.bru new file mode 100644 index 0000000..e51957e --- /dev/null +++ b/gateway-api/tests/manual-test/api-test/api-gateway/Retrieve-Patient-Record.bru @@ -0,0 +1,80 @@ +meta { + name: Retrieve Patient Record + type: http + seq: 3 +} + +post { + url: {{environment}}/FHIR/STU3/patient/$gpc.getstructuredrecord + body: text + auth: inherit +} + +headers { + Ssp-TraceID: {{$guid}} + Ssp-From: 200000000359 + Ssp-To: 918999198738 + Accept: application/fhir+json + Ssp-InteractionId: urn:nhs:names:services:gpconnect:fhir:operation:gpc.getstructuredrecord-1 + Authorization: Bearer {{jwt_token}} + Content-Type: application/fhir+json +} + +body:text { + {"resourceType":"Parameters","parameter":[{"name":"patientNHSNumber","valueIdentifier":{"system":"https://fhir.nhs.uk/Id/nhs-number","value":"9690938118"}},{"name":"includeAllergies","part":[{"name":"includeResolvedAllergies","valueBoolean":true}]}]} +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { + Required JWT Scope: patient/*.read +} + +example { + name: Example 200 Response + + request: { + url: {{environment}}/B82617/STU3/1/gpconnect/structured/fhir/Patient/$gpc.getstructuredrecord + method: POST + mode: text + headers: { + Ssp-TraceID: 369ae31c-8bfc-4df1-9861-1640c914c7f5 + Ssp-From: 200000000359 + Ssp-To: 918999198738 + Accept: application/fhir+json + Ssp-InteractionId: urn:nhs:names:services:gpconnect:fhir:operation:gpc.getstructuredrecord-1 + } + + body:text: { + {"resourceType":"Parameters","parameter":[{"name":"patientNHSNumber","valueIdentifier":{"system":"https://fhir.nhs.uk/Id/nhs-number","value":"9690938118"}},{"name":"includeAllergies","part":[{"name":"includeResolvedAllergies","valueBoolean":true}]}]} + } + } + + response: { + headers: { + Transfer-Encoding: chunked + Connection: keep-alive + Strict-Transport-Security: max-age=31536000 + Cache-Control: no-store + Date: Mon, 15 Sep 2025 15:14:46 GMT + Location: https://{{environment}}/B82617/STU3/1/gpconnect/fhir/Bundle/369ae31c-8bfc-4df1-9861-1640c914c7f5 + Server: nginx + X-Powered-By: HAPI FHIR 3.0.0 REST Server (FHIR Server; FHIR 3.0.1/DSTU3) + Content-Type: application/fhir+json; charset=UTF-8 + } + + status: { + code: OK + text: 200 + } + body: { + type: text + content: ''' + {"resourceType":"Bundle","id":"369ae31c-8bfc-4df1-9861-1640c914c7f5","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/GPConnect-StructuredRecord-Bundle-1"]},"type":"collection","entry":[{"resource":{"resourceType":"Patient","id":"16","meta":{"versionId":"1521806400000","profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Patient-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-RegistrationDetails-1","extension":[{"url":"registrationPeriod","valuePeriod":{"start":"1962-07-13T00:00:00+01:00"}}]},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-NHSCommunication-1","extension":[{"url":"language","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-HumanLanguage-1","code":"en","display":"English"}]}},{"url":"preferred","valueBoolean":false},{"url":"modeOfCommunication","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-LanguageAbilityMode-1","code":"RWR","display":"Received written"}]}},{"url":"communicationProficiency","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-LanguageAbilityProficiency-1","code":"E","display":"Excellent"}]}},{"url":"interpreterRequired","valueBoolean":false}]}],"identifier":[{"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-NHSNumberVerificationStatus-1","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-NHSNumberVerificationStatus-1","code":"01","display":"Number present and verified"}]}}],"system":"https://fhir.nhs.uk/Id/nhs-number","value":"9690938118"}],"active":true,"name":[{"use":"official","text":"Sibyl CRAINE","family":"CRAINE","given":["Sibyl"],"prefix":["MRS"]}],"telecom":[{"system":"phone","value":"01454587554","use":"home"}],"gender":"female","birthDate":"1983-11-24","address":[{"use":"home","type":"physical","line":["1 LANGHAM WALK"],"city":"STOCKTON-ON-TEES","district":"CLEVELAND","postalCode":"TS19 7NX"}],"generalPractitioner":[{"reference":"Practitioner/1"}],"managingOrganization":{"reference":"Organization/7"}}},{"resource":{"resourceType":"List","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-List-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ListWarningCode-1","valueCode":"confidential-items"},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ListWarningCode-1","valueCode":"data-in-transit"},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ClinicalSetting-1","valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"1060971000000108","display":"General practice service"}]}}],"status":"current","mode":"snapshot","title":"Allergies and adverse reactions","code":{"coding":[{"system":"http://snomed.info/sct","code":"886921000000105","display":"Allergies and adverse reactions"}]},"subject":{"identifier":{"system":"https://fhir.nhs.uk/Id/nhs-number","value":"9690938118"}},"note":[{"text":"Items excluded due to confidentiality and/or patient preferences.\r\nPatient record transfer from previous GP practice not yet complete; information recorded before 08-Sep-2025 may be missing.\r\nInformation not available"}],"emptyReason":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-ListEmptyReasonCode-1","code":"no-content-recorded","display":"No Content Recorded"}]}}},{"resource":{"resourceType":"List","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-List-1"]},"contained":[{"resourceType":"AllergyIntolerance","id":"11","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-AllergyIntolerance-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-AllergyIntoleranceEnd-1","extension":[{"url":"endDate","valueDateTime":"2016-07-01T12:00:00+01:00"},{"url":"reasonEnded","valueString":"No information available"}]}],"identifier":[{"system":"https://fhir.nhs.uk/Id/cross-care-setting-identifier","value":"b6c0cd9c-8600-11f0-9e03-00505692d4aa"}],"clinicalStatus":"resolved","verificationStatus":"unconfirmed","category":["medication"],"code":{"coding":[{"system":"http://snomed.info/sct","code":"294716003","display":"Biphasic insulin allergy (disorder)"}]},"patient":{"reference":"Patient/16"},"onsetDateTime":"2016-05-01T12:00:00+01:00","assertedDate":"2016-06-01T12:00:00+01:00","recorder":{"reference":"Practitioner/5"},"lastOccurrence":"2016-07-01T12:00:00+01:00","note":[{"text":"Vomiting and diarrhoea"}],"reaction":[{"manifestation":[{"coding":[{"system":"http://snomed.info/sct","code":"49237006","display":"Allergic diarrhea (disorder)"}]}],"description":"Vomiting and diarrhoea"}]},{"resourceType":"AllergyIntolerance","id":"12","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-AllergyIntolerance-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-AllergyIntoleranceEnd-1","extension":[{"url":"endDate","valueDateTime":"2016-07-02T12:00:00+01:00"},{"url":"reasonEnded","valueString":"No information available"}]}],"identifier":[{"system":"https://fhir.nhs.uk/Id/cross-care-setting-identifier","value":"b6c0ce77-8600-11f0-9e03-00505692d4aa"}],"clinicalStatus":"resolved","verificationStatus":"unconfirmed","category":["medication"],"code":{"coding":[{"system":"http://snomed.info/sct","code":"294716003","display":"Biphasic insulin allergy (disorder)"}]},"patient":{"reference":"Patient/16"},"onsetDateTime":"2016-05-02T12:00:00+01:00","assertedDate":"2016-06-02T12:00:00+01:00","recorder":{"reference":"Practitioner/5"},"lastOccurrence":"2016-07-02T12:00:00+01:00","note":[{"text":"Vomiting and diarrhoea"}],"reaction":[{"manifestation":[{"coding":[{"system":"http://snomed.info/sct","code":"49237006","display":"Allergic diarrhea (disorder)"}]}],"description":"Vomiting and diarrhoea"}]}],"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ListWarningCode-1","valueCode":"confidential-items"},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ListWarningCode-1","valueCode":"data-in-transit"},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-ClinicalSetting-1","valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"1060971000000108","display":"General practice service"}]}}],"status":"current","mode":"snapshot","title":"Ended allergies","code":{"coding":[{"system":"http://snomed.info/sct","code":"1103671000000101","display":"Ended allergies"}]},"subject":{"identifier":{"system":"https://fhir.nhs.uk/Id/nhs-number","value":"9690938118"}},"note":[{"text":"Items excluded due to confidentiality and/or patient preferences.\r\nPatient record transfer from previous GP practice not yet complete; information recorded before 08-Sep-2025 may be missing."}],"entry":[{"item":{"reference":"#11"}},{"item":{"reference":"#12"}}]}},{"resource":{"resourceType":"Practitioner","id":"1","meta":{"versionId":"1469444400000","lastUpdated":"2016-07-25T12:00:00.000+01:00","profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Practitioner-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-NHSCommunication-1","extension":[{"url":"language","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-HumanLanguage-1","code":"de","display":"German"}]}}]},{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-NHSCommunication-1","extension":[{"url":"language","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-HumanLanguage-1","code":"en","display":"English"}]}}]}],"identifier":[{"system":"https://fhir.nhs.uk/Id/sds-user-id","value":"G13579135"}],"name":[{"use":"usual","family":"Gilbert","given":["Nichole"],"prefix":["Miss"]}],"gender":"female"}},{"resource":{"resourceType":"Practitioner","id":"5","meta":{"versionId":"1469444400000","lastUpdated":"2016-07-25T12:00:00.000+01:00","profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Practitioner-1"]},"extension":[{"url":"https://fhir.nhs.uk/STU3/StructureDefinition/Extension-CareConnect-GPC-NHSCommunication-1","extension":[{"url":"language","valueCodeableConcept":{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-HumanLanguage-1","code":"en","display":"English"}]}}]}],"identifier":[{"system":"https://fhir.nhs.uk/Id/sds-user-id","value":"G22222226"},{"system":"https://fhir.nhs.uk/Id/sds-role-profile-id","value":"PT2222"},{"system":"https://fhir.nhs.uk/Id/sds-role-profile-id","value":"PT4444"}],"name":[{"use":"usual","family":"Parsons","given":["Melissa"],"prefix":["Mrs"]}],"gender":"female"}},{"resource":{"resourceType":"PractitionerRole","id":"PT2222","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-PractitionerRole-1"]},"practitioner":{"reference":"Practitioner/5"},"organization":{"reference":"Organization/2"},"code":[{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-SDSJobRoleName-1","code":"R0042","display":"paediatrician"}]}]}},{"resource":{"resourceType":"PractitionerRole","id":"PT4444","meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-PractitionerRole-1"]},"practitioner":{"reference":"Practitioner/5"},"organization":{"reference":"Organization/2"},"code":[{"coding":[{"system":"https://fhir.nhs.uk/STU3/CodeSystem/CareConnect-SDSJobRoleName-1","code":"R0042","display":"paediatrician"}]}]}},{"resource":{"resourceType":"Organization","id":"2","meta":{"versionId":"1469444400000","lastUpdated":"2016-07-25T12:00:00.000+01:00","profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Organization-1"]},"identifier":[{"system":"https://fhir.nhs.uk/Id/ods-organization-code","value":"R1A14"}],"name":"Test GP Care Trust","telecom":[{"system":"phone","value":"12345678","use":"work"}],"address":[{"use":"work","line":["24 Back Lane","Farsley"],"city":"Leeds","district":"West Yorkshire","postalCode":"GPC 113"}]}},{"resource":{"resourceType":"Organization","id":"7","meta":{"versionId":"1469444400000","lastUpdated":"2016-07-25T12:00:00.000+01:00","profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Organization-1"]},"identifier":[{"system":"https://fhir.nhs.uk/Id/ods-organization-code","value":"B82617"}],"name":"COXWOLD SURGERY","telecom":[{"system":"phone","value":"12345678","use":"work"}],"address":[{"use":"work","line":["NHS NPFIT Test Data Manager","Princes Exchange"],"city":"Leeds","district":"West Yorkshire","postalCode":"LS1 4HY"}]}}]} + ''' + } + } +} diff --git a/gateway-api/tests/manual-test/api-test/api-gateway/Set-to-localhost.bru b/gateway-api/tests/manual-test/api-test/api-gateway/Set-to-localhost.bru new file mode 100644 index 0000000..8696f99 --- /dev/null +++ b/gateway-api/tests/manual-test/api-test/api-gateway/Set-to-localhost.bru @@ -0,0 +1,20 @@ +meta { + name: Set to localhost + type: http + seq: 4 +} + +get { + url: https://pokeapi.co/api/v2/pokemon/garchomp + body: none + auth: inherit +} + +script:pre-request { + bru.setEnvVar('environment', 'localhost:8080'); +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/gateway-api/tests/manual-test/api-test/api-gateway/bruno.json b/gateway-api/tests/manual-test/api-test/api-gateway/bruno.json new file mode 100644 index 0000000..e063363 --- /dev/null +++ b/gateway-api/tests/manual-test/api-test/api-gateway/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "api-gateway", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} diff --git a/gateway-api/tests/manual-test/api-test/api-gateway/mock-response.js b/gateway-api/tests/manual-test/api-test/api-gateway/mock-response.js new file mode 100644 index 0000000..f252f60 --- /dev/null +++ b/gateway-api/tests/manual-test/api-test/api-gateway/mock-response.js @@ -0,0 +1,62 @@ +const http = require('http'); + +const PORT = 8080; + +const RESPONSE_BODY = { + "resourceType":"Bundle", + "id":"369ae31c-8bfc-4df1-9861-1640c914c7f5", + "meta":{"profile":["https://fhir.nhs.uk/STU3/StructureDefinition/GPConnect-StructuredRecord-Bundle-1"]}, + "type":"collection", + "entry":[ + { + "resource":{ + "resourceType":"Patient", + "id":"16", + "meta":{ + "versionId":"1521806400000", + "profile":["https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-Patient-1"] + }, + "identifier":[ + { + "system":"https://fhir.nhs.uk/Id/nhs-number", + "value":"9690938118" + } + ], + "active":true, + "name":[ + { + "use":"official", + "text":"Sibyl CRAINE", + "family":"CRAINE", + "given":["Sibyl"], + "prefix":["MRS"] + } + ], + "gender":"female", + "birthDate":"1983-11-24" + } + } + ] +}; + + +const server = http.createServer((req, res) => { + if ( + req.method === 'POST' && + req.url === '/FHIR/STU3/patient/$gpc.getstructuredrecord' + ) { + res.writeHead(200, { + 'Content-Type': 'application/fhir+json' + }); + + res.end(JSON.stringify(RESPONSE_BODY, null, 2)); + return; + } + + res.writeHead(404); + res.end('Not Found'); +}); + +server.listen(PORT, () => { + console.log(`Mock Retrieve Patient Record server running at http://localhost:${PORT}`); +}); diff --git a/gateway-api/tests/manual-test/api-test/environments/local.bru b/gateway-api/tests/manual-test/api-test/environments/local.bru new file mode 100644 index 0000000..31fce92 --- /dev/null +++ b/gateway-api/tests/manual-test/api-test/environments/local.bru @@ -0,0 +1,3 @@ +vars { + environment: 127.0.0.1:8080 +} diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index e2b799f..031d70f 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -33,5 +33,6 @@ Syft Terraform toolchain Trufflehog +usebruno VMs [Vv]scode