From 02344902ad2ffc455e1b702749a6ac070f7af0ef Mon Sep 17 00:00:00 2001 From: Pete Loggie <158574875+pl-nhs@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:33:09 +0000 Subject: [PATCH] New Karate mock for retrieve patient and patch patient functionality (#994) * patch patient tests - successful patching scenarios * starts adding tests for patch error scenarios * more update scenarios implemented * invalid address ID test * adds more scnearios, moves some stubs around * adds final two patch error scenarios to feature * tidies up mock patch code * makes all tests like the original tests, gets everything working * fixes logic so that successful patch scenarios work * ignore sandbox tests in parallel runs for the moment * removes the get-patient-search.js file from the mock - not needed for this * removes comments, adds lines, cleans up * adds parallel runner for mock env; updates test for content headers - application/json should be allowed * adds karate sandbox tests to pipeline --- .gitignore | 1 + azure/azure-pr-pipeline.yml | 8 +- azure/templates/pds-tests-karate-sandbox.yml | 39 ++ .../src/test/java/mocks/MockRunner.java | 26 ++ .../test/java/mocks/get-patient-retrieve.js | 32 ++ .../src/test/java/mocks/patch-patient.js | 145 +++++++ karate-tests/src/test/java/mocks/sandbox.js | 138 ++++++ .../errorResponses/INVALID_RESOURCE_ID.json | 19 + .../stubs/errorResponses/INVALID_UPDATE.json | 20 + .../stubs/errorResponses/INVALID_VALUE.json | 20 + .../MISSING_VALUE_x-request-id.json | 20 + .../errorResponses/PRECONDITION_FAILED.json | 20 + .../errorResponses}/RESOURCE_NOT_FOUND.json | 0 .../errorResponses/UNSUPPORTED_SERVICE.json | 19 + .../patientResponses/patient_9000000009.json} | 0 .../patientResponses/patient_9000000025.json} | 0 .../patientResponses/patient_9000000033.json | 35 ++ .../patientResponses}/patient_9693632109.json | 0 .../searchResponses/Minimal_Patient.json | 35 ++ .../Minimal_PatientSearch.json | 49 +++ .../searchResponses/Patient-Jayne-Smyth.json | 393 ++++++++++++++++++ .../mocks/stubs/searchResponses/Patient.json | 393 ++++++++++++++++++ .../searchResponses/PatientCompoundName.json | 231 ++++++++++ .../PatientSearch-Jayne-Smyth.json | 231 ++++++++++ .../stubs/searchResponses/PatientSearch.json | 231 ++++++++++ .../searchResponses/Sensitive_Patient.json | 83 ++++ .../Sensitive_PatientSearch.json | 97 +++++ .../test/java/patients/TestMockParallel.java | 34 ++ .../src/test/java/patients/TestParallel.java | 4 + .../MockPatchPatientTests.java | 29 ++ .../healthcareWorker/patchPatient.feature | 74 +++- .../patchPatientErrors.feature | 258 ++++++++++++ .../healthcareWorker/postPatient.feature | 8 +- 33 files changed, 2674 insertions(+), 18 deletions(-) create mode 100644 azure/templates/pds-tests-karate-sandbox.yml create mode 100644 karate-tests/src/test/java/mocks/MockRunner.java create mode 100644 karate-tests/src/test/java/mocks/get-patient-retrieve.js create mode 100644 karate-tests/src/test/java/mocks/patch-patient.js create mode 100644 karate-tests/src/test/java/mocks/sandbox.js create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_UPDATE.json create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_VALUE.json create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/PRECONDITION_FAILED.json rename karate-tests/src/test/java/{stubs/oldSandbox/errors => mocks/stubs/errorResponses}/RESOURCE_NOT_FOUND.json (100%) create mode 100644 karate-tests/src/test/java/mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json rename karate-tests/src/test/java/{stubs/patient/sandbox-patient.json => mocks/stubs/patientResponses/patient_9000000009.json} (100%) rename karate-tests/src/test/java/{stubs/oldSandbox/sensitive-patient.json => mocks/stubs/patientResponses/patient_9000000025.json} (100%) create mode 100644 karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000033.json rename karate-tests/src/test/java/{stubs/patient => mocks/stubs/patientResponses}/patient_9693632109.json (100%) create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_Patient.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_PatientSearch.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Patient-Jayne-Smyth.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Patient.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/PatientCompoundName.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch-Jayne-Smyth.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_Patient.json create mode 100644 karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_PatientSearch.json create mode 100644 karate-tests/src/test/java/patients/TestMockParallel.java create mode 100644 karate-tests/src/test/java/patients/healthcareWorker/MockPatchPatientTests.java create mode 100644 karate-tests/src/test/java/patients/healthcareWorker/patchPatientErrors.feature diff --git a/.gitignore b/.gitignore index 8e847ec94..b6f6235ae 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ keys/ karate-tests/target/ +karate-tests/src/test/java/target/ diff --git a/azure/azure-pr-pipeline.yml b/azure/azure-pr-pipeline.yml index a027253c7..9c4d08f02 100644 --- a/azure/azure-pr-pipeline.yml +++ b/azure/azure-pr-pipeline.yml @@ -47,6 +47,12 @@ extends: - template: templates/pds-tests-karate.yml depends_on: - pytest_bdd_tests + - environment: internal-dev + stage_name: karate_sandbox_tests + post_deploy: + - template: templates/pds-tests-karate-sandbox.yml + depends_on: + - karate_tests - environment: internal-dev service_name: ${{ variables.service_name }}-asid-required short_service_name: ${{ variables.short_service_name }}-asid @@ -58,7 +64,7 @@ extends: post_deploy: - template: templates/pds-tests.yml depends_on: - - karate_tests + - karate_sandbox_tests - environment: internal-dev-sandbox proxy_path: sandbox diff --git a/azure/templates/pds-tests-karate-sandbox.yml b/azure/templates/pds-tests-karate-sandbox.yml new file mode 100644 index 000000000..59c85e831 --- /dev/null +++ b/azure/templates/pds-tests-karate-sandbox.yml @@ -0,0 +1,39 @@ +steps: + + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + + - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" + parameters: + config_ids: + - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID + secret_ids: + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_API_KEY + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_WITH_ASID_API_KEY + - ptl/backends/ig3/INTERNAL_DEV_ASID + + - bash: | + echo '##vso[task.setvariable variable=CLIENT_ID]$(CLIENT_ID)' + displayName: Expose common variables + + - bash: | + export CLIENT_ID="$(CLIENT_ID)" + mvn clean test -Dtest=TestMockParallel + displayName: 'Run Karate Sandbox Tests' + workingDirectory: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/karate-tests" + + - task: PublishTestResults@2 + displayName: 'Publish Karate sandbox test results' + condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed') + inputs: + testResultsFiles: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/karate-tests/target/karate-reports/*.xml + failTaskOnFailedTests: true + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Karate Sandbox HTML test report as an artifact' + condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed') + inputs: + pathToPublish: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/karate-tests/target/karate-reports + artifactName: KarateSandboxHTMLReports \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/MockRunner.java b/karate-tests/src/test/java/mocks/MockRunner.java new file mode 100644 index 000000000..b4f13da9b --- /dev/null +++ b/karate-tests/src/test/java/mocks/MockRunner.java @@ -0,0 +1,26 @@ +package mocks; + + +import com.intuit.karate.http.HttpServer; +import com.intuit.karate.http.ServerConfig; +import com.intuit.karate.http.ServerContext; + + +public class MockRunner { + + public static HttpServer start(String root, int port) { + ServerConfig config = new ServerConfig(root) + .useGlobalSession(true); + config.contextFactory(request -> { + ServerContext context = new ServerContext(config, request); + context.setApi(true); + request.setResourcePath("sandbox.js"); + return context; + }); + return HttpServer.config(config) + .http(port) + .corsEnabled(true) + .build(); + } + +} \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/get-patient-retrieve.js b/karate-tests/src/test/java/mocks/get-patient-retrieve.js new file mode 100644 index 000000000..ca314f3ff --- /dev/null +++ b/karate-tests/src/test/java/mocks/get-patient-retrieve.js @@ -0,0 +1,32 @@ +/* + Our patients "database" for the get by NHS Number requests :-) +*/ +session.patients = session.patients || { + '9000000009': context.read('classpath:mocks/stubs/patientResponses/patient_9000000009.json'), + '9000000025': context.read('classpath:mocks/stubs/patientResponses/patient_9000000025.json'), + '9000000033': context.read('classpath:mocks/stubs/patientResponses/patient_9000000033.json'), + '9693632109': context.read('classpath:mocks/stubs/patientResponses/patient_9693632109.json') +} + +/* + Handler for get patient by NHS Number +*/ +if (request.pathMatches('/Patient/{nhsNumber}') && request.get) { + let valid = validateHeaders(request) && validateNHSNumber(request) ; + + if (valid) { + const nhsNumber = request.pathParams.nhsNumber; + if (typeof session.patients[nhsNumber] == 'undefined') { + response.body = context.read('classpath:mocks/stubs/errorResponses/RESOURCE_NOT_FOUND.json'); + response.headers = basicResponseHeaders(request) + response.status = 404 + } else { + patient = session.patients[nhsNumber] + let responseHeaders = basicResponseHeaders(request) + responseHeaders['etag'] = `W/"${patient.meta.versionId}"` + response.body = patient; + response.headers = responseHeaders; + response.status = 200; + } + } +} diff --git a/karate-tests/src/test/java/mocks/patch-patient.js b/karate-tests/src/test/java/mocks/patch-patient.js new file mode 100644 index 000000000..649ecc212 --- /dev/null +++ b/karate-tests/src/test/java/mocks/patch-patient.js @@ -0,0 +1,145 @@ +function buildResponseHeaders(request, patient) { + return { + 'content-type': 'application/fhir+json', + 'etag': `W/"${patient.meta.versionId}"`, + 'x-request-id': request.header('x-request-id'), + 'x-correlation-id': request.header('x-correlation-id') + }; +} + +/* + Diagnostics strings for error messages +*/ +const NO_IF_MATCH_HEADER = "Invalid update with error - If-Match header must be supplied to update this resource"; +const NO_PATCHES_PROVIDED = "Invalid update with error - No patches found"; +const INVALID_RESOURCE_ID = "Invalid update with error - This resource has changed since you last read. Please re-read and try again with the new version number."; +const INVALID_PATCH = "Invalid patch: Operation `op` property is not one of operations defined in RFC-6902" + +/* + Functions to handle error responses +*/ +function invalidUpdateError(request, diagnostics) { + let body = context.read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json'); + body.issue[0].diagnostics = diagnostics; + response.headers = basicResponseHeaders(request); + response.body = body; + response.status = 400; + return false +} + +function preconditionFailedError(request, diagnostics) { + let body = context.read('classpath:mocks/stubs/errorResponses/PRECONDITION_FAILED.json'); + body.issue[0].diagnostics = diagnostics; + response.headers = basicResponseHeaders(request); + response.body = body; + response.status = 412; + return false +} + +function unsupportedServiceError(request) { + let body = context.read('classpath:mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json'); + response.headers = basicResponseHeaders(request); + response.body = body; + response.status = 400; + return false +} + +/* + Validate the headers specific to patching a patient +*/ +function validatePatchHeaders(request) { + var valid = true; + if (!request.header('if-match')) { + valid = preconditionFailedError(request, NO_IF_MATCH_HEADER) + } + if (valid && !request.header('content-type').startsWith('application/json')) { + valid = unsupportedServiceError(request) + } + return valid +} + +/* + The main logic for patching a patient +*/ +function patchPatient(originalPatient, request) { + + if (!request.body.patches) { + return invalidUpdateError(request, NO_PATCHES_PROVIDED); + } + if (request.header('if-match') != `W/"${originalPatient.meta.versionId}"`) { + return preconditionFailedError(request, INVALID_RESOURCE_ID); + } + + let updatedPatient = JSON.parse(JSON.stringify(originalPatient)); + let updateErrors = []; + + const validOperations = ['add', 'replace', 'remove', 'test'] + for(let i = 0; i < request.body.patches.length; i++) { + let patch = request.body.patches[i]; + if (!validOperations.includes(patch.op)) { + return invalidUpdateError(request, INVALID_PATCH) + } + if (patch.op == 'add' && patch.path === '/name/-') { + updatedPatient.name.push(patch.value); + } + if (patch.op == 'replace' && patch.path === '/name/0/given/0') { + updatedPatient.name[0].given[0] = patch.value; + } + if (patch.op == 'replace' && patch.path === '/gender') { + updatedPatient.gender = patch.value; + } + if (patch.op == 'remove' && patch.path === '/name/0/suffix/0') { + updatedPatient.name[0].suffix.splice(0, 1); + } + + // these specific error scenarios for update errors should be reviewed + if (patch.op == 'replace' && patch.path === "/address/0/line/0" && patch.value === "2 Whitehall Quay") { + updateErrors.push("Invalid update with error - no id or url found for path with root /address/0"); + } else if (patch.op == 'replace' && patch.path.startsWith("/address/0/") && !originalPatient.hasOwnProperty('address')) { + updateErrors.push("Invalid update with error - Invalid patch - index '0' is out of bounds"); + } else if (patch.op == 'replace' && patch.path === "/address/0/id" && patch.value === "456") { + updateErrors.push("Invalid update with error - no 'address' resources with object id 456"); + } else if (patch.op == 'replace' && patch.path === "/address/0/line") { + updateErrors.push("Invalid update with error - Invalid patch - can't replace non-existent object 'line'"); + } else if (patch.op == 'replace' && patch.path === "/address/0/id" && patch.value === "123456") { + updateErrors.push("Invalid update with error - no 'address' resources with object id 123456"); + } + } + + // why is it that for this specific scenario (Invalid patch - attempt to replace non-existent object), + // we have to pick the last error message, when for all the others we pick the first error message? + const rogueErrors = [ + "Invalid update with error - no 'address' resources with object id 456", + "Invalid update with error - Invalid patch - can't replace non-existent object 'line'" + ] + + if (updateErrors.length > 0) { + if (updateErrors.every(item => rogueErrors.includes(item)) && rogueErrors.every(item => updateErrors.includes(item))) { + return invalidUpdateError(request, updateErrors[1]) + } else { + return invalidUpdateError(request, updateErrors[0]) + } + } else { + updatedPatient.meta.versionId = (parseInt(updatedPatient.meta.versionId) + 1); + return updatedPatient; + } +} + +/* + Handler for patch patient +*/ +if (request.pathMatches('/Patient/{nhsNumber}') && request.patch) { + const valid = validateNHSNumber(request) && validatePatchHeaders(request) && validateHeaders(request) + if (valid) { + const nhsNumber = request.pathParams.nhsNumber; + const originalPatient = session.patients[nhsNumber]; + let updatedPatient = patchPatient(originalPatient, request); + if (updatedPatient) { + // this line is commented out because the existing tests assume a stateless mock + // session.patients[nhsNumber] = updatedPatient; + response.headers = buildResponseHeaders(request, updatedPatient); + response.body = updatedPatient; + response.status = 200; + } + } +} diff --git a/karate-tests/src/test/java/mocks/sandbox.js b/karate-tests/src/test/java/mocks/sandbox.js new file mode 100644 index 000000000..0d2ae366a --- /dev/null +++ b/karate-tests/src/test/java/mocks/sandbox.js @@ -0,0 +1,138 @@ +/* + Common constants +*/ +const X_REQUEST_ID = "X-Request-ID"; + +/* + Supporting functions +*/ +// reading this file gives us the `validate` function for validating NHS numbers +context.read('classpath:helpers/nhs-number-validator.js'); + +function getTimestampedBody(pathToBody) { + let body = context.read(pathToBody) + body['timestamp'] = new Date().toISOString() + return body +} + +function isValidUUID(uuid) { + const regex = new RegExp("[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}") + return regex.test(uuid) +} + + +function getParam(request, paramName) { + // Get the value of the parameter, and convert it to lowercase if it's a string + value = request.params[paramName] + if (typeof value !== 'string') { + return value + } else { + return value.toLowerCase() + } +} + +function basicResponseHeaders(request) { + return { + 'content-type': 'application/fhir+json', + 'x-request-id': request.headers['x-request-id'], + 'x-correlation-id': request.headers['x-correlation-id'] + } +}; + +/* + Validate the headers +*/ +function validateHeaders(request) { + var valid = true; + + const requestID = request.header('x-request-id') + if (!requestID) { + response.body = context.read('classpath:mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json') + const headers = basicResponseHeaders(request) + response.headers = headers + response.status = 400 + valid = false + } else if (!isValidUUID(requestID)) { + valid = invalidValueError(X_REQUEST_ID, requestID, request) + } + return valid +} + + +function validateNHSNumber(request) { + const nhsNumber = request.pathParams.nhsNumber; + let valid = true; + let validNHSNumber = validate(nhsNumber) + if (!validNHSNumber) { + valid = false + response.headers = basicResponseHeaders(request) + response.body = context.read('classpath:mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json'); + response.status = 400 + } else if (typeof session.patients[nhsNumber] == 'undefined') { + valid = false + response.headers = basicResponseHeaders(request) + response.body = context.read('classpath:mocks/stubs/errorResponses/RESOURCE_NOT_FOUND.json'); + response.status = 404 + } + return valid; +} + + +/* + Common error response handlers +*/ +function invalidValueError(field, value, request) { + + let body = context.read('classpath:mocks/stubs/errorResponses/INVALID_VALUE.json'); + body.issue[0].diagnostics = `Invalid value - '${value}' in header '${field}'`; + response.body = body; + response.headers = basicResponseHeaders(request); + response.status = 400; + return false +} + + +/* + Response definitions +*/ +function returnBundle(filename) { + response.body = getTimestampedBody(`classpath:mocks/stubs/patientResponses/${filename}`) + response.status = 200 +} + + +function returnEmptyBundle() { + response.body = getTimestampedBody('classpath:mocks/stubs/patientResponses/empty_bundle.json') + response.status = 200 +} + + +function returnMissingValueError(diagnostics) { + let body = context.read('classpath:mocks/stubs/patient/errorResponses/missing_value.json') + body["issue"][0]["diagnostics"] = diagnostics + response.body = body + response.status = 400 +} + + +function returnTooManyMatchesError() { + response.body = context.read('classpath:mocks/stubs/patient/errorResponses/too_many_matches.json') + response.status = 400 +} + + +function validateDate(dateString, field) { + const regex=new RegExp("([0-9]{4}[-](0[1-9]|1[0-2])[-]([0-2]{1}[0-9]{1}|3[0-1]{1})|([0-2]{1}[0-9]{1}|3[0-1]{1})[-](0[1-9]|1[0-2])[-][0-9]{4})") + const valid = regex.test(dateString) + if (!valid) { + const diagnostics = `Invalid value - '${dateString}' in field '${field}'` + const body = context.read('classpath:stubs/patient/errorResponses/invalid_search_data.json') + body['issue'][0]['diagnostics'] = diagnostics + response.body = body + response.status = 400 + } +} + + +context.read('classpath:mocks/get-patient-retrieve.js'); +context.read('classpath:mocks/patch-patient.js'); \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json new file mode 100644 index 000000000..3ac8b63ea --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json @@ -0,0 +1,19 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "value", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "INVALID_RESOURCE_ID", + "display": "Resource Id is invalid" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_UPDATE.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_UPDATE.json new file mode 100644 index 000000000..5a9f91407 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_UPDATE.json @@ -0,0 +1,20 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "structure", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "INVALID_UPDATE", + "display": "Update is invalid" + } + ] + }, + "diagnostics": "#(diagnostics)" + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_VALUE.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_VALUE.json new file mode 100644 index 000000000..c33d501a9 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/INVALID_VALUE.json @@ -0,0 +1,20 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "value", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "INVALID_VALUE", + "display": "Provided value is invalid" + } + ] + }, + "diagnostics": "#(diagnostics)" + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json new file mode 100644 index 000000000..8bcaf2e99 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json @@ -0,0 +1,20 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "required", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "MISSING_VALUE", + "display": "Required value is missing" + } + ] + }, + "diagnostics": "Invalid request with error - X-Request-ID header must be supplied to access this resource" + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/PRECONDITION_FAILED.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/PRECONDITION_FAILED.json new file mode 100644 index 000000000..ebadaf8dd --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/PRECONDITION_FAILED.json @@ -0,0 +1,20 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "structure", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "PRECONDITION_FAILED", + "display": "Required condition was not fulfilled" + } + ] + }, + "diagnostics": "#(diagnostics)" + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/stubs/oldSandbox/errors/RESOURCE_NOT_FOUND.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/RESOURCE_NOT_FOUND.json similarity index 100% rename from karate-tests/src/test/java/stubs/oldSandbox/errors/RESOURCE_NOT_FOUND.json rename to karate-tests/src/test/java/mocks/stubs/errorResponses/RESOURCE_NOT_FOUND.json diff --git a/karate-tests/src/test/java/mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json b/karate-tests/src/test/java/mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json new file mode 100644 index 000000000..942b17517 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json @@ -0,0 +1,19 @@ +{ + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "error", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/R4/CodeSystem/Spine-ErrorOrWarningCode", + "version": "1", + "code": "UNSUPPORTED_SERVICE", + "display": "Unsupported Service" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/karate-tests/src/test/java/stubs/patient/sandbox-patient.json b/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000009.json similarity index 100% rename from karate-tests/src/test/java/stubs/patient/sandbox-patient.json rename to karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000009.json diff --git a/karate-tests/src/test/java/stubs/oldSandbox/sensitive-patient.json b/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000025.json similarity index 100% rename from karate-tests/src/test/java/stubs/oldSandbox/sensitive-patient.json rename to karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000025.json diff --git a/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000033.json b/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000033.json new file mode 100644 index 000000000..f5ab771b8 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9000000033.json @@ -0,0 +1,35 @@ +{ + "resourceType": "Patient", + "id": "9000000033", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000033", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + } +} \ No newline at end of file diff --git a/karate-tests/src/test/java/stubs/patient/patient_9693632109.json b/karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9693632109.json similarity index 100% rename from karate-tests/src/test/java/stubs/patient/patient_9693632109.json rename to karate-tests/src/test/java/mocks/stubs/patientResponses/patient_9693632109.json diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_Patient.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_Patient.json new file mode 100644 index 000000000..3185c25ea --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_Patient.json @@ -0,0 +1,35 @@ +{ + "resourceType": "Patient", + "id": "9000000033", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000033", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + } +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_PatientSearch.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_PatientSearch.json new file mode 100644 index 000000000..607e240bc --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Minimal_PatientSearch.json @@ -0,0 +1,49 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "timestamp": "2019-12-25T12:00:00+00:00", + "total": 1, + "entry": [ + { + "fullUrl": "https://api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/9000000033", + "search": { + "score": 0.75 + }, + "resource": { + "resourceType": "Patient", + "id": "9000000033", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000033", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + } + } + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient-Jayne-Smyth.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient-Jayne-Smyth.json new file mode 100644 index 000000000..d15c184f2 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient-Jayne-Smyth.json @@ -0,0 +1,393 @@ +{ + "resourceType": "Patient", + "id": "9000000017", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000017", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Jayne" + ], + "family": "Smyth", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "generalPractitioner": [ + { + "id": "254406A3", + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + } + ], + "managingOrganization": { + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NominatedPharmacy", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-PreferredDispenserOrganization", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y23456" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-MedicalApplianceSupplier", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y34567" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSCommunication", + "extension": [ + { + "url": "language", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-HumanLanguage", + "version": "1.0.0", + "code": "fr", + "display": "French" + } + ] + } + }, + { + "url": "interpreterRequired", + "valueBoolean": true + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-ContactPreference", + "extension": [ + { + "url": "PreferredWrittenCommunicationFormat", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-PreferredWrittenCommunicationFormat", + "code": "12", + "display": "Braille" + } + ] + } + }, + { + "url": "PreferredContactMethod", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-PreferredContactMethod", + "code": "1", + "display": "Letter" + } + ] + } + }, + { + "url": "PreferredContactTimes", + "valueString": "Not after 7pm" + } + ] + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-birthPlace", + "valueAddress": { + "city": "Manchester", + "district": "Greater Manchester", + "country": "GBR" + } + }, + { + "url": "https://fhir.nhs.uk/StructureDefinition/Extension-PDS-RemovalFromRegistration", + "extension": [ + { + "url": "removalFromRegistrationCode", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/CodeSystem/PDS-RemovalReasonExitCode", + "code": "SCT", + "display": "Transferred to Scotland" + } + ] + } + }, + { + "url": "effectiveTime", + "valuePeriod": { + "start": "2020-01-01T00:00:00+00:00", + "end": "2021-12-31T00:00:00+00:00" + } + } + ] + } + ], + "telecom": [ + { + "id": "789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "phone", + "value": "01632960587", + "use": "home" + }, + { + "id": "790", + "period": { + "start": "2019-01-01", + "end": "2022-12-31" + }, + "system": "email", + "value": "jayne.smyth@example.com", + "use": "home" + }, + { + "id": "OC789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "other", + "value": "01632960587", + "use": "home", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-OtherContactSystem", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-OtherContactSystem", + "code": "textphone", + "display": "Minicom (Textphone)" + } + } + ] + } + ], + "contact": [ + { + "id": "C123", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "C", + "display": "Emergency Contact" + } + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "01632960587" + } + ] + } + ], + "address": [ + { + "id": "456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "home", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + }, + { + "id": "T456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "temp", + "text": "Student Accommodation", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient.json new file mode 100644 index 000000000..5e5f0a33e --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Patient.json @@ -0,0 +1,393 @@ +{ + "resourceType": "Patient", + "id": "9000000009", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000009", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Jane" + ], + "family": "Smith", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "generalPractitioner": [ + { + "id": "254406A3", + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + } + ], + "managingOrganization": { + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NominatedPharmacy", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-PreferredDispenserOrganization", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y23456" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-MedicalApplianceSupplier", + "valueReference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y34567" + } + } + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSCommunication", + "extension": [ + { + "url": "language", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-HumanLanguage", + "version": "1.0.0", + "code": "fr", + "display": "French" + } + ] + } + }, + { + "url": "interpreterRequired", + "valueBoolean": true + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-ContactPreference", + "extension": [ + { + "url": "PreferredWrittenCommunicationFormat", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-PreferredWrittenCommunicationFormat", + "code": "12", + "display": "Braille" + } + ] + } + }, + { + "url": "PreferredContactMethod", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-PreferredContactMethod", + "code": "1", + "display": "Letter" + } + ] + } + }, + { + "url": "PreferredContactTimes", + "valueString": "Not after 7pm" + } + ] + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-birthPlace", + "valueAddress": { + "city": "Manchester", + "district": "Greater Manchester", + "country": "GBR" + } + }, + { + "url": "https://fhir.nhs.uk/StructureDefinition/Extension-PDS-RemovalFromRegistration", + "extension": [ + { + "url": "removalFromRegistrationCode", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/CodeSystem/PDS-RemovalReasonExitCode", + "code": "SCT", + "display": "Transferred to Scotland" + } + ] + } + }, + { + "url": "effectiveTime", + "valuePeriod": { + "start": "2020-01-01T00:00:00+00:00", + "end": "2021-12-31T00:00:00+00:00" + } + } + ] + } + ], + "telecom": [ + { + "id": "789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "phone", + "value": "01632960587", + "use": "home" + }, + { + "id": "790", + "period": { + "start": "2019-01-01", + "end": "2022-12-31" + }, + "system": "email", + "value": "jane.smith@example.com", + "use": "home" + }, + { + "id": "OC789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "other", + "value": "01632960587", + "use": "home", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-OtherContactSystem", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-OtherContactSystem", + "code": "textphone", + "display": "Minicom (Textphone)" + } + } + ] + } + ], + "contact": [ + { + "id": "C123", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "C", + "display": "Emergency Contact" + } + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "01632960587" + } + ] + } + ], + "address": [ + { + "id": "456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "home", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + }, + { + "id": "T456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "temp", + "text": "Student Accommodation", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientCompoundName.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientCompoundName.json new file mode 100644 index 000000000..eea76721f --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientCompoundName.json @@ -0,0 +1,231 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "timestamp": "2019-12-25T12:00:00+00:00", + "total": 1, + "entry": [ + { + "fullUrl": "https://api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/9000000015", + "search": { + "score": 0.75 + }, + "resource": { + "resourceType": "Patient", + "id": "9000000015", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000015", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "John Paul" + ], + "family": "Smith", + "prefix": [ + "Mr" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "male", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "address": [ + { + "id": "456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "home", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + } + ], + "telecom": [ + { + "id": "789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "phone", + "value": "01632960587", + "use": "home" + }, + { + "id": "790", + "period": { + "start": "2019-01-01", + "end": "2022-12-31" + }, + "system": "email", + "value": "johnp.smith@example.com", + "use": "home" + }, + { + "id": "OC789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "other", + "value": "01632960587", + "use": "home", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-OtherContactSystem", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-OtherContactSystem", + "code": "textphone", + "display": "Minicom (Textphone)" + } + } + ] + } + ], + "contact": [ + { + "id": "C123", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "C", + "display": "Emergency Contact" + } + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "01632960587" + } + ] + } + ], + "generalPractitioner": [ + { + "id": "254406A3", + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + } + ], + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + } + ] + } + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch-Jayne-Smyth.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch-Jayne-Smyth.json new file mode 100644 index 000000000..6cc34ec2c --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch-Jayne-Smyth.json @@ -0,0 +1,231 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "timestamp": "2019-12-25T12:00:00+00:00", + "total": 1, + "entry": [ + { + "fullUrl": "https://api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/9000000017", + "search": { + "score": 0.75 + }, + "resource": { + "resourceType": "Patient", + "id": "9000000017", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000017", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Jayne" + ], + "family": "Smyth", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "address": [ + { + "id": "456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "home", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + } + ], + "telecom": [ + { + "id": "789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "phone", + "value": "01632960587", + "use": "home" + }, + { + "id": "790", + "period": { + "start": "2019-01-01", + "end": "2022-12-31" + }, + "system": "email", + "value": "jayne.smyth@example.com", + "use": "home" + }, + { + "id": "OC789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "other", + "value": "01632960587", + "use": "home", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-OtherContactSystem", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-OtherContactSystem", + "code": "textphone", + "display": "Minicom (Textphone)" + } + } + ] + } + ], + "contact": [ + { + "id": "C123", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "C", + "display": "Emergency Contact" + } + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "01632960587" + } + ] + } + ], + "generalPractitioner": [ + { + "id": "254406A3", + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + } + ], + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + } + ] + } + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch.json new file mode 100644 index 000000000..ba42bb77a --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/PatientSearch.json @@ -0,0 +1,231 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "timestamp": "2019-12-25T12:00:00+00:00", + "total": 1, + "entry": [ + { + "fullUrl": "https://api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/9000000009", + "search": { + "score": 0.75 + }, + "resource": { + "resourceType": "Patient", + "id": "9000000009", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000009", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "U", + "display": "unrestricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Jane" + ], + "family": "Smith", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "address": [ + { + "id": "456", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "use": "home", + "line": [ + "1 Trevelyan Square", + "Boar Lane", + "City Centre", + "Leeds", + "West Yorkshire" + ], + "postalCode": "LS1 6AE", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "PAF" + } + }, + { + "url": "value", + "valueString": "12345678" + } + ] + }, + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-AddressKey", + "extension": [ + { + "url": "type", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-AddressKeyType", + "code": "UPRN" + } + }, + { + "url": "value", + "valueString": "123456789012" + } + ] + } + ] + } + ], + "telecom": [ + { + "id": "789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "phone", + "value": "01632960587", + "use": "home" + }, + { + "id": "790", + "period": { + "start": "2019-01-01", + "end": "2022-12-31" + }, + "system": "email", + "value": "jane.smith@example.com", + "use": "home" + }, + { + "id": "OC789", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "system": "other", + "value": "01632960587", + "use": "home", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-OtherContactSystem", + "valueCoding": { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-OtherContactSystem", + "code": "textphone", + "display": "Minicom (Textphone)" + } + } + ] + } + ], + "contact": [ + { + "id": "C123", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "C", + "display": "Emergency Contact" + } + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "01632960587" + } + ] + } + ], + "generalPractitioner": [ + { + "id": "254406A3", + "type": "Organization", + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + } + } + } + ], + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + } + ] + } + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_Patient.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_Patient.json new file mode 100644 index 000000000..0c2ae36f7 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_Patient.json @@ -0,0 +1,83 @@ +{ + "resourceType": "Patient", + "id": "9000000025", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000025", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "R", + "display": "restricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Janet" + ], + "family": "Smythe", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2010-10-22", + "multipleBirthInteger": 1, + "deceasedDateTime": "2010-10-22T00:00:00+00:00", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2010-10-22T00:00:00+00:00" + } + ] + } + ] +} diff --git a/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_PatientSearch.json b/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_PatientSearch.json new file mode 100644 index 000000000..413f576d4 --- /dev/null +++ b/karate-tests/src/test/java/mocks/stubs/searchResponses/Sensitive_PatientSearch.json @@ -0,0 +1,97 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "timestamp": "2019-12-25T12:00:00+00:00", + "total": 1, + "entry": [ + { + "fullUrl": "https://api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/9000000025", + "search": { + "score": 0.75 + }, + "resource": { + "resourceType": "Patient", + "id": "9000000025", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000025", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-NHSNumberVerificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-NHSNumberVerificationStatus", + "version": "1.0.0", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ] + } + ], + "meta": { + "versionId": "2", + "security": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", + "code": "R", + "display": "restricted" + } + ] + }, + "name": [ + { + "id": "123", + "use": "usual", + "period": { + "start": "2020-01-01", + "end": "2021-12-31" + }, + "given": [ + "Janet" + ], + "family": "Smythe", + "prefix": [ + "Mrs" + ], + "suffix": [ + "MBE" + ] + } + ], + "gender": "female", + "birthDate": "2005-06-16", + "multipleBirthInteger": 1, + "deceasedDateTime": "2005-06-16T00:00:00+00:00", + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-DeathNotificationStatus", + "extension": [ + { + "url": "deathNotificationStatus", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-DeathNotificationStatus", + "version": "1.0.0", + "code": "2", + "display": "Formal - death notice received from Registrar of Deaths" + } + ] + } + }, + { + "url": "systemEffectiveDate", + "valueDateTime": "2005-06-16T00:00:00+00:00" + } + ] + } + ] + } + } + ] +} diff --git a/karate-tests/src/test/java/patients/TestMockParallel.java b/karate-tests/src/test/java/patients/TestMockParallel.java new file mode 100644 index 000000000..02961f8b4 --- /dev/null +++ b/karate-tests/src/test/java/patients/TestMockParallel.java @@ -0,0 +1,34 @@ +package patients; + +import com.intuit.karate.Results; +import com.intuit.karate.Runner; +import com.intuit.karate.http.HttpServer; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import mocks.MockRunner; + + +public class TestMockParallel { + + static HttpServer server; + + @BeforeAll + static void beforeAll() { + server = MockRunner.start("src/test/java/mocks", 8080); + } + + @Test + void testMockParallel() { + Results results = Runner.path("classpath:patients") + .tags("@sandbox") + .outputJunitXml(true) + .karateEnv("mock") + .parallel(5); + assertTrue(results.getFailCount() == 0, results.getErrorMessages()); + } + +} \ No newline at end of file diff --git a/karate-tests/src/test/java/patients/TestParallel.java b/karate-tests/src/test/java/patients/TestParallel.java index 16c6ccb14..769549b56 100644 --- a/karate-tests/src/test/java/patients/TestParallel.java +++ b/karate-tests/src/test/java/patients/TestParallel.java @@ -2,7 +2,9 @@ import com.intuit.karate.Results; import com.intuit.karate.Runner; + import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.Test; @@ -11,10 +13,12 @@ public class TestParallel { @Test void testDevParallel() { Results results = Runner.path("classpath:patients") + .tags("~@sandbox") .outputJunitXml(true) .karateEnv("dev") .parallel(5); assertTrue(results.getFailCount() == 0, results.getErrorMessages()); } + } \ No newline at end of file diff --git a/karate-tests/src/test/java/patients/healthcareWorker/MockPatchPatientTests.java b/karate-tests/src/test/java/patients/healthcareWorker/MockPatchPatientTests.java new file mode 100644 index 000000000..f2517e746 --- /dev/null +++ b/karate-tests/src/test/java/patients/healthcareWorker/MockPatchPatientTests.java @@ -0,0 +1,29 @@ +package patients.healthcareWorker; + +import org.junit.jupiter.api.BeforeAll; + +import com.intuit.karate.http.HttpServer; +import com.intuit.karate.junit5.Karate; + +import mocks.MockRunner; + +public class MockPatchPatientTests { + + static HttpServer server; + + @BeforeAll + static void beforeAll() { + server = MockRunner.start("src/test/java/mocks", 8080); + } + + @Karate.Test + Karate testPatchPatient() { + return Karate.run("patchPatient").relativeTo(getClass()); + } + + @Karate.Test + Karate testPatchErrors() { + return Karate.run("patchPatientErrors").relativeTo(getClass()); + } + +} diff --git a/karate-tests/src/test/java/patients/healthcareWorker/patchPatient.feature b/karate-tests/src/test/java/patients/healthcareWorker/patchPatient.feature index c831bebdc..9275fe432 100644 --- a/karate-tests/src/test/java/patients/healthcareWorker/patchPatient.feature +++ b/karate-tests/src/test/java/patients/healthcareWorker/patchPatient.feature @@ -1,12 +1,18 @@ +@sandbox Feature: Patch patient - Healthcare worker access mode + # These tests are only run against the sandbox at the moment - until we have a better + # way of managing test data in the integration environment + Background: * def utils = call read('classpath:helpers/utils.feature') + * def accessToken = karate.callSingle('classpath:patients/healthcareWorker/auth-redirect.feature').accessToken * def requestHeaders = call read('classpath:patients/healthcareWorker/healthcare-worker-headers.js') * configure headers = requestHeaders + * url baseURL - * def nhsNumber = karate.get('nhsNumber', '9912002725') + * def nhsNumber = karate.get('nhsNumber', '9000000009') * path 'Patient', nhsNumber * method get * status 200 @@ -14,23 +20,65 @@ Feature: Patch patient - Healthcare worker access mode * def patientObject = response * def etag = karate.response.header('etag') * def originalVersion = parseInt(response.meta.versionId) + + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + Scenario: Add new name to patient + * match response.name == '#[1]' + + * def newName = + """ + { + "use": "usual", + "period": {"start": "2019-12-31"}, + "prefix": "Dr", + "given": ["Joe", "Horation", "Maximus"], + "family": "Bloggs", + "suffix": "PhD", + } + """ + * path 'Patient', nhsNumber + * request {"patches": [{ "op": "add", "path": "/name/-", "value": "#(newName)" }]} + * method patch + * status 200 + * match response.name == '#[2]' + * match response.name[1] == newName + * match parseInt(response.meta.versionId) == originalVersion + 1 + + Scenario: Replace given name of a patient + * def newName = "Anne" + + * def existingName = response.name[0].given[0] + * match existingName != newName + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"replace","path":"/name/0/given/0","value":"#(newName)"}]} + * method patch + * status 200 + * match response.name[0].given[0] == newName + * match parseInt(response.meta.versionId) == originalVersion + 1 + + Scenario: Remove suffix from patient + * def existingSuffix = response.name[0].suffix + * match existingSuffix != null + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 200 + * match response.name[0].suffix == '#[0]' + * match parseInt(response.meta.versionId) == originalVersion + 1 - @gender Scenario: Change the gender of a patient * def genderOptions = ['male', 'female', 'unknown'] * def targetGender = utils.pickDifferentOption(genderOptions, patientObject.gender) - * header Content-Type = "application/json-patch+json" - * header If-Match = etag + * path 'Patient', nhsNumber - * request - """ - { - "patches": [ - { "op": "replace", "path": "/gender", "value": "#(targetGender)" } - ] - } - """ + * request {"patches": [{ "op": "replace", "path": "/gender", "value": "#(targetGender)" }]} * method patch * status 200 * match response.gender == targetGender - * match parseInt(response.meta.versionId) == originalVersion + 1 \ No newline at end of file + * match parseInt(response.meta.versionId) == originalVersion + 1 + + \ No newline at end of file diff --git a/karate-tests/src/test/java/patients/healthcareWorker/patchPatientErrors.feature b/karate-tests/src/test/java/patients/healthcareWorker/patchPatientErrors.feature new file mode 100644 index 000000000..fe379cd0d --- /dev/null +++ b/karate-tests/src/test/java/patients/healthcareWorker/patchPatientErrors.feature @@ -0,0 +1,258 @@ +@sandbox +Feature: Patch patient errors - Healthcare worker access mode + + # These tests are only run against the sandbox at the moment - until we have a better + # way of managing test data in the integration environment + + Background: + * def utils = call read('classpath:helpers/utils.feature') + + * def accessToken = karate.callSingle('classpath:patients/healthcareWorker/auth-redirect.feature').accessToken + * def requestHeaders = call read('classpath:patients/healthcareWorker/healthcare-worker-headers.js') + * configure headers = requestHeaders + + * url baseURL + * def nhsNumber = karate.get('nhsNumber', '9000000009') + * path 'Patient', nhsNumber + * method get + * status 200 + + * def patientObject = response + * def etag = karate.response.header('etag') + + Scenario: No patch operations + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def diagnostics = "Invalid update with error - No patches found" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * path 'Patient', nhsNumber + * request {} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Incorrect resource version + * def originalVersion = response.meta.versionId + + * header Content-Type = "application/json-patch+json" + * def incorrectResourceVersion = originalVersion + 1 + * header If-Match = 'W/"' + incorrectResourceVersion + '"' + + * def diagnostics = "Invalid update with error - This resource has changed since you last read. Please re-read and try again with the new version number." + * def expectedBody = read('classpath:mocks/stubs/errorResponses/PRECONDITION_FAILED.json') + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 412 + * match response == expectedBody + + Scenario: Invalid x-request-id header + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def invalidUUID = "12345" + * def diagnostics = "Invalid value - '" + invalidUUID + "' in header 'X-Request-ID'" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_VALUE.json') + + * def badHeaders = requestHeaders + * badHeaders["x-request-id"] = invalidUUID + * configure headers = badHeaders + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Missing If-Match header + * header Content-Type = "application/json-patch+json" + + * def diagnostics = "Invalid update with error - If-Match header must be supplied to update this resource" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/PRECONDITION_FAILED.json') + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 412 + * match response == expectedBody + + Scenario: Invalid content-type header + * header Content-Type = "application/bananas" + * header If-Match = etag + + * def expectedBody = read('classpath:mocks/stubs/errorResponses/UNSUPPORTED_SERVICE.json') + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def diagnostics = "Invalid patch: Operation `op` property is not one of operations defined in RFC-6902" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"bad_value","path":"not a path"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid NHS Number + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_RESOURCE_ID.json') + + * path 'Patient', '9000000000' + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Patient not found + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def expectedBody = read('classpath:mocks/stubs/errorResponses/RESOURCE_NOT_FOUND.json') + + * path 'Patient', '9111231130' + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 404 + * match response == expectedBody + + Scenario: Missing x-request-id header + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * def expectedBody = read('classpath:mocks/stubs/errorResponses/MISSING_VALUE_x-request-id.json') + + * def badHeaders = requestHeaders + * remove badHeaders.x-request-id + * configure headers = badHeaders + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"remove","path":"/name/0/suffix/0"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch - no address ID + * def diagnostics = "Invalid update with error - no id or url found for path with root /address/0" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + * path 'Patient', nhsNumber + * request {"patches":[{"op":"replace","path":"/address/0/line/0","value":"2 Whitehall Quay"},{"op":"replace","path":"/address/0/postalCode","value":"LS1 4BU"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch - attempt to replace non-existent object + * def diagnostics = "Invalid update with error - Invalid patch - can't replace non-existent object 'line'" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + * path 'Patient', nhsNumber + * request + """ + {"patches":[ + {"op":"replace","path":"/address/0/id","value":"456"}, + {"op":"replace","path":"/address/0/line","value":["2 Trevelyan Square","Boar Lane","Leeds"]}, + {"op":"replace","path":"/address/0/postalCode","value":"LS1 4BU"} + ]} + """ + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch - invalid address ID + * def diagnostics = "Invalid update with error - no 'address' resources with object id 123456" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + * path 'Patient', nhsNumber + * request + """ + {"patches":[ + {"op":"replace","path":"/address/0/id","value":"123456"}, + {"op":"replace","path":"/address/0/line/0","value":"2 Whitehall Quay"}, + {"op":"replace","path":"/address/0/postalCode","value":"LS1 4BU"} + ]} + """ + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch - invalid address ID only + * def diagnostics = "Invalid update with error - no 'address' resources with object id 123456" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + * path 'Patient', nhsNumber + * request {"patches":[{"op":"replace","path":"/address/0/id","value":"123456"}]} + * method patch + * status 400 + * match response == expectedBody + + Scenario: Invalid patch - patient with no address + * def nhsNumber = "9000000033" + * path 'Patient', nhsNumber + * method get + * status 200 + * def etag = karate.response.header('etag') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + * path 'Patient', nhsNumber + * request + """ + { + "patches":[ + {"op":"replace","path":"/address/0/id","value":"456"}, + {"op":"replace","path":"/address/0/line/0","value":"2 Whitehall Quay"}, + {"op":"replace","path":"/address/0/postalCode","value":"LS1 4BU"} + ] + } + """ + * method patch + * status 400 + * def diagnostics = "Invalid update with error - Invalid patch - index '0' is out of bounds" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + * match response == expectedBody + + Scenario: Invalid patch - Patient with no address / Request without address ID + * def nhsNumber = "9000000033" + * path 'Patient', nhsNumber + * method get + * status 200 + * def etag = karate.response.header('etag') + + * def diagnostics = "Invalid update with error - no id or url found for path with root /address/0" + * def expectedBody = read('classpath:mocks/stubs/errorResponses/INVALID_UPDATE.json') + + * configure headers = requestHeaders + * header Content-Type = "application/json-patch+json" + * header If-Match = etag + + * path 'Patient', nhsNumber + * request {"patches":[{"op":"replace","path":"/address/0/line/0","value":"2 Whitehall Quay"},{"op":"replace","path":"/address/0/postalCode","value":"LS1 4BU"}]} + * method patch + * status 400 + * match response == expectedBody \ No newline at end of file diff --git a/karate-tests/src/test/java/patients/healthcareWorker/postPatient.feature b/karate-tests/src/test/java/patients/healthcareWorker/postPatient.feature index abc3a0859..c39c8763f 100644 --- a/karate-tests/src/test/java/patients/healthcareWorker/postPatient.feature +++ b/karate-tests/src/test/java/patients/healthcareWorker/postPatient.feature @@ -45,7 +45,7 @@ Scenario: Post patient - new patient * path "Patient" * request read('classpath:patients/healthcareWorker/post-patient-request.json') - * configure retry = { count: 5, interval: 1 } + * configure retry = { count: 5, interval: 2 } * retry until responseStatus != 429 * method post * status 201 @@ -80,7 +80,7 @@ Scenario: Fail to create a record for a new patient, single demographics match f * path "Patient" * request read('classpath:patients/healthcareWorker/post-patient-request.json') * method post - * configure retry = { count: 5, interval: 1 } + * configure retry = { count: 5, interval: 2 } * retry until responseStatus != 429 * status 200 * match response == read('classpath:stubs/patient/errorResponses/single_match_found.json') @@ -114,7 +114,7 @@ Scenario: Fail to create a record for a new patient, multiple demographics match * configure headers = requestHeaders * path "Patient" * request requestBody - * configure retry = { count: 5, interval: 1 } + * configure retry = { count: 5, interval: 2 } * retry until responseStatus != 429 * method post * status 200 @@ -124,7 +124,7 @@ Scenario: Fail to create a record for a new patient, multiple demographics match Scenario: Negative path: invalid request body * path "Patient" * request { bananas: "in pyjamas" } - * configure retry = { count: 5, interval: 1 } + * configure retry = { count: 5, interval: 2 } * retry until responseStatus != 429 * method post * status 400