diff --git a/lib/bulk_data_test_kit.rb b/lib/bulk_data_test_kit.rb index 764fb8a..d6a0dc8 100644 --- a/lib/bulk_data_test_kit.rb +++ b/lib/bulk_data_test_kit.rb @@ -2,3 +2,4 @@ require_relative 'inferno_requirements_tools/ext/inferno_core/runnable' require_relative 'bulk_data_test_kit/v1.0.1/bulk_data_test_suite' require_relative 'bulk_data_test_kit/v2.0.0/bulk_data_test_suite' +require_relative 'bulk_data_test_kit/v2.0.0_client/bulk_data_client_test_suite' diff --git a/lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb b/lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb index 93b4a3b..84fd623 100644 --- a/lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb +++ b/lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb @@ -7,7 +7,7 @@ module BulkDataTestKit module BulkDataV101 class BulkDataTestSuite < Inferno::TestSuite - title 'Bulk Data Access v1.0.1' + title 'Bulk Data Access v1.0.1 Server' version VERSION id :bulk_data_v101 links [ diff --git a/lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb b/lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb index e44e324..b6bd3b2 100644 --- a/lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb +++ b/lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb @@ -7,7 +7,7 @@ module BulkDataTestKit module BulkDataV200 class BulkDataTestSuite < Inferno::TestSuite - title 'Bulk Data Access v2.0.0' + title 'Bulk Data Access v2.0.0 Server' version VERSION id :bulk_data_v200 links [ diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_group.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_group.rb new file mode 100644 index 0000000..5b5f71c --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_group.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative 'bulk_data_client_delete_wait_test' +require_relative 'bulk_data_client_kick_off_test' +require_relative 'bulk_data_client_delete_test' + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client Delete Tests + class DeleteGroup < Inferno::TestGroup + title 'Bulk Data Client Delete Tests' + + description %( + The Bulk Data Client Delete tests verify the ability of a client to delete + a kicked-off bulk data export request. + ) + + id :bulk_data_client_delete_group + + run_as_group + + test from: :bulk_data_client_delete_wait + test from: :bulk_data_client_kick_off + test from: :bulk_data_client_delete + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_test.rb new file mode 100644 index 0000000..7ea0f40 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client - Delete + class DeleteTest < Inferno::Test + title 'Bulk Data Delete Request' + + description %( + This test verifies that after a Bulk Data request has been started, the client can send a + DELETE request to the URL provided in the Content-Location header to + [delete the request](https://hl7.org/fhir/uv/bulkdata/STU2/export.html#bulk-data-delete-request), + as described in the [FHIR Asynchronous Request Pattern](https://www.hl7.org/fhir/R4/async.html). + ) + + id :bulk_data_client_delete + + verifies_requirements 'hl7.fhir.uv.bulkdata_2.0.0@119' + + run do + assert load_tagged_requests(DELETE_TAG).any?, fail_message + end + + def fail_message + 'Did not receive a delete request.' + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_wait_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_wait_test.rb new file mode 100644 index 0000000..858ab6a --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_delete_wait_test.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client - Delete Wait + class DeleteWaitTest < Inferno::Test + include URLs + + title 'Wait For Request Sequence' + + description %( + This test will receive bulk data export requests until the user confirms they are finished. + ) + + id :bulk_data_client_delete_wait + + input :access_token, :export_type, :group_id + + run do + wait( + identifier: access_token, + message: %( + Kick-off a **#{export_type}** endpoint type bulk export using the following base URL: + + #{kickoff_url} + + #{export_type == GROUP_EXPORT_TYPE ? "Ensure the Group ID is set to **#{group_id}**." : ''} + + Include the following bearer access token with all requests: **#{access_token}** + + Once the export request has been kicked-off, + [delete the export](https://build.fhir.org/ig/HL7/bulk-data/export.html#bulk-data-delete-request). + + The entire request sequence will be recorded and used in the subsequent tests to + verify comformity to the + [Bulk Data IG](https://build.fhir.org/ig/HL7/bulk-data/export.html#sequence-overview). + + [Click here](#{resume_pass_url}?id=#{access_token}) when finished. + ) + ) + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_group.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_group.rb new file mode 100644 index 0000000..7da13ce --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_group.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative 'bulk_data_client_export_wait_test' +require_relative 'bulk_data_client_kick_off_test' +require_relative 'bulk_data_client_status_test' +require_relative 'bulk_data_client_output_test' + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client Export Tests + class ExportGroup < Inferno::TestGroup + title 'Bulk Data Client Export Tests' + + description %( + The Bulk Data Client Export tests verify the ability of a client to: + - Kick-off a new bulk data export request + - Poll the in-progress request for status updates + - Download a completed bulk data export file + ) + + id :bulk_data_client_export_group + + run_as_group + + test from: :bulk_data_client_export_wait + test from: :bulk_data_client_kick_off + test from: :bulk_data_client_status + test from: :bulk_data_client_output + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_wait_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_wait_test.rb new file mode 100644 index 0000000..715eb6a --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_export_wait_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client - Export Wait + class ExportWaitTest < Inferno::Test + include URLs + + title 'Wait For Request Sequence' + + description %( + This test will receive bulk data export requests until the user confirms they are finished. + ) + + id :bulk_data_client_export_wait + + input :access_token, :export_type, :group_id + + run do + wait( + identifier: access_token, + message: %( + Perform a **#{export_type}** endpoint type bulk export kick-off using the following base URL: + + #{kickoff_url} + + #{export_type == GROUP_EXPORT_TYPE ? "Ensure the Group ID is set to **#{group_id}**." : ''} + + Include the following bearer access token with all requests: **#{access_token}** + + After the kick-off is made, a subsequent status request (using the URL provided in the response + to the kick-off request) and then a download request (using the URL provided in the response to + the status request) are expected. + + The entire request sequence will be recorded and used in the subsequent tests to + verify comformity to the + [Bulk Data IG](https://build.fhir.org/ig/HL7/bulk-data/export.html#sequence-overview). + + [Click here](#{resume_pass_url}?id=#{access_token}) when finished. + ) + ) + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_kick_off_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_kick_off_test.rb new file mode 100644 index 0000000..229f71e --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_kick_off_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client Kick-off + class KickOffTest < Inferno::Test + title 'Bulk Data Kick-off Request' + + description %( + This test verifies that a client can + [kick-off a Bulk Data request](https://hl7.org/fhir/uv/bulkdata/STU2/export.html#roles) + using the `$export` operation against the proper endpoint type. + ) + + id :bulk_data_client_kick_off + + verifies_requirements 'hl7.fhir.uv.bulkdata_2.0.0@17' + + input :export_type + + run do + case export_type + when PATIENT_EXPORT_TYPE + assert load_tagged_requests(PATIENT_KICKOFF_TAG).any?, patient_fail_message + when GROUP_EXPORT_TYPE + assert load_tagged_requests(GROUP_KICKOFF_TAG).any?, group_fail_message + when SYSTEM_EXPORT_TYPE + assert load_tagged_requests(SYSTEM_KICKOFF_TAG).any?, system_fail_message + end + end + + def patient_fail_message + 'Did not receive a Patient type kick-off request.' + end + + def group_fail_message + 'Did not receive a Group type kick-off request.' + end + + def system_fail_message + 'Did not receive a System type kick-off request.' + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_output_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_output_test.rb new file mode 100644 index 0000000..6ba383d --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_output_test.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client Output + class OutputTest < Inferno::Test + title 'Bulk Data Output File Request' + + description %( + This test verifies that the client, using the URL supplied in the Complete Status response body, + can [download](https://hl7.org/fhir/uv/bulkdata/STU2/export.html#bulk-data-output-file-request) + the generated Bulk Data file. + ) + + id :bulk_data_client_output + + verifies_requirements 'hl7.fhir.uv.bulkdata_2.0.0@200' + + run do + assert load_tagged_requests(OUTPUT_TAG).any?, fail_message + end + + def fail_message + 'Did not receive a download request.' + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_status_test.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_status_test.rb new file mode 100644 index 0000000..2303707 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_status_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Client Status + class StatusTest < Inferno::Test + title 'Bulk Data Status Request' + + description %( + This test verifies that after a Bulk Data request has been started, the client can + [poll the status URL](https://hl7.org/fhir/uv/bulkdata/STU2/export.html#bulk-data-status-request) + provided in the Content-Location header, as as described in the + [FHIR Asynchronous Request Pattern](https://www.hl7.org/fhir/R4/async.html). + ) + + id :bulk_data_client_status + + verifies_requirements 'hl7.fhir.uv.bulkdata_2.0.0@123' + + run do + assert load_tagged_requests(STATUS_TAG).any?, fail_message + end + + def fail_message + 'Did not receive a status request.' + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_test_suite.rb b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_test_suite.rb new file mode 100644 index 0000000..2959023 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_test_suite.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require_relative '../version' +require_relative 'tags' +require_relative 'urls' +require_relative 'export_types' + +require_relative 'endpoints/delete' +require_relative 'endpoints/kick_off' +require_relative 'endpoints/output' +require_relative 'endpoints/status' + +require_relative 'bulk_data_client_export_group' +require_relative 'bulk_data_client_delete_group' + +module BulkDataTestKit + module BulkDataV200Client + # Bulk Data Access v2.0.0 Client Test Suite + class BulkDataClientTestSuite < Inferno::TestSuite + title 'Bulk Data Access v2.0.0 Client' + + description File.read(File.join(__dir__, 'docs', 'suite_description.md')) + + version VERSION + + id :bulk_data_v200_client + + links [ + { + label: 'Report Issue', + url: 'https://github.com/inferno-framework/bulk-data-test-kit/issues/' + }, + { + label: 'Open Source', + url: 'https://github.com/inferno-framework/bulk-data-test-kit/' + }, + { + label: 'Download', + url: 'https://github.com/inferno-framework/bulk-data-test-kit/releases' + }, + { + label: 'Implementation Guide', + url: 'https://hl7.org/fhir/uv/bulkdata/STU2/' + } + ] + + input :access_token, + title: 'Access Token', + description: 'The access token that will be included in all client requests during testing.' + + input :export_type, + title: 'Export Type', + description: 'The export endpoint type to test against.', + type: 'radio', + default: SYSTEM_EXPORT_TYPE, + options: { + list_options: [ + { + label: 'All Patients', + value: PATIENT_EXPORT_TYPE + }, + { + label: 'Group of Patients', + value: GROUP_EXPORT_TYPE + }, + { + label: 'System Level Export', + value: SYSTEM_EXPORT_TYPE + } + ] + } + + input :group_id, + title: 'Group ID', + description: 'If using the Group endpoint, the identifier of the Group to export.', + default: 1, + locked: true + + suite_endpoint :get, PATIENT_KICKOFF_ROUTE, Endpoints::KickOff + suite_endpoint :get, GROUP_KICKOFF_ROUTE, Endpoints::KickOff + suite_endpoint :get, SYSTEM_KICKOFF_ROUTE, Endpoints::KickOff + suite_endpoint :get, STATUS_ROUTE, Endpoints::Status + suite_endpoint :get, OUTPUT_ROUTE, Endpoints::Output + suite_endpoint :delete, STATUS_ROUTE, Endpoints::Delete + + resume_test_route :get, RESUME_PASS_PATH do |request| + request.query_parameters['id'] + end + + group from: :bulk_data_client_export_group + group from: :bulk_data_client_delete_group + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/docs/suite_description.md b/lib/bulk_data_test_kit/v2.0.0_client/docs/suite_description.md new file mode 100644 index 0000000..fd4bcbf --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/docs/suite_description.md @@ -0,0 +1,33 @@ +The Bulk Data Access v2.0.0 Client test suite validates the conformance of a client +to the [FHIR Bulk Data Access IG STU2](http://hl7.org/fhir/uv/bulkdata/STU2). + +## Scope + +These tests are a **DRAFT** intended to allow implementers to perform +preliminary checks of their systems against the requirements stated for Bulk Data client actors +and [provide feedback](https://github.com/inferno-framework/bulk-data-test-kit/issues) +on the tests. Future versions of these tests may verify other +requirements and may change the test verification logic. + +## Running the Tests + +### Quick Start + +Inferno needs to be able to identify when requests come from the client under test. Testers must provide a bearer access token that will be provided within the Authentication header (Bearer ) on all requests made to Inferno endpoints during the test. Inferno uses this information to associate the message with the test session and determine how to respond. How the token provided to Inferno is generated is up to the tester. + +Note: authentication options for these tests have not been finalized and are subject to change as the requirements evolve. If the implemented approach prevents you from using these tests, please provide feedback so the limitations can be addressed. + +### Sample Execution - Postman + +To try out these tests without a Bulk Data client implementation, you may +run them using [this "bulk data client system export" Postman collection](https://github.com/inferno-framework/bulk-data-test-kit/blob/main/lib/bulk_data_test_kit/v2.0.0_client/postman/system_export.postman_collection.json) and [this "bulk data client delete" Postman collection](https://github.com/inferno-framework/bulk-data-test-kit/blob/main/lib/bulk_data_test_kit/v2.0.0_client/postman/delete.postman_collection.json). + +To run client tests against one of the Postman collections: +1. Start an Inferno session of the Bulk Data Client test suite. +2. Navigate to either the export tests group or the delete tests group (depending on which Postman collection you want to use). +3. Click the "Run Tests" button in the upper right, enter any value (e.g. `SAMPLE_TOKEN`) as the access token, and select the "System Level Export" option for export type. +4. Click the "Submit" button. The simulated server will then be waiting for an interaction. +5. Open Postman and import the relevant Postman collection. +6. Set the `access_token` variable equal to the value entered in step 3 (the value is also displayed on the test modal in the Inferno UI for reference). +7. Run the collection. +8. Once the postman collection has run, click the "Click here" link in the wait dialog to evaluate the requests. diff --git a/lib/bulk_data_test_kit/v2.0.0_client/endpoints/delete.rb b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/delete.rb new file mode 100644 index 0000000..1bb84b9 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/delete.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + module Endpoints + # Delete Endpoint + class Delete < Inferno::DSL::SuiteEndpoint + def test_run_identifier + request.get_header('HTTP_AUTHORIZATION')&.split&.last + end + + def make_response + response.status = 202 + end + + def tags + [DELETE_TAG] + end + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/endpoints/kick_off.rb b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/kick_off.rb new file mode 100644 index 0000000..815327d --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/kick_off.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + module Endpoints + # Kick Off Endpoint + class KickOff < Inferno::DSL::SuiteEndpoint + include URLs + + def test_run_identifier + request.get_header('HTTP_AUTHORIZATION')&.split&.last + end + + def make_response + response.status = 202 + response.headers['Content-Location'] = status_url + end + + def tags + case request_type.titleize + when PATIENT_EXPORT_TYPE + [PATIENT_KICKOFF_TAG] + when GROUP_EXPORT_TYPE + [GROUP_KICKOFF_TAG] + when SYSTEM_EXPORT_TYPE + [SYSTEM_KICKOFF_TAG] + end + end + + def request_type + request.params[:type] || SYSTEM_EXPORT_TYPE + end + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/endpoints/output.rb b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/output.rb new file mode 100644 index 0000000..fa66e56 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/output.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + module Endpoints + # Output Endpoint + class Output < Inferno::DSL::SuiteEndpoint + def test_run_identifier + request.get_header('HTTP_AUTHORIZATION')&.split&.last + end + + def make_response + response.status = 200 + response.headers['Content-Type'] = 'application/fhir+ndjson' + response.body = "#{example_patient.to_json.squish}\n" + end + + def tags + [OUTPUT_TAG] + end + + def example_patient + FHIR::Patient.new( + id: test_run_identifier, + name: FHIR::HumanName.new(given: 'Example', family: 'Patient') + ) + end + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/endpoints/status.rb b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/status.rb new file mode 100644 index 0000000..138f98f --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/endpoints/status.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + module Endpoints + # Status Endpoint + class Status < Inferno::DSL::SuiteEndpoint + include URLs + + def test_run_identifier + request.get_header('HTTP_AUTHORIZATION')&.split&.last + end + + def make_response + response.status = 200 + response.body = response_body.to_json + response.format = :json + end + + def tags + [STATUS_TAG] + end + + def response_body + { + transactionTime: DateTime.now.iso8601, + request: kickoff_url, + requiresAccessToken: true, + output: [{ + type: 'Patient', + url: output_url + }], + error: [] + } + end + end + end + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/export_types.rb b/lib/bulk_data_test_kit/v2.0.0_client/export_types.rb new file mode 100644 index 0000000..25f47bf --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/export_types.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + PATIENT_EXPORT_TYPE = 'Patient' + GROUP_EXPORT_TYPE = 'Group' + SYSTEM_EXPORT_TYPE = 'System' + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/postman/delete.postman_collection.json b/lib/bulk_data_test_kit/v2.0.0_client/postman/delete.postman_collection.json new file mode 100644 index 0000000..7f93131 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/postman/delete.postman_collection.json @@ -0,0 +1,133 @@ +{ + "info": { + "name": "Bulk Data Client - Delete", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Kickoff", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"kick-off was accepted\", function () {", + " pm.response.to.have.status(202);", + "});", + "", + "pm.collectionVariables.set(\"status_url\", pm.response.headers.get('Content-Location'));", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/custom/bulk_data_v200_client/fhir/$export", + "host": [ + "{{base_url}}" + ], + "path": [ + "custom", + "bulk_data_v200_client", + "fhir", + "$export" + ] + } + }, + "response": [] + }, + { + "name": "Delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"export was deleted\", function () {", + " pm.response.to.have.status(202);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{status_url}}", + "host": [ + "{{status_url}}" + ] + } + }, + "response": [] + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{access_token}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "access_token", + "value": "SAMPLE_TOKEN", + "type": "string" + }, + { + "key": "base_url", + "value": "http://localhost:4567", + "type": "string" + }, + { + "key": "status_url", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/lib/bulk_data_test_kit/v2.0.0_client/postman/system_export.postman_collection.json b/lib/bulk_data_test_kit/v2.0.0_client/postman/system_export.postman_collection.json new file mode 100644 index 0000000..19591d7 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/postman/system_export.postman_collection.json @@ -0,0 +1,181 @@ +{ + "info": { + "name": "Bulk Data Client - System Export", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Kickoff", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"kick-off was accepted\", function () {", + " pm.response.to.have.status(202);", + "});", + "", + "pm.collectionVariables.set(\"status_url\", pm.response.headers.get('Content-Location'));", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/custom/bulk_data_v200_client/fhir/$export", + "host": [ + "{{base_url}}" + ], + "path": [ + "custom", + "bulk_data_v200_client", + "fhir", + "$export" + ] + } + }, + "response": [] + }, + { + "name": "Status", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let responseData = pm.response.json();", + "", + "pm.test(\"export is complete\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.collectionVariables.set(\"download_url\", responseData.output[0].url);", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{status_url}}", + "host": [ + "{{status_url}}" + ] + } + }, + "response": [] + }, + { + "name": "Download", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"downlaod is successful\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{download_url}}", + "host": [ + "{{download_url}}" + ] + } + }, + "response": [] + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{access_token}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "access_token", + "value": "SAMPLE_TOKEN", + "type": "string" + }, + { + "key": "base_url", + "value": "http://localhost:4567", + "type": "string" + }, + { + "key": "status_url", + "value": "", + "type": "string" + }, + { + "key": "download_url", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/lib/bulk_data_test_kit/v2.0.0_client/tags.rb b/lib/bulk_data_test_kit/v2.0.0_client/tags.rb new file mode 100644 index 0000000..5990f22 --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/tags.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + METADATA_TAG = 'bulk_data_metadata' + PATIENT_KICKOFF_TAG = 'bulk_data_kickoff_patient' + GROUP_KICKOFF_TAG = 'bulk_data_kickoff_group' + SYSTEM_KICKOFF_TAG = 'bulk_data_kickoff_system' + STATUS_TAG = 'bulk_data_status' + OUTPUT_TAG = 'bulk_data_output' + DELETE_TAG = 'bulk_data_delete' + end +end diff --git a/lib/bulk_data_test_kit/v2.0.0_client/urls.rb b/lib/bulk_data_test_kit/v2.0.0_client/urls.rb new file mode 100644 index 0000000..e3fd11a --- /dev/null +++ b/lib/bulk_data_test_kit/v2.0.0_client/urls.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module BulkDataTestKit + module BulkDataV200Client + RESUME_PASS_PATH = '/resume_pass' + BASE_ROUTE = '/fhir' + METADATA_ROUTE = "#{BASE_ROUTE}/metadata".freeze + PATIENT_KICKOFF_ROUTE = "#{BASE_ROUTE}/:type/$export".freeze + GROUP_KICKOFF_ROUTE = "#{BASE_ROUTE}/:type/:group_id/$export".freeze + SYSTEM_KICKOFF_ROUTE = "#{BASE_ROUTE}/$export".freeze + STATUS_ROUTE = '/status' + OUTPUT_ROUTE = '/output/example.ndjson' + + # URLs for use in Bulk Data Client tests and endpoints + module URLs + def base_url + "#{Inferno::Application['base_url']}/custom/#{suite_id}" + end + + def resume_pass_url + base_url + RESUME_PASS_PATH + end + + def kickoff_url + base_url + BASE_ROUTE + end + + def status_url + base_url + STATUS_ROUTE + end + + def output_url + base_url + OUTPUT_ROUTE + end + + def suite_id + if respond_to?('result') # If being used with a suite endpoint + result.test_id.split('-').first + elsif self.class.respond_to?('suite') # If being used with a test/group/suite + self.class.suite.id + end + end + end + end +end diff --git a/spec/bulk_data_test_kit/v2.0.0_client/bulk_data_client_spec.rb b/spec/bulk_data_test_kit/v2.0.0_client/bulk_data_client_spec.rb new file mode 100644 index 0000000..fad7f78 --- /dev/null +++ b/spec/bulk_data_test_kit/v2.0.0_client/bulk_data_client_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +require_relative '../../../lib/bulk_data_test_kit/v2.0.0_client/tags' +require_relative '../../../lib/bulk_data_test_kit/v2.0.0_client/urls' + +RSpec.describe BulkDataTestKit::BulkDataV200Client do + let(:suite) { Inferno::Repositories::TestSuites.new.find('bulk_data_v200_client') } + let(:session_data_repo) { Inferno::Repositories::SessionData.new } + let(:results_repo) { Inferno::Repositories::Results.new } + let(:requests_repo) { Inferno::Repositories::Requests.new } + let(:test_session) { repo_create(:test_session, test_suite_id: 'bulk_data_v200_client') } + + let(:kickoff_test) { Inferno::Repositories::Tests.new.find('bulk_data_client_kick_off') } + let(:status_test) { Inferno::Repositories::Tests.new.find('bulk_data_client_status') } + let(:output_test) { Inferno::Repositories::Tests.new.find('bulk_data_client_output') } + let(:delete_test) { Inferno::Repositories::Tests.new.find('bulk_data_client_delete') } + + let(:patient_kickoff_tag) { BulkDataTestKit::BulkDataV200Client::PATIENT_KICKOFF_TAG } + let(:group_kickoff_tag) { BulkDataTestKit::BulkDataV200Client::GROUP_KICKOFF_TAG } + let(:system_kickoff_tag) { BulkDataTestKit::BulkDataV200Client::SYSTEM_KICKOFF_TAG } + let(:status_tag) { BulkDataTestKit::BulkDataV200Client::STATUS_TAG } + let(:output_tag) { BulkDataTestKit::BulkDataV200Client::OUTPUT_TAG } + let(:delete_tag) { BulkDataTestKit::BulkDataV200Client::DELETE_TAG } + + let(:patient_fail) { 'Did not receive a Patient type kick-off request.' } + let(:group_fail) { 'Did not receive a Group type kick-off request.' } + let(:system_fail) { 'Did not receive a System type kick-off request.' } + let(:status_fail) { 'Did not receive a status request.' } + let(:output_fail) { 'Did not receive a download request.' } + let(:delete_fail) { 'Did not receive a delete request.' } + + def run(runnable, inputs = {}) + test_run_params = { test_session_id: test_session.id }.merge(runnable.reference_hash) + test_run = Inferno::Repositories::TestRuns.new.create(test_run_params) + inputs.each do |name, value| + session_data_repo.save( + test_session_id: test_session.id, + name:, + value:, + type: runnable.config.input_type(name) + ) + end + Inferno::TestRunner.new(test_session:, test_run:).run(runnable) + end + + def mock_request(result, tags, verb = 'get') + requests_repo.create({ + verb:, + url: 'https://www.example.com/', + direction: 'incoming', + result_id: result.id, + test_session_id: test_session.id, + tags: + }) + end + + describe 'Bulk Data Client' do + describe 'kick-off test' do + %w[Patient Group System].each do |type| + describe "for #{type} type" do + it 'passes after kick-off request received' do + allow(kickoff_test).to receive_messages(suite:) + result = run(kickoff_test, export_type: type) + + expect(result.result).to eq('fail') + expect(result.result_message).to eq(send("#{type.downcase}_fail")) + + mock_request(result, [send("#{type.downcase}_kickoff_tag")]) + + result = run(kickoff_test, export_type: type) + + expect(result.result).to eq('pass') + end + end + end + end + + describe 'status test' do + it 'passes after status request received' do + allow(status_test).to receive_messages(suite:) + result = run(status_test) + + expect(result.result).to eq('fail') + expect(result.result_message).to eq(status_fail) + + mock_request(result, [status_tag]) + + result = run(status_test) + + expect(result.result).to eq('pass') + end + end + + describe 'output test' do + it 'passes after export downloaded' do + allow(output_test).to receive_messages(suite:) + result = run(output_test) + + expect(result.result).to eq('fail') + expect(result.result_message).to eq(output_fail) + + mock_request(result, [output_tag]) + + result = run(output_test) + + expect(result.result).to eq('pass') + end + end + + describe 'delete test' do + it 'passes after delete request received' do + allow(delete_test).to receive_messages(suite:) + result = run(delete_test) + + expect(result.result).to eq('fail') + expect(result.result_message).to eq(delete_fail) + + mock_request(result, [delete_tag], 'delete') + + result = run(delete_test) + + expect(result.result).to eq('pass') + end + end + end +end