davinci_crd_test_kit 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/presets/inferno_crd_client_suite.json.erb +20 -14
- data/config/presets/inferno_crd_client_suite_prefetch_subset_v221.json.erb +125 -0
- data/config/presets/inferno_crd_client_suite_v221.json.erb +188 -0
- data/config/presets/inferno_crd_server_suite.json.erb +58 -1
- data/config/presets/inferno_crd_server_suite_v221.json.erb +94 -0
- data/config/presets/ri_crd_request_generator.json_v221.json.erb +13 -0
- data/config/presets/ri_crd_server.json.erb +19 -19
- data/lib/davinci_crd_test_kit/client/client_base_urls.rb +80 -0
- data/lib/davinci_crd_test_kit/{client_hook_request_validation.rb → client/client_hook_request_validation.rb} +1 -1
- data/lib/davinci_crd_test_kit/client/crd_client_options.rb +30 -0
- data/lib/davinci_crd_test_kit/client/endpoints/cds_services_discovery_handler.rb +34 -0
- data/lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb +342 -0
- data/lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb +410 -0
- data/lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb +239 -0
- data/lib/davinci_crd_test_kit/{mock_service_response.rb → client/endpoints/mock_service_response.rb} +165 -59
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/companions_prerequisites.json +1 -0
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/create_update_coverage_information.json +3 -2
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/external_reference.json +1 -1
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/launch_smart_app.json +1 -1
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/propose_alternate_request.json +1 -0
- data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/request_form_completion.json +17 -16
- data/lib/davinci_crd_test_kit/client/multi_request_message_helper.rb +35 -0
- data/lib/davinci_crd_test_kit/client/tagged_request_load_helper.rb +38 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_create_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_read_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_search_test.rb +234 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_update_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_validation_test.rb +63 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/decode_auth_token_test.rb +65 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/retrieve_jwks_test.rb +109 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/token_header_test.rb +70 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/token_payload_test.rb +85 -0
- data/lib/davinci_crd_test_kit/{routes/cds-services.json → client/v2.0.1/cds-services-v201.json} +1 -1
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_appointment_book_group.rb +108 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_card_must_support_group.rb +31 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_discharge_group.rb +105 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_start_group.rb +105 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_fhir_api_group.rb +790 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_hooks_group.rb +74 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_dispatch_group.rb +111 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_select_group.rb +116 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_sign_group.rb +113 -0
- data/lib/davinci_crd_test_kit/{client_registration_group.rb → client/v2.0.1/client_registration_group.rb} +12 -8
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_urls.rb +13 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/crd_client_suite.rb +134 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/appointment_book_receive_request_test.rb +129 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/encounter_discharge_receive_request_test.rb +126 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/encounter_start_receive_request_test.rb +126 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_dispatch_receive_request_test.rb +138 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_select_receive_request_test.rb +134 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_sign_receive_request_test.rb +136 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_coverage_information.rb +93 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_external_reference.rb +62 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_instructions.rb +62 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/registration/client_registration_verification_test.rb +94 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/registration/client_service_registration_attestation_test.rb +40 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_fetched_data_test.rb +86 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_optional_fields_test.rb +63 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_prefetch_equals_queried_test.rb +96 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_required_fields_test.rb +55 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_valid_context_test.rb +70 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_valid_prefetch_test.rb +62 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/client_display_cards_attest.rb +83 -0
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/inferno_response_validation.rb +79 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/api/client_coverage_info_update_test.rb +212 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/api/client_crd_update_verification_group.rb +18 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/decode_auth_token_test.rb +69 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/retrieve_jwks_test.rb +120 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/token_header_test.rb +92 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/token_payload_test.rb +93 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-prefetch-subset-v221.json +198 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-v221.json +202 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_appointment_book_group.rb +109 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_cross_hook_group.rb +28 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_discharge_group.rb +102 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_start_group.rb +101 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_fhir_api_group.rb +88 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_hooks_group.rb +84 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_long_running_hook_group.rb +32 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_dispatch_group.rb +107 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_select_group.rb +108 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_sign_group.rb +113 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_registration_group.rb +33 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_urls.rb +27 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/crd_client_suite.rb +234 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_card_must_support_coverage_information_test.rb +63 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_fhirpath_collection_as_comma_delimited_string_test.rb +60 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_hook_instances_unique_test.rb +45 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_location_address_propagation_test.rb +135 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_prefetch_complete_and_subset_test.rb +103 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/appointment_book_receive_request_test.rb +156 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_discharge_receive_request_test.rb +157 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_start_receive_request_test.rb +157 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_dispatch_receive_request_test.rb +165 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_select_receive_request_test.rb +165 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_sign_receive_request_test.rb +165 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_long_running_receive_request_test.rb +64 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_skip_long_running_attestation_test.rb +54 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_registration_verification_test.rb +161 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_service_registration_attestation_test.rb +108 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_conformance_test.rb +47 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_coverage_verification_test.rb +152 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_data_fetch_verification_test.rb +55 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_granted_scopes_test.rb +123 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_complete_test.rb +133 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_profiles_test.rb +55 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_requested_version_test.rb +54 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_secured_transport_test.rb +48 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/client_display_cards_attest.rb +75 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/hook_response_support_coverage_information_test.rb +30 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/inferno_response_validation.rb +77 -0
- data/lib/davinci_crd_test_kit/cross_suite/base_urls.rb +20 -0
- data/lib/davinci_crd_test_kit/cross_suite/cards_identification.rb +321 -0
- data/lib/davinci_crd_test_kit/{cards_validation.rb → cross_suite/cards_validation.rb} +104 -47
- data/lib/davinci_crd_test_kit/cross_suite/coverage-information_stu201_metadata.yml +27 -0
- data/lib/davinci_crd_test_kit/cross_suite/coverage-information_stu221_metadata.yml +60 -0
- data/lib/davinci_crd_test_kit/cross_suite/fhirpath_on_cds_request.rb +177 -0
- data/lib/davinci_crd_test_kit/{hook_request_field_validation.rb → cross_suite/hook_request_field_validation.rb} +282 -203
- data/lib/davinci_crd_test_kit/cross_suite/logical_models_override_helper.rb +220 -0
- data/lib/davinci_crd_test_kit/cross_suite/prefetch_completeness_checker.rb +476 -0
- data/lib/davinci_crd_test_kit/cross_suite/prefetch_contents_validation.rb +81 -0
- data/lib/davinci_crd_test_kit/cross_suite/prefetch_profile_validation.rb +48 -0
- data/lib/davinci_crd_test_kit/cross_suite/profiles_and_resource_types.rb +63 -0
- data/lib/davinci_crd_test_kit/cross_suite/replace_tokens.rb +38 -0
- data/lib/davinci_crd_test_kit/cross_suite/requests_logical_model_validation.rb +203 -0
- data/lib/davinci_crd_test_kit/cross_suite/response_logical_model_validation.rb +435 -0
- data/lib/davinci_crd_test_kit/{suggestion_actions_validation.rb → cross_suite/suggestion_actions_validation.rb} +70 -50
- data/lib/davinci_crd_test_kit/cross_suite/tags.rb +46 -0
- data/lib/davinci_crd_test_kit/metadata.rb +10 -44
- data/lib/davinci_crd_test_kit/requirements/cds-hooks-library_1.0.1_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/requirements/cds-hooks_2.0_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/requirements/cds-hooks_3.0.0-ballot_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/requirements/davinci_crd_test_kit_requirements.csv +742 -65
- data/lib/davinci_crd_test_kit/requirements/generated/crd_client_requirements_coverage.csv +279 -54
- data/lib/davinci_crd_test_kit/requirements/generated/crd_client_v221_requirements_coverage.csv +1430 -0
- data/lib/davinci_crd_test_kit/requirements/generated/crd_server_requirements_coverage.csv +36 -45
- data/lib/davinci_crd_test_kit/requirements/generated/crd_server_v221_requirements_coverage.csv +143 -0
- data/lib/davinci_crd_test_kit/requirements/hl7.fhir.us.davinci-crd_2.0.1_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/requirements/hl7.fhir.us.davinci-crd_2.2.1_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/server/endpoints/jwk_set_endpoint_handler.rb +13 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_create_endpoint.rb +23 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_delete_endpoint.rb +30 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_metadata_endpoint.rb +112 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_read_endpoint.rb +21 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_request_handler.rb +261 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_search_endpoint.rb +561 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_update_endpoint.rb +24 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/stress-test-Bundle.json +54687 -0
- data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr_endpoints.rb +95 -0
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb +225 -0
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_no_special_requests.rb +17 -0
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_with_bad_auth.rb +14 -0
- data/lib/davinci_crd_test_kit/{jwt_helper.rb → server/jwt_helper.rb} +1 -12
- data/lib/davinci_crd_test_kit/server/resource_extractor.rb +68 -0
- data/lib/davinci_crd_test_kit/server/server_abstract_invoke_hook_test.rb +183 -0
- data/lib/davinci_crd_test_kit/server/server_base_urls.rb +30 -0
- data/lib/davinci_crd_test_kit/{server_hook_helper.rb → server/server_hook_helper.rb} +3 -3
- data/lib/davinci_crd_test_kit/{server_hook_request_validation.rb → server/server_hook_request_validation.rb} +1 -1
- data/lib/davinci_crd_test_kit/{test_helper.rb → server/server_test_helper.rb} +7 -3
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Appointment.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/ClaimResponse.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/CommunicationRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Coverage.yml +21 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Device.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/DeviceRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Encounter.yml +7 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Location.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/MedicationRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/NutritionOrder.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Organization.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Patient.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Practitioner.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/PractitionerRole.yml +40 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/ServiceRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Task.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/VisionPrescription.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_server_suite.rb +99 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/discovery/discovery_endpoint_test.rb +90 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/discovery/discovery_services_validation_test.rb +67 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/interaction/server_invoke_hook_test.rb +12 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/coverage_information_system_action_across_hooks_validation_test.rb +34 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/external_reference_card_across_hooks_validation_test.rb +30 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/instructions_card_received_across_hooks_test.rb +27 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_appointment_book_group.rb +191 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_demonstrate_hook_response_group.rb +93 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_discovery_group.rb +62 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_encounter_discharge_group.rb +186 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_encounter_start_group.rb +186 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_hooks_group.rb +73 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_dispatch_group.rb +191 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_select_group.rb +211 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_sign_group.rb +216 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_required_card_response_validation_group.rb +28 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_urls.rb +13 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_context_validation_test.rb +30 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_optional_fields_validation_test.rb +39 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_required_fields_validation_test.rb +40 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/additional_orders_validation_test.rb +59 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/card_optional_fields_validation_test.rb +51 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/coverage_information_system_action_received_test.rb +65 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/coverage_information_system_action_validation_test.rb +120 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +70 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/external_reference_card_validation_test.rb +37 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/form_completion_response_validation_test.rb +67 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/instructions_card_received_test.rb +30 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/launch_smart_app_card_validation_test.rb +39 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/propose_alternate_request_card_validation_test.rb +46 -0
- data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/service_response_validation_test.rb +83 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Appointment_withorder.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Appointment_withoutorder.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/CommunicationRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Coverage.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Device.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/DeviceRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Encounter.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Location.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/MedicationRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/NutritionOrder.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Organization.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Patient.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/PractitionerRole.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/ServiceRequest.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/VisionPrescription.yml +5 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_server_suite.rb +118 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_configuration_test.rb +159 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_endpoint_test.rb +90 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_prefetch_support_test.rb +43 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_services_validation_test.rb +121 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_standard_prefetch_expressions_test.rb +166 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_single_request_test.rb +22 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_test.rb +17 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/coverage_information_must_support_test.rb +71 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/coverage_information_system_action_across_hooks_validation_test.rb +36 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/supported_us_core_versions_test.rb +118 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_appointment_book_group.rb +121 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_demonstrate_hook_response_group.rb +94 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_discovery_group.rb +71 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_discharge_group.rb +107 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_start_group.rb +107 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_hooks_group.rb +73 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_dispatch_group.rb +121 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_select_group.rb +115 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_sign_group.rb +132 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_required_card_response_validation_group.rb +30 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_urls.rb +13 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_response_group.rb +23 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_info_reason_test.rb +108 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_not_found_group.rb +116 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_active_coverage_group.rb +115 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_member_found_group.rb +117 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_group.rb +121 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_invoke_test.rb +22 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_context_validation_test.rb +31 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_no_custom_extensions_test.rb +120 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_optional_fields_validation_test.rb +40 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_required_fields_validation_test.rb +41 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/additional_orders_validation_test.rb +109 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/all_responses_include_coverage_information_test.rb +123 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_info_configuration_test.rb +83 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_card_absence_test.rb +76 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_validation_test.rb +130 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +119 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/external_reference_card_validation_test.rb +89 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/form_completion_response_validation_test.rb +126 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/hook_request_resource_resolution.rb +174 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/instructions_card_received_test.rb +83 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/launch_smart_app_card_validation_test.rb +88 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/order_dispatch_coverage_information_test.rb +38 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/propose_alternate_request_card_validation_test.rb +81 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/service_response_validation_test.rb +63 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_cds_hooks_elements_test.rb +78 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_configuration_test.rb +78 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_context_test.rb +78 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/verify_response_without_billing_options_test.rb +43 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/verify_response_without_configuration_test.rb +44 -0
- data/lib/davinci_crd_test_kit/version.rb +2 -2
- data/lib/davinci_crd_test_kit.rb +4 -2
- metadata +307 -93
- data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +0 -785
- data/lib/davinci_crd_test_kit/client_hooks_group.rb +0 -74
- data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +0 -93
- data/lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb +0 -75
- data/lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb +0 -48
- data/lib/davinci_crd_test_kit/client_tests/client_encounter_discharge_group.rb +0 -73
- data/lib/davinci_crd_test_kit/client_tests/client_encounter_start_group.rb +0 -73
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +0 -42
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_read_test.rb +0 -40
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +0 -232
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +0 -42
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +0 -61
- data/lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb +0 -79
- data/lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb +0 -82
- data/lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb +0 -81
- data/lib/davinci_crd_test_kit/client_tests/client_registration_verification_test.rb +0 -88
- data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +0 -60
- data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +0 -90
- data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +0 -90
- data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +0 -57
- data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +0 -49
- data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +0 -68
- data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +0 -69
- data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +0 -102
- data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +0 -98
- data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +0 -101
- data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +0 -105
- data/lib/davinci_crd_test_kit/client_tests/submitted_response_validation.rb +0 -48
- data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +0 -65
- data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +0 -78
- data/lib/davinci_crd_test_kit/crd_client_suite.rb +0 -193
- data/lib/davinci_crd_test_kit/crd_options.rb +0 -9
- data/lib/davinci_crd_test_kit/crd_server_suite.rb +0 -125
- data/lib/davinci_crd_test_kit/igs/davinci-crd-2.0.1.tgz +0 -0
- data/lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb +0 -18
- data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +0 -77
- data/lib/davinci_crd_test_kit/routes/jwk_set_endpoint_handler.rb +0 -15
- data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +0 -176
- data/lib/davinci_crd_test_kit/server_demonstrate_hook_response_group.rb +0 -77
- data/lib/davinci_crd_test_kit/server_discovery_group.rb +0 -60
- data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +0 -170
- data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +0 -170
- data/lib/davinci_crd_test_kit/server_hooks_group.rb +0 -71
- data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +0 -176
- data/lib/davinci_crd_test_kit/server_order_select_group.rb +0 -195
- data/lib/davinci_crd_test_kit/server_order_sign_group.rb +0 -201
- data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +0 -26
- data/lib/davinci_crd_test_kit/server_tests/additional_orders_validation_test.rb +0 -68
- data/lib/davinci_crd_test_kit/server_tests/card_optional_fields_validation_test.rb +0 -47
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_across_hooks_validation_test.rb +0 -32
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +0 -63
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +0 -118
- data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +0 -71
- data/lib/davinci_crd_test_kit/server_tests/discovery_endpoint_test.rb +0 -88
- data/lib/davinci_crd_test_kit/server_tests/discovery_services_validation_test.rb +0 -65
- data/lib/davinci_crd_test_kit/server_tests/external_reference_card_across_hooks_validation_test.rb +0 -28
- data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +0 -36
- data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +0 -78
- data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_across_hooks_test.rb +0 -25
- data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_test.rb +0 -26
- data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +0 -38
- data/lib/davinci_crd_test_kit/server_tests/propose_alternate_request_card_validation_test.rb +0 -63
- data/lib/davinci_crd_test_kit/server_tests/service_call_test.rb +0 -101
- data/lib/davinci_crd_test_kit/server_tests/service_request_context_validation_test.rb +0 -28
- data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +0 -37
- data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +0 -38
- data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +0 -81
- data/lib/davinci_crd_test_kit/tags.rb +0 -10
- data/lib/davinci_crd_test_kit/urls.rb +0 -52
- /data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/instructions.json +0 -0
- /data/lib/davinci_crd_test_kit/{crd_jwks.json → server/endpoints/crd_jwks.json} +0 -0
- /data/lib/davinci_crd_test_kit/{jwks.rb → server/endpoints/jwks.rb} +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module DaVinciCRDTestKit
|
|
2
|
+
module V221
|
|
3
|
+
class DiscoveryConfigurationTest < Inferno::Test
|
|
4
|
+
title 'Server makes configuration options available'
|
|
5
|
+
id :crd_v221_discovery_configuration
|
|
6
|
+
description %(
|
|
7
|
+
According to the spec:
|
|
8
|
+
|
|
9
|
+
> CRD servers SHALL, at minimum, offer configuration options for each
|
|
10
|
+
type of card they support
|
|
11
|
+
|
|
12
|
+
This test verifies that all primary hook services contain at least one
|
|
13
|
+
valid configuration option for `coverage-info`. Secondary hooks are not
|
|
14
|
+
expected to return cards, so they are ignored in this test.
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@dev-4',
|
|
18
|
+
'hl7.fhir.us.davinci-crd_2.2.1@dev-5',
|
|
19
|
+
'hl7.fhir.us.davinci-crd_2.2.1@dev-7',
|
|
20
|
+
'hl7.fhir.us.davinci-crd_2.2.1@dev-8'
|
|
21
|
+
|
|
22
|
+
input :cds_services
|
|
23
|
+
input :crd_discovery_service_ignore_list,
|
|
24
|
+
optional: true
|
|
25
|
+
|
|
26
|
+
def primary_hooks
|
|
27
|
+
['appointment-book', 'order-sign', 'order-dispatch']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def primary_hook?(hook)
|
|
31
|
+
primary_hooks.include? hook
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def required_fields
|
|
35
|
+
['code', 'type', 'name', 'description', 'default']
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def verify_unique_values(hook, service)
|
|
39
|
+
unique_fields = ['code', 'name', 'description']
|
|
40
|
+
|
|
41
|
+
config_options =
|
|
42
|
+
service
|
|
43
|
+
.dig('extension', 'davinci-crd.configuration-options')
|
|
44
|
+
|
|
45
|
+
return if config_options.blank?
|
|
46
|
+
|
|
47
|
+
unique_fields.each do |unique_field|
|
|
48
|
+
value_counts =
|
|
49
|
+
config_options
|
|
50
|
+
.map { |config_option| config_option[unique_field] }
|
|
51
|
+
.tally
|
|
52
|
+
|
|
53
|
+
duplicate_values =
|
|
54
|
+
value_counts
|
|
55
|
+
.select { |_value, count| count > 1 }
|
|
56
|
+
.keys
|
|
57
|
+
|
|
58
|
+
next if duplicate_values.blank?
|
|
59
|
+
|
|
60
|
+
duplicate_values_string = duplicate_values.map { |value| "\n- `#{value}`" }.join
|
|
61
|
+
|
|
62
|
+
add_message(
|
|
63
|
+
'error',
|
|
64
|
+
"Services for hook `#{hook}` contain duplicate values for `#{unique_field}`:" \
|
|
65
|
+
"#{duplicate_values_string}"
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def verify_required_fields(hook, service)
|
|
71
|
+
service.dig('extension', 'davinci-crd.configuration-options')&.each do |config_option|
|
|
72
|
+
required_fields.each do |field_name|
|
|
73
|
+
unless config_option.key?(field_name)
|
|
74
|
+
add_message(
|
|
75
|
+
'error',
|
|
76
|
+
"Hook `#{hook}` service `#{service['id']}` configuration option `#{config_option['code']}` " \
|
|
77
|
+
"does not contain `#{field_name}` field"
|
|
78
|
+
)
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
next if field_name == 'default'
|
|
83
|
+
|
|
84
|
+
next if config_option[field_name].is_a? String
|
|
85
|
+
|
|
86
|
+
type = config_option[field_name].class
|
|
87
|
+
add_message(
|
|
88
|
+
'error',
|
|
89
|
+
"Expected hook `#{hook}` service `#{service['id']}` configuration option `#{config_option['code']}` " \
|
|
90
|
+
"field `#{field_name}` to be a String, but found #{type}"
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
run do
|
|
97
|
+
ignore_list = crd_discovery_service_ignore_list.to_s.split(',').map(&:strip).reject(&:blank?)
|
|
98
|
+
services =
|
|
99
|
+
JSON.parse(cds_services)['services']
|
|
100
|
+
.reject { |service| ignore_list.include? service['id'] }
|
|
101
|
+
|
|
102
|
+
services_by_hook = services.group_by { |service| service['hook'] }
|
|
103
|
+
|
|
104
|
+
services_by_hook.each do |hook, hook_services|
|
|
105
|
+
hook_services.each do |service|
|
|
106
|
+
verify_required_fields(hook, service)
|
|
107
|
+
verify_unique_values(hook, service)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
primary_hook_services = services_by_hook.slice(*primary_hooks).values.flatten
|
|
112
|
+
|
|
113
|
+
assert messages.none? { |message| message[:type] == 'error' },
|
|
114
|
+
'Some services contain invalid configuration options.'
|
|
115
|
+
|
|
116
|
+
omit_if primary_hook_services.blank?, 'No services for primary hooks found'
|
|
117
|
+
|
|
118
|
+
services_without_configuration_options =
|
|
119
|
+
primary_hook_services.reject do |service|
|
|
120
|
+
service['extension'].present? && service['extension']['davinci-crd.configuration-options'].present?
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
if services_without_configuration_options.present?
|
|
124
|
+
add_message(
|
|
125
|
+
'error',
|
|
126
|
+
'The following services do not contain any configuration options: ' \
|
|
127
|
+
"#{services_without_configuration_options.map { |service| service['code'] }.join(', ')}"
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
primary_hook_services.each do |service|
|
|
132
|
+
coverage_info_config =
|
|
133
|
+
service.dig('extension', 'davinci-crd.configuration-options')&.find do |config_option|
|
|
134
|
+
config_option['code'] == 'coverage-info'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
if coverage_info_config.present?
|
|
138
|
+
if coverage_info_config['type'] != 'boolean'
|
|
139
|
+
add_message(
|
|
140
|
+
'error',
|
|
141
|
+
"Service `#{service['id']}` `coverage-info` configuration option is not of type boolean"
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
next
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
add_message(
|
|
149
|
+
'error',
|
|
150
|
+
"Service `#{service['id']}` does not contain a `coverage-info` configuration option"
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
assert messages.none? { |message| message[:type] == 'error' },
|
|
155
|
+
'Not all primary hook services contain valid configuration options.'
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module DaVinciCRDTestKit
|
|
2
|
+
module V221
|
|
3
|
+
class DiscoveryEndpointTest < Inferno::Test
|
|
4
|
+
title 'Server returns a discovery response'
|
|
5
|
+
id :crd_v221_discovery_endpoint_test
|
|
6
|
+
description %(
|
|
7
|
+
A CDS Service provider must expose its discovery endpoint at `{baseURL}/cds-services`
|
|
8
|
+
as specified in the [CDS Hooks Specification](https://cds-hooks.hl7.org/2.0/#discovery).
|
|
9
|
+
|
|
10
|
+
This test checks that the server responds to a GET request at the following endpoint:
|
|
11
|
+
|
|
12
|
+
`GET {baseURL}/cds-services`
|
|
13
|
+
|
|
14
|
+
It does this by checking that the server responds with an HTTP OK 200 status code
|
|
15
|
+
and that the body of the response is a valid JSON object. This test does not
|
|
16
|
+
inspect the structure and content of the response body to see if it contains the required information.
|
|
17
|
+
It only checks to see if the RESTful interaction is supported and returns a valid JSON object.
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
input_order :base_url, :authentication_required, :encryption_method, :jwks_kid
|
|
21
|
+
input :base_url
|
|
22
|
+
input :authentication_required,
|
|
23
|
+
title: 'Discovery endpoint requires authentication?',
|
|
24
|
+
type: 'radio',
|
|
25
|
+
default: 'no',
|
|
26
|
+
options: {
|
|
27
|
+
list_options: [
|
|
28
|
+
{
|
|
29
|
+
label: 'No',
|
|
30
|
+
value: 'no'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 'Yes',
|
|
34
|
+
value: 'yes'
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
input :encryption_method,
|
|
39
|
+
title: 'JWT Signing Algorithm',
|
|
40
|
+
description: <<~DESCRIPTION,
|
|
41
|
+
CDS Hooks recommends ES384 and RS384 for JWT signature verification.
|
|
42
|
+
Select which method to use.
|
|
43
|
+
DESCRIPTION
|
|
44
|
+
type: 'radio',
|
|
45
|
+
default: 'ES384',
|
|
46
|
+
options: {
|
|
47
|
+
list_options: [
|
|
48
|
+
{
|
|
49
|
+
label: 'ES384',
|
|
50
|
+
value: 'ES384'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: 'RS384',
|
|
54
|
+
value: 'RS384'
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
input :jwks_kid,
|
|
59
|
+
title: 'CDS Services JWKS kid',
|
|
60
|
+
description: <<~DESCRIPTION,
|
|
61
|
+
The key ID of the JWKS private key to use for signing the JWTs when invoking a CDS service endpoint
|
|
62
|
+
requiring authentication.
|
|
63
|
+
Defaults to the first JWK in the list if no kid is supplied.
|
|
64
|
+
DESCRIPTION
|
|
65
|
+
optional: true
|
|
66
|
+
output :cds_services
|
|
67
|
+
|
|
68
|
+
run do
|
|
69
|
+
discovery_url = "#{base_url.chomp('/')}/cds-services"
|
|
70
|
+
headers = { 'Accept' => 'application/json' }
|
|
71
|
+
|
|
72
|
+
if authentication_required == 'yes'
|
|
73
|
+
token = JwtHelper.build(
|
|
74
|
+
aud: discovery_url,
|
|
75
|
+
iss: inferno_base_url,
|
|
76
|
+
jku: "#{inferno_base_url}/jwks.json",
|
|
77
|
+
kid: jwks_kid,
|
|
78
|
+
encryption_method:
|
|
79
|
+
)
|
|
80
|
+
headers['Authorization'] = "Bearer #{token}"
|
|
81
|
+
end
|
|
82
|
+
get(discovery_url, headers:, tags: [DISCOVERY_TAG])
|
|
83
|
+
assert_response_status(200)
|
|
84
|
+
assert_valid_json(request.response_body)
|
|
85
|
+
|
|
86
|
+
output cds_services: request.response_body
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require_relative '../../server_test_helper'
|
|
2
|
+
|
|
3
|
+
module DaVinciCRDTestKit
|
|
4
|
+
module V221
|
|
5
|
+
class DiscoveryPrefetchSupportTest < Inferno::Test
|
|
6
|
+
include DaVinciCRDTestKit::ServerTestHelper
|
|
7
|
+
|
|
8
|
+
title 'Server advertises prefetch support'
|
|
9
|
+
id :crd_v221_discovery_prefetch_support
|
|
10
|
+
description %(
|
|
11
|
+
This test expects the server to demonstrate support for prefetch queries
|
|
12
|
+
by providing a prefetch query in at least one of the advertised service
|
|
13
|
+
in the discovery response.
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@found-24'
|
|
17
|
+
|
|
18
|
+
input :cds_services
|
|
19
|
+
input :crd_discovery_service_ignore_list,
|
|
20
|
+
optional: true
|
|
21
|
+
|
|
22
|
+
run do
|
|
23
|
+
object = parse_json(cds_services)
|
|
24
|
+
assert object['services'], 'Discovery response did not contain `services`'
|
|
25
|
+
|
|
26
|
+
services = object['services']
|
|
27
|
+
assert services.is_a?(Array), 'Services field of the CDS Discovery response object is not an array.'
|
|
28
|
+
|
|
29
|
+
ignored_service_ids = crd_discovery_service_ignore_list.to_s.split(',').map(&:strip).reject(&:blank?)
|
|
30
|
+
services
|
|
31
|
+
.select { |service| ignored_service_ids.include?(service['id']) }
|
|
32
|
+
.each do |service|
|
|
33
|
+
info "Ignoring service `#{service['id']}` because it is in the ignore list."
|
|
34
|
+
end
|
|
35
|
+
services_for_crd_validation = services.reject { |service| ignored_service_ids.include?(service['id']) }
|
|
36
|
+
|
|
37
|
+
prefetch_supported = services_for_crd_validation.any? { |service| service['prefetch'].present? }
|
|
38
|
+
|
|
39
|
+
skip_if !prefetch_supported, 'No CRD services advertised prefetch support'
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require_relative '../../server_test_helper'
|
|
2
|
+
|
|
3
|
+
module DaVinciCRDTestKit
|
|
4
|
+
module V221
|
|
5
|
+
class DiscoveryServicesValidationTest < Inferno::Test
|
|
6
|
+
include DaVinciCRDTestKit::ServerTestHelper
|
|
7
|
+
|
|
8
|
+
title 'Discovery response contains valid services'
|
|
9
|
+
id :crd_v221_discovery_services_validation
|
|
10
|
+
description %(
|
|
11
|
+
As per the [CDS Hooks Spec](https://cds-hooks.hl7.org/2.0/#response),
|
|
12
|
+
the response to the discovery endpoint SHALL be an object containing
|
|
13
|
+
a list of CDS services. If your CDS server hosts no CDS services,
|
|
14
|
+
the discovery endpoint should return a 200 HTTP response with
|
|
15
|
+
an empty array of services.
|
|
16
|
+
|
|
17
|
+
Each CDS service must contain the following required fields:
|
|
18
|
+
`hook`, `description`, and `id`.
|
|
19
|
+
|
|
20
|
+
Additionally, the [CRD
|
|
21
|
+
Spec](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/deviations.html#crd-version-declaration)
|
|
22
|
+
states that "CRD servers SHALL declare at least one supported CRD
|
|
23
|
+
version for each supported hook" using the `davinci-crd.version`
|
|
24
|
+
extension.
|
|
25
|
+
|
|
26
|
+
This test checks for the presence of the required fields and
|
|
27
|
+
validates that they are of the correct type.
|
|
28
|
+
|
|
29
|
+
CDS services provided by the server to support use cases outside the
|
|
30
|
+
scope of the CRD specification can be provided in an ignore list input to
|
|
31
|
+
opt-out of CRD validation.
|
|
32
|
+
|
|
33
|
+
The test will be skipped if the server hosts no CRD CDS services.
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@dev-1'
|
|
37
|
+
|
|
38
|
+
input :cds_services
|
|
39
|
+
input :crd_discovery_service_ignore_list,
|
|
40
|
+
title: 'Service ID Ignore List',
|
|
41
|
+
description: %(
|
|
42
|
+
In the case that CDS services advertised on this server support
|
|
43
|
+
use cases outside the scope of the CRD specification, provide a
|
|
44
|
+
comma-separated list of service IDs to opt-out of CRD validation.
|
|
45
|
+
Ignored services will not be stored for use in later tests. If
|
|
46
|
+
blank, all services are checked and saved.
|
|
47
|
+
),
|
|
48
|
+
optional: true
|
|
49
|
+
output :appointment_book_service_ids, :encounter_start_service_ids, :encounter_discharge_service_ids,
|
|
50
|
+
:order_dispatch_service_ids, :order_select_service_ids, :order_sign_service_ids
|
|
51
|
+
|
|
52
|
+
EXTENSION_KEY = 'davinci-crd.version'.freeze
|
|
53
|
+
|
|
54
|
+
def required_fields
|
|
55
|
+
{
|
|
56
|
+
'hook' => String,
|
|
57
|
+
'description' => String,
|
|
58
|
+
'id' => String,
|
|
59
|
+
'extension' => Hash
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
run do
|
|
64
|
+
object = parse_json(cds_services)
|
|
65
|
+
assert object['services'], 'Discovery response did not contain `services`'
|
|
66
|
+
|
|
67
|
+
services = object['services']
|
|
68
|
+
assert services.is_a?(Array), 'Services field of the CDS Discovery response object is not an array.'
|
|
69
|
+
|
|
70
|
+
ignored_service_ids = crd_discovery_service_ignore_list.to_s.split(',').map(&:strip).reject(&:blank?)
|
|
71
|
+
services
|
|
72
|
+
.select { |service| ignored_service_ids.include?(service['id']) }
|
|
73
|
+
.each do |service|
|
|
74
|
+
info "Ignoring service `#{service['id']}` because it is in the ignore list."
|
|
75
|
+
end
|
|
76
|
+
services_for_crd_validation = services.reject { |service| ignored_service_ids.include?(service['id']) }
|
|
77
|
+
|
|
78
|
+
service_hooks_to_ids = services_for_crd_validation.each_with_object({}) do |service, hash|
|
|
79
|
+
hash[service['hook']] ||= []
|
|
80
|
+
hash[service['hook']] << service['id'] if service['id']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
output appointment_book_service_ids: service_hooks_to_ids['appointment-book']&.join(', '),
|
|
84
|
+
encounter_start_service_ids: service_hooks_to_ids['encounter-start']&.join(', '),
|
|
85
|
+
encounter_discharge_service_ids: service_hooks_to_ids['encounter-discharge']&.join(', '),
|
|
86
|
+
order_dispatch_service_ids: service_hooks_to_ids['order-dispatch']&.join(', '),
|
|
87
|
+
order_select_service_ids: service_hooks_to_ids['order-select']&.join(', '),
|
|
88
|
+
order_sign_service_ids: service_hooks_to_ids['order-sign']&.join(', ')
|
|
89
|
+
|
|
90
|
+
skip_if services.empty?, 'Server hosts no CDS Services.'
|
|
91
|
+
skip_if services_for_crd_validation.empty?, 'Ignore list excludes all CDS Services from validation.'
|
|
92
|
+
|
|
93
|
+
services_for_crd_validation.each do |service|
|
|
94
|
+
required_fields.each do |field, type|
|
|
95
|
+
assert(service[field], "Service `#{service['id']}` did not contain required field: `#{field}`")
|
|
96
|
+
assert(service[field].is_a?(type), "Service `#{service['id']}`: field `#{field}` is not of type #{type}")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
assert service['extension'].key?(EXTENSION_KEY),
|
|
100
|
+
"Service `#{service['id']}`: does not contain a `#{EXTENSION_KEY}` extension"
|
|
101
|
+
assert service['extension'][EXTENSION_KEY].is_a?(Array),
|
|
102
|
+
"Service `#{service['id']}`: `#{EXTENSION_KEY}` extension is not of type Array"
|
|
103
|
+
assert service['extension'][EXTENSION_KEY].present?,
|
|
104
|
+
"Service `#{service['id']}`: `#{EXTENSION_KEY}` extension is empty"
|
|
105
|
+
non_string_values = service['extension'][EXTENSION_KEY].reject { |value| value.is_a? String }
|
|
106
|
+
assert non_string_values.blank?,
|
|
107
|
+
"Service `#{service['id']}`: `#{EXTENSION_KEY}` extension contains non-string values: " \
|
|
108
|
+
"#{non_string_values.join(', ')}"
|
|
109
|
+
|
|
110
|
+
invalid_versions =
|
|
111
|
+
service['extension'][EXTENSION_KEY]
|
|
112
|
+
.reject { |version| version.match?(/\A[1-9]\d*\.\d+\Z/) }
|
|
113
|
+
|
|
114
|
+
assert invalid_versions.blank?,
|
|
115
|
+
"Service `#{service['id']}`: `#{EXTENSION_KEY}` extension contains invalid " \
|
|
116
|
+
"version strings: #{invalid_versions.join(', ')}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require_relative '../../server_test_helper'
|
|
2
|
+
|
|
3
|
+
module DaVinciCRDTestKit
|
|
4
|
+
module V221
|
|
5
|
+
class DiscoveryStandardPrefetchExpressionsTest < Inferno::Test
|
|
6
|
+
include DaVinciCRDTestKit::ServerTestHelper
|
|
7
|
+
|
|
8
|
+
title 'Server uses standard prefetch expressions'
|
|
9
|
+
id :crd_v221_discovery_standard_prefetch_expressions
|
|
10
|
+
description %(
|
|
11
|
+
This test checks each prefetch expression value advertised by CRD services in the discovery
|
|
12
|
+
response against the standard prefetch expressions listed in the CRD IG for the service hook. If a prefetch
|
|
13
|
+
expression does not match one of the standard expressions for that hook, the test will add a warning.
|
|
14
|
+
|
|
15
|
+
These warnings should be checked individually, as the interpretation of the warning will depend on the server's
|
|
16
|
+
intent: For expressions that are intended to capture any of the data elements in that hook's set of
|
|
17
|
+
[standard prefetch queries](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/foundation.html#appointment-book-prefetch),
|
|
18
|
+
it must match the corresponding expression in the IG, and the warning should be considered an error. Note that
|
|
19
|
+
the expression keys do not need to match and are ignored by this test. FHIRPath alternative ordering is also
|
|
20
|
+
ignored by this test.
|
|
21
|
+
For expressions that are meant to capture other data in addition to what is captured by the standard prefetch
|
|
22
|
+
queries, the warning can be ignored.
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@found-29'
|
|
26
|
+
|
|
27
|
+
input :cds_services
|
|
28
|
+
input :crd_discovery_service_ignore_list,
|
|
29
|
+
optional: true
|
|
30
|
+
|
|
31
|
+
ALL_HOOKS = [
|
|
32
|
+
'appointment-book',
|
|
33
|
+
'encounter-start',
|
|
34
|
+
'encounter-discharge',
|
|
35
|
+
'order-dispatch',
|
|
36
|
+
'order-select',
|
|
37
|
+
'order-sign'
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
def ignored_service_ids
|
|
41
|
+
crd_discovery_service_ignore_list.to_s.split(',').map(&:strip).reject(&:blank?)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def services_for_crd_validation(services)
|
|
45
|
+
services
|
|
46
|
+
.select { |service| ignored_service_ids.include?(service['id']) }
|
|
47
|
+
.each do |service|
|
|
48
|
+
info "Ignoring service `#{service['id']}` because it is in the ignore list."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
services.reject { |service| ignored_service_ids.include?(service['id']) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# rubocop:disable Layout/LineLength
|
|
55
|
+
def standard_prefetch_expressions(hook)
|
|
56
|
+
case hook
|
|
57
|
+
when 'appointment-book'
|
|
58
|
+
{
|
|
59
|
+
'patient' => 'Patient/{{context.patientId}}',
|
|
60
|
+
'encounter' => 'Encounter/{{context.encounterId}}',
|
|
61
|
+
'coverage' => 'Coverage?patient={{context.patientId}}&status=active',
|
|
62
|
+
'deviceRequests' => "DeviceRequest?_id={{context.appointments.entry.resource.basedOn.extension('http://hl7.org/fhir/StructureDefinition/alternate-reference').value.resolve().ofType(DeviceRequest).id}}",
|
|
63
|
+
'serviceRequests' => 'ServiceRequest?_id={{context.appointments.entry.resource.basedOn.resolve().ofType(ServiceRequest).id}}',
|
|
64
|
+
'medicationRequests' => "MedicationRequest?_id={{context.appointments.entry.resource.basedOn.extension('http://hl7.org/fhir/StructureDefinition/alternate-reference').value.resolve().ofType(MedicationRequest).id}}",
|
|
65
|
+
'devices' => 'Device?_id={{%deviceRequests.entry.resource.code.resolve().id}}',
|
|
66
|
+
'medications' => 'Medication?_id={{%medicationRequests.entry.resource.medication.resolve().id}}',
|
|
67
|
+
'practitionerRoles' => 'PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|context.appointments.entry.resource.participant.actor.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id}}',
|
|
68
|
+
'practitioners' => 'Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|context.appointments.entry.resource.participant.actor.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id}}',
|
|
69
|
+
'organizations' => 'Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|%medicationRequests.entry.resource.dispenseRequest.performer.resolve().ofType(Organization).id|%serviceRequests.entry.resource.performer.resolve().ofType(Organization).id}}',
|
|
70
|
+
'locations' => 'Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|context.appointments.entry.resource.participant.actor.resolve().ofType(Location).id|%serviceRequests.entry.resource.locationReference.resolve().ofType(Location).id}}'
|
|
71
|
+
}
|
|
72
|
+
when 'encounter-start', 'encounter-discharge'
|
|
73
|
+
{
|
|
74
|
+
'patient' => 'Patient/{{context.patientId}}',
|
|
75
|
+
'encounter' => 'Encounter/{{context.encounterId}}',
|
|
76
|
+
'coverage' => 'Coverage?patient={{context.patientId}}&status=active',
|
|
77
|
+
'practitionerRoles' => 'PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id}}',
|
|
78
|
+
'practitioners' => 'Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id}}',
|
|
79
|
+
'organizations' => 'Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id}}',
|
|
80
|
+
'locations' => 'Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id}}'
|
|
81
|
+
}
|
|
82
|
+
when 'order-dispatch'
|
|
83
|
+
{
|
|
84
|
+
'patient' => 'Patient/{{context.patientId}}',
|
|
85
|
+
'encounter' => 'Encounter/{{context.encounterId}}',
|
|
86
|
+
'coverage' => 'Coverage?patient={{context.patientId}}&status=active',
|
|
87
|
+
'communicationRequests' => 'CommunicationRequest?_id={{context.dispatchedOrders.resolve().ofType(CommunicationRequest).id}}',
|
|
88
|
+
'deviceRequests' => 'DeviceRequest?_id={{context.dispatchedOrders.resolve().ofType(DeviceRequest).id}}',
|
|
89
|
+
'medicationRequests' => 'MedicationRequest?_id={{context.dispatchedOrders.resolve().ofType(MedicationRequest).id}}',
|
|
90
|
+
'nutritionOrders' => 'NutritionOrder?_id={{context.dispatchedOrders.resolve().ofType(NutritionOrder).id}}',
|
|
91
|
+
'serviceRequests' => 'ServiceRequest?_id={{context.dispatchedOrders.resolve().ofType(ServiceRequest).id}}',
|
|
92
|
+
'visionPrescriptions' => 'VisionPrescription?_id={{context.dispatchedOrders.resolve().ofType(VisionPrescription).id}}',
|
|
93
|
+
'devices' => 'Device?_id={{%deviceRequests.entry.resource.code.resolve().id}}',
|
|
94
|
+
'medications' => 'Medication?_id={{%medicationRequests.entry.resource.medication.resolve().id}}',
|
|
95
|
+
'practitionerRoles' => 'PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.sender.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.recipient.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}',
|
|
96
|
+
'practitioners' => 'Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.sender.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(Practitioner).id}}',
|
|
97
|
+
'organizations' => 'Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Organization).id|%communicationRequests.entry.resource.sender.resolve().ofType(Organization).id|%medicationRequests.entry.resource.dispenseRequest.performer.resolve().id|%serviceRequests.entry.resource.performer.resolve().ofType(Organization).id}}',
|
|
98
|
+
'locations' => 'Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|%serviceRequests.entry.resource.locationReference.resolve().id}}'
|
|
99
|
+
}
|
|
100
|
+
when 'order-select', 'order-sign'
|
|
101
|
+
{
|
|
102
|
+
'patient' => 'Patient/{{context.patientId}}',
|
|
103
|
+
'encounter' => 'Encounter/{{context.encounterId}}',
|
|
104
|
+
'coverage' => 'Coverage?patient={{context.patientId}}&status=active',
|
|
105
|
+
'devices' => 'Device?_id={{context.draftOrders.entry.resource.ofType(DeviceRequest).code.resolve().id}}',
|
|
106
|
+
'medications' => 'Medication?_id={{context.draftOrders.entry.resource.ofType(MedicationRequest).medication.resolve().id}}',
|
|
107
|
+
'practitionerRoles' => 'PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.sender.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.recipient.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.requester.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.performer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}',
|
|
108
|
+
'practitioners' => 'Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.sender.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.requester.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.performer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(Practitioner).id}}',
|
|
109
|
+
'organizations' => 'Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|context.draftOrders.entry.resource.dispenseRequest.performer.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Organization).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Organization).id|context.draftOrders.entry.resource.performer.resolve().ofType(Organization).id}}',
|
|
110
|
+
'locations' => 'Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}'
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
# rubocop:enable Layout/LineLength
|
|
115
|
+
|
|
116
|
+
# The IG examples contain FHIRPath alternatives separated by `|`, and CDS Hooks prefetch field names can vary.
|
|
117
|
+
# Build a canonical signature so alternative ordering and `%prefetchKey` names do not affect matching.
|
|
118
|
+
def expression_signature(expression)
|
|
119
|
+
expression = expression.to_s
|
|
120
|
+
# CRD's standard expressions use one `{{...}}` token; split that token into its surrounding request string.
|
|
121
|
+
match = expression.match(/\A(.*?)\{\{(.*)\}\}(.*)\z/)
|
|
122
|
+
return expression unless match
|
|
123
|
+
|
|
124
|
+
prefix, fhirpath_expression, suffix = match.captures
|
|
125
|
+
alternatives =
|
|
126
|
+
fhirpath_expression
|
|
127
|
+
.split('|')
|
|
128
|
+
.map { |alternative| alternative.gsub(/%[A-Za-z][A-Za-z0-9_-]*/, '%prefetch') }
|
|
129
|
+
.uniq
|
|
130
|
+
.sort
|
|
131
|
+
|
|
132
|
+
[prefix, alternatives, suffix]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
run do
|
|
136
|
+
object = parse_json(cds_services)
|
|
137
|
+
assert object['services'], 'Discovery response did not contain `services`'
|
|
138
|
+
|
|
139
|
+
services = object['services']
|
|
140
|
+
assert services.is_a?(Array), 'Services field of the CDS Discovery response object is not an array.'
|
|
141
|
+
|
|
142
|
+
prefetch_services =
|
|
143
|
+
services_for_crd_validation(services)
|
|
144
|
+
.select { |service| service['prefetch'].is_a?(Hash) && service['prefetch'].present? }
|
|
145
|
+
|
|
146
|
+
skip_if prefetch_services.blank?, 'No CRD services advertised prefetch support'
|
|
147
|
+
|
|
148
|
+
prefetch_services.each do |service|
|
|
149
|
+
next unless ALL_HOOKS.include?(service['hook'])
|
|
150
|
+
|
|
151
|
+
standard_expression_signatures =
|
|
152
|
+
standard_prefetch_expressions(service['hook']).values.map { |expression| expression_signature(expression) }
|
|
153
|
+
|
|
154
|
+
service['prefetch'].each do |prefetch_key, prefetch_value|
|
|
155
|
+
next if standard_expression_signatures.include?(expression_signature(prefetch_value))
|
|
156
|
+
|
|
157
|
+
msg = "Service `#{service['id']}` advertises prefetch expression `#{prefetch_value}` " \
|
|
158
|
+
"for hook `#{service['hook']}` in prefetch field `#{prefetch_key}`, which does not match " \
|
|
159
|
+
'one of the standard CRD prefetch expressions for that hook.'
|
|
160
|
+
add_message('warning', msg)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_single_request_test.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative '../server_urls'
|
|
2
|
+
require_relative '../../jobs/invoke_hook_no_special_requests'
|
|
3
|
+
require_relative '../../server_abstract_invoke_hook_test'
|
|
4
|
+
|
|
5
|
+
module DaVinciCRDTestKit
|
|
6
|
+
module V221
|
|
7
|
+
class InvokeHookSingleTest < ServerAbstractInvokeHookTest
|
|
8
|
+
include ServerURLs
|
|
9
|
+
|
|
10
|
+
title 'Inferno invokes the selected hook with the provided request body'
|
|
11
|
+
id :crd_v221_server_invoke_hook_single_request_test
|
|
12
|
+
|
|
13
|
+
def perform_invoke_hook_job(*)
|
|
14
|
+
Inferno::Jobs.perform(DaVinciCRDTestKit::Jobs::InvokeHookNoSpecialRequests, *)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check_request_length(payloads)
|
|
18
|
+
skip_if payloads.length != 1, 'This test supports only one request body.'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative '../server_urls'
|
|
2
|
+
require_relative '../../server_abstract_invoke_hook_test'
|
|
3
|
+
|
|
4
|
+
module DaVinciCRDTestKit
|
|
5
|
+
module V221
|
|
6
|
+
class ServerInvokeHookTest < ServerAbstractInvokeHookTest
|
|
7
|
+
include ServerURLs
|
|
8
|
+
|
|
9
|
+
title 'Inferno invokes the selected hook'
|
|
10
|
+
id :crd_v221_server_invoke_hook_test
|
|
11
|
+
|
|
12
|
+
def coverage_info_configuration_supported?
|
|
13
|
+
true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|