davinci_crd_test_kit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/davinci_crd_test_kit/card_responses/companions_prerequisites.json +58 -0
  4. data/lib/davinci_crd_test_kit/card_responses/create_update_coverage_information.json +20 -0
  5. data/lib/davinci_crd_test_kit/card_responses/external_reference.json +21 -0
  6. data/lib/davinci_crd_test_kit/card_responses/instructions.json +14 -0
  7. data/lib/davinci_crd_test_kit/card_responses/launch_smart_app.json +21 -0
  8. data/lib/davinci_crd_test_kit/card_responses/propose_alternate_request.json +71 -0
  9. data/lib/davinci_crd_test_kit/card_responses/request_form_completion.json +227 -0
  10. data/lib/davinci_crd_test_kit/cards_validation.rb +234 -0
  11. data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +762 -0
  12. data/lib/davinci_crd_test_kit/client_hook_request_validation.rb +15 -0
  13. data/lib/davinci_crd_test_kit/client_hooks_group.rb +706 -0
  14. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +71 -0
  15. data/lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb +48 -0
  16. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +40 -0
  17. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_read_test.rb +39 -0
  18. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +232 -0
  19. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +40 -0
  20. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +60 -0
  21. data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +40 -0
  22. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +68 -0
  23. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +68 -0
  24. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +41 -0
  25. data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +40 -0
  26. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +63 -0
  27. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +151 -0
  28. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +79 -0
  29. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +76 -0
  30. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +79 -0
  31. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +65 -0
  32. data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +34 -0
  33. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +61 -0
  34. data/lib/davinci_crd_test_kit/crd_client_suite.rb +156 -0
  35. data/lib/davinci_crd_test_kit/crd_jwks.json +59 -0
  36. data/lib/davinci_crd_test_kit/crd_options.rb +9 -0
  37. data/lib/davinci_crd_test_kit/crd_server_suite.rb +115 -0
  38. data/lib/davinci_crd_test_kit/ext/inferno_core/runnable.rb +22 -0
  39. data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +410 -0
  40. data/lib/davinci_crd_test_kit/jwks.rb +25 -0
  41. data/lib/davinci_crd_test_kit/jwt_helper.rb +74 -0
  42. data/lib/davinci_crd_test_kit/mock_service_response.rb +421 -0
  43. data/lib/davinci_crd_test_kit/routes/cds-services.json +74 -0
  44. data/lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb +18 -0
  45. data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +93 -0
  46. data/lib/davinci_crd_test_kit/routes/jwk_set_endpoint_handler.rb +15 -0
  47. data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +173 -0
  48. data/lib/davinci_crd_test_kit/server_discovery_group.rb +59 -0
  49. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +144 -0
  50. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +144 -0
  51. data/lib/davinci_crd_test_kit/server_hook_request_validation.rb +15 -0
  52. data/lib/davinci_crd_test_kit/server_hooks_group.rb +69 -0
  53. data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +173 -0
  54. data/lib/davinci_crd_test_kit/server_order_select_group.rb +169 -0
  55. data/lib/davinci_crd_test_kit/server_order_sign_group.rb +198 -0
  56. data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +23 -0
  57. data/lib/davinci_crd_test_kit/server_tests/additional_orders_validation_test.rb +70 -0
  58. data/lib/davinci_crd_test_kit/server_tests/card_optional_fields_validation_test.rb +47 -0
  59. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_across_hooks_validation_test.rb +32 -0
  60. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +58 -0
  61. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +121 -0
  62. data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +72 -0
  63. data/lib/davinci_crd_test_kit/server_tests/discovery_endpoint_test.rb +88 -0
  64. data/lib/davinci_crd_test_kit/server_tests/discovery_services_validation_test.rb +65 -0
  65. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_across_hooks_validation_test.rb +28 -0
  66. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +36 -0
  67. data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +79 -0
  68. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_across_hooks_test.rb +25 -0
  69. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_test.rb +28 -0
  70. data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +38 -0
  71. data/lib/davinci_crd_test_kit/server_tests/propose_alternate_request_card_validation_test.rb +65 -0
  72. data/lib/davinci_crd_test_kit/server_tests/service_call_test.rb +86 -0
  73. data/lib/davinci_crd_test_kit/server_tests/service_request_context_validation_test.rb +30 -0
  74. data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +41 -0
  75. data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +43 -0
  76. data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +82 -0
  77. data/lib/davinci_crd_test_kit/suggestion_actions_validation.rb +123 -0
  78. data/lib/davinci_crd_test_kit/tags.rb +8 -0
  79. data/lib/davinci_crd_test_kit/test_helper.rb +23 -0
  80. data/lib/davinci_crd_test_kit/urls.rb +52 -0
  81. data/lib/davinci_crd_test_kit/version.rb +3 -0
  82. data/lib/davinci_crd_test_kit.rb +2 -0
  83. metadata +170 -0
@@ -0,0 +1,68 @@
1
+ require_relative '../urls'
2
+
3
+ module DaVinciCRDTestKit
4
+ class EncounterStartReceiveRequestTest < Inferno::Test
5
+ include URLs
6
+
7
+ id :crd_encounter_start_request
8
+ title 'Request received for encounter-start hook'
9
+ description %(
10
+ This test waits for an incoming [encounter-start](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html#encounter-start)
11
+ hook request and responds to the client with the response types selected as an input.
12
+ )
13
+ receives_request :encounter_start
14
+
15
+ input :iss
16
+ input :encounter_start_selected_response_types,
17
+ title: 'Response types to return from encounter-start hook requests',
18
+ description: %(
19
+ Select the cards/action response types that the Inferno hook request endpoints will return. The default
20
+ response type that will be returned for this hook is the `Instructions` card type.
21
+ ),
22
+ type: 'checkbox',
23
+ default: ['instructions'],
24
+ options: {
25
+ list_options: [
26
+ {
27
+ label: 'External Reference',
28
+ value: 'external_reference'
29
+ },
30
+ {
31
+ label: 'Instructions',
32
+ value: 'instructions'
33
+ },
34
+ {
35
+ label: 'Coverage Information',
36
+ value: 'coverage_information'
37
+ },
38
+ {
39
+ label: 'Request Form Completion',
40
+ value: 'request_form_completion'
41
+ },
42
+ {
43
+ label: 'Create/Update Coverage Information',
44
+ value: 'create_update_coverage_info'
45
+ },
46
+ {
47
+ label: 'Launch SMART Application',
48
+ value: 'launch_smart_app'
49
+ }
50
+ ]
51
+ }
52
+
53
+ run do
54
+ wait(
55
+ identifier: "encounter-start #{iss}",
56
+ message: %(
57
+ **Encounter Start CDS Service Test**:
58
+
59
+ Invoke the encounter-start hook and send a request to:
60
+
61
+ `#{encounter_start_url}`
62
+
63
+ Inferno will process the request and return CDS cards if successful.
64
+ )
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../client_hook_request_validation'
2
+
3
+ module DaVinciCRDTestKit
4
+ class HookRequestOptionalFieldsTest < Inferno::Test
5
+ include DaVinciCRDTestKit::ClientHookRequestValidation
6
+
7
+ id :crd_hook_request_optional_fields
8
+ title 'Hook request contains optional fields'
9
+ description %(
10
+ Under the [CDS hooks HTTP Request section](https://cds-hooks.hl7.org/2.0/#http-request_1), the specification
11
+ requires that a CDS service request SHALL include a JSON POST body which MAY contain the following optional input
12
+ fields:
13
+ * `fhirServer` - *URL*
14
+ * `fhirAuthorization` - *object*
15
+ * `prefetch` - *object*
16
+
17
+ This test checks for the precense of these fields and if they are of the correct type. This test is optional and
18
+ will not fail if the hook request does not contain an optional field, it only produces an informational message.
19
+ If the client provides its FHIR server URL in the `fhirServer` field, and it's authorization token in the
20
+ `fhirAuthorization` field object, they will be produced as an output from this test to be used in
21
+ subsequent tests.
22
+ )
23
+ optional
24
+
25
+ output :client_fhir_server
26
+ output :client_access_token,
27
+ optional: true
28
+
29
+ uses_request :hook_request
30
+
31
+ run do
32
+ assert_valid_json(request.request_body)
33
+ request_body = JSON.parse(request.request_body)
34
+
35
+ client_fhir_server = hook_request_optional_fields_check(request_body)
36
+
37
+ output client_fhir_server: client_fhir_server[:fhir_server_uri],
38
+ client_access_token: client_fhir_server[:fhir_access_token]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ require_relative '../client_hook_request_validation'
2
+
3
+ module DaVinciCRDTestKit
4
+ class HookRequestRequiredFieldsTest < Inferno::Test
5
+ include DaVinciCRDTestKit::ClientHookRequestValidation
6
+ include URLs
7
+
8
+ id :crd_hook_request_required_fields
9
+ title 'Hook request contains required fields'
10
+ description %(
11
+ Under the [CDS hooks HTTP Request section](https://cds-hooks.hl7.org/2.0/#http-request_1), the specification
12
+ requires that a CDS service request SHALL include a JSON POST body with the following input fields:
13
+ * `hook` - *string*
14
+ * `hookInstance` - *string*
15
+ * `context` - *object*
16
+
17
+ Additionally, if the optional `fhirAuthorization` field is present, then the `fhirServer` field is required.
18
+
19
+ This test also checks that the `hook` field contains the correct CDS service name that the CDS client is sending
20
+ a request for
21
+ )
22
+
23
+ uses_request :hook_request
24
+
25
+ def hook_url
26
+ base_url + config.options[:hook_path]
27
+ end
28
+
29
+ def hook_name
30
+ config.options[:hook_name]
31
+ end
32
+
33
+ run do
34
+ assert_valid_json(request.request_body)
35
+ request_body = JSON.parse(request.request_body)
36
+
37
+ hook_request_required_fields_check(request_body, hook_name)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ require_relative '../client_hook_request_validation'
2
+ module DaVinciCRDTestKit
3
+ class HookRequestValidContextTest < Inferno::Test
4
+ include URLs
5
+ include ClientHookRequestValidation
6
+
7
+ id :crd_hook_request_valid_context
8
+ title 'Hook contains valid context'
9
+ description %(
10
+ As stated in the [CDS hooks specification](https://cds-hooks.hl7.org/2.0#http-request), a CDS service request's
11
+ `context` field contains hook-specific contextual data that the CDS service will need. The context is specified
12
+ in the hook definition to guide developers on the information available at the point in the workflow when the hook
13
+ is triggered.
14
+
15
+ The `context` requirements for each [hook specified in the CRD IG](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html)
16
+ can be found below:
17
+ * [appointment-book](https://cds-hooks.hl7.org/hooks/appointment-book/2023SepSTU1Ballot/appointment-book/)
18
+ * [encounter-start](https://cds-hooks.hl7.org/hooks/encounter-start/2023SepSTU1Ballot/encounter-start/)
19
+ * [encounter-discharge](https://cds-hooks.hl7.org/hooks/encounter-discharge/2023SepSTU1Ballot/encounter-discharge/)
20
+ * [order-select](https://cds-hooks.hl7.org/hooks/order-select/2023SepSTU1Ballot/order-select/)
21
+ * [order-dispatch](https://cds-hooks.hl7.org/hooks/order-dispatch/2023SepSTU1Ballot/order-dispatch/)
22
+ * [order-sign](https://cds-hooks.org/hooks/order-sign/)
23
+
24
+ This test performs the following:
25
+ * Verifies that the incoming hook request's `context` field contains the fields required by each hook and
26
+ that they are in the correct format
27
+ * Checks the optional fields and ensures they are in the correct format
28
+ * Validates any resources contained in a `context` field that contains a Bundle or FHIR resource
29
+ * Makes FHIR requests for any `context` fields that contain an id or reference and validates each resource
30
+ response against its corresponding CRD resource profile
31
+ * Check some specific `context` requirements for hooks that have special requirements for certain fields
32
+
33
+ The client must provide its FHIR server URL and access token in the hook request in order to run
34
+ this test.
35
+ )
36
+ uses_request :hook_request
37
+
38
+ input :client_fhir_server
39
+ input :client_access_token,
40
+ optional: true
41
+
42
+ fhir_client do
43
+ url :client_fhir_server
44
+ bearer_token :client_access_token
45
+ end
46
+
47
+ def hook_name
48
+ config.options[:hook_name]
49
+ end
50
+
51
+ run do
52
+ assert_valid_json(request.request_body)
53
+ request_body = JSON.parse(request.request_body)
54
+
55
+ hook_context = request_body['context']
56
+
57
+ assert(hook_context, 'Hook request does not contain required `context` field')
58
+
59
+ hook_request_context_check(hook_context, hook_name)
60
+ no_error_validation('Context is not valid.')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,151 @@
1
+ require_relative '../urls'
2
+
3
+ module DaVinciCRDTestKit
4
+ class HookRequestValidPrefetchTest < Inferno::Test
5
+ include URLs
6
+
7
+ id :crd_hook_request_valid_prefetch
8
+ title 'Hook contains valid prefetch response'
9
+ description %(
10
+ As stated in the [CDS hooks specification](https://cds-hooks.hl7.org/2.0#http-request), a CDS service request's
11
+ `prefetch` field is an optional field that contains key/value pairs of FHIR queries that the service is requesting
12
+ the CDS Client to perform and provide on each service call. The key is a string that describes the type of data
13
+ being requested and the value is a string representing the FHIR query. See [Prefetch Template](https://cds-hooks.hl7.org/2.0#prefetch-template)
14
+ for more information about how the `prefetch` formatting works.
15
+
16
+ This test verifies that the incoming hook request's `prefetch` field is in a valid JSON format and validates each
17
+ contained resource against its corresponding CRD resource profile. This test is optional and will be skipped if no
18
+ `prefetch` field is contained in the hook request.
19
+ )
20
+ optional
21
+
22
+ uses_request :hook_request
23
+
24
+ def hook_name
25
+ config.options[:hook_name]
26
+ end
27
+
28
+ def cds_services_json
29
+ JSON.parse(File.read(File.join(
30
+ __dir__, '..', 'routes', 'cds-services.json'
31
+ )))['services']
32
+ end
33
+
34
+ def structure_definition_map
35
+ {
36
+ 'Practitioner' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-practitioner',
37
+ 'PractitionerRole' => 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitionerrole',
38
+ 'Patient' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-patient',
39
+ 'Coverage' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-coverage',
40
+ 'RelatedPerson' => 'http://hl7.org/fhir/StructureDefinition/RelatedPerson',
41
+ 'Encounter' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-encounter',
42
+ 'DeviceRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-devicerequest',
43
+ 'MedicationRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-medicationrequest',
44
+ 'NutritionOrder' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-nutritionorder',
45
+ 'ServiceRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest',
46
+ 'VisionPrescription' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-visionprescription'
47
+ }
48
+ end
49
+
50
+ def validate_prefetch_coverage(received_resource, advertised_prefetch_key,
51
+ received_context_patient_id, advertised_status)
52
+ assert_resource_type('Bundle', resource: received_resource)
53
+ assert(received_resource.entry.any?, 'Bundle of coverage resources received from prefetch is empty')
54
+ coverage_resource = received_resource.entry.first.resource
55
+ assert_resource_type('Coverage', resource: coverage_resource)
56
+ assert_valid_resource(resource: coverage_resource,
57
+ profile_url: structure_definition_map['Coverage'])
58
+
59
+ coverage_beneficiary_reference = coverage_resource.beneficiary
60
+ coverage_beneficiary_patient_id = coverage_beneficiary_reference.reference_id
61
+ assert(coverage_beneficiary_patient_id.present?,
62
+ "Could not get beneficiary reference id from `#{advertised_prefetch_key}` field's Coverage resource")
63
+
64
+ assert(coverage_beneficiary_patient_id == received_context_patient_id,
65
+ %(Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `beneficiary` reference id of
66
+ '#{received_context_patient_id}', instead was '#{coverage_beneficiary_patient_id}'))
67
+
68
+ coverage_status = coverage_resource.status
69
+ assert(coverage_status == advertised_status,
70
+ %(Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `status` of
71
+ '#{advertised_status}', instead was '#{coverage_status}'))
72
+ end
73
+
74
+ def validate_prefetch_resource(received_resource, advertised_prefetch_key, context_field_resource_type,
75
+ context_field_id)
76
+ assert_resource_type(context_field_resource_type, resource: received_resource)
77
+
78
+ if hook_name == 'order-dispatch'
79
+ assert_valid_resource(resource: received_resource)
80
+ else
81
+ assert_valid_resource(resource: received_resource,
82
+ profile_url: structure_definition_map[context_field_resource_type])
83
+ end
84
+
85
+ received_prefetch_resource_id = received_resource.id
86
+ assert(received_prefetch_resource_id.present?,
87
+ "`#{advertised_prefetch_key}` field's FHIR resource does not contain the `id` field")
88
+ assert(received_prefetch_resource_id == context_field_id,
89
+ %(Expected `#{advertised_prefetch_key}` field's FHIR resource to have an `id` of '#{context_field_id}',
90
+ instead was '#{received_prefetch_resource_id}'))
91
+ end
92
+
93
+ run do
94
+ assert_valid_json(request.request_body)
95
+ request_body = JSON.parse(request.request_body)
96
+
97
+ received_prefetch = request_body['prefetch']
98
+ received_context = request_body['context']
99
+
100
+ skip_if received_prefetch.blank?, 'Received hook request does not contain the `prefetch` field.'
101
+ skip_if received_context.blank?,
102
+ %(Received hook request does not contain the `context` field which is needed to validate the `prefetch`
103
+ field)
104
+
105
+ advertised_hook_service = cds_services_json.find { |service| service['hook'] == hook_name }
106
+
107
+ advertised_prefetch_fields = advertised_hook_service['prefetch']
108
+
109
+ advertised_prefetch_fields.each do |advertised_prefetch_key, advertised_prefetch_template|
110
+ next unless received_prefetch[advertised_prefetch_key].present?
111
+
112
+ assert(received_prefetch[advertised_prefetch_key].is_a?(Hash),
113
+ "Prefetch field `#{advertised_prefetch_key}` is not of type `Hash`.")
114
+
115
+ received_prefetch_resource = FHIR.from_contents(received_prefetch[advertised_prefetch_key].to_json)
116
+
117
+ if advertised_prefetch_template.include?('?')
118
+ advertised_prefetch_fhir_search = advertised_prefetch_template.gsub(/{|}/, '').split('?')
119
+ advertised_prefetch_resource_type = advertised_prefetch_fhir_search.first
120
+
121
+ if advertised_prefetch_resource_type == 'Coverage'
122
+ advertised_coverage_query_params = Rack::Utils.parse_nested_query(advertised_prefetch_fhir_search.last)
123
+
124
+ advertised_patient_token = advertised_coverage_query_params['patient']
125
+ advertised_context_patient_id_key = advertised_patient_token.split('.').last
126
+ received_context_patient_id = received_context[advertised_context_patient_id_key]
127
+
128
+ advertised_status_param = advertised_coverage_query_params['status']
129
+
130
+ validate_prefetch_coverage(received_prefetch_resource, advertised_prefetch_key, received_context_patient_id,
131
+ advertised_status_param)
132
+ end
133
+ else
134
+ advertised_prefetch_token = advertised_prefetch_template.gsub(/{|}/, '').split('/')
135
+ advertised_context_id = advertised_prefetch_token.last.split('.').last
136
+
137
+ if advertised_prefetch_token.length == 1
138
+ received_context_reference = FHIR::Reference.new(reference: received_context[advertised_context_id])
139
+ received_context_resource_type = received_context_reference.resource_type
140
+ received_context_id = received_context_reference.reference_id
141
+ else
142
+ received_context_id = received_context[advertised_context_id]
143
+ received_context_resource_type = advertised_prefetch_token.first
144
+ end
145
+ validate_prefetch_resource(received_prefetch_resource, advertised_prefetch_key,
146
+ received_context_resource_type, received_context_id)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,79 @@
1
+ require_relative '../urls'
2
+
3
+ module DaVinciCRDTestKit
4
+ class OrderDispatchReceiveRequestTest < Inferno::Test
5
+ include URLs
6
+
7
+ id :crd_order_dispatch_request
8
+ title 'Request received for order-dispatch hook'
9
+ description %(
10
+ This test waits for an incoming [order-dispatch](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html#order-dispatch)
11
+ hook request and responds to the client with the response types selected as an input. This hook is a 'primary'
12
+ hook, meaning that CRD Servers SHALL, at minimum, return a [Coverage Information](https://hl7.org/fhir/us/davinci-crd/STU2/StructureDefinition-ext-coverage-information.html)
13
+ system action for these hooks, even if the response indicates that further information is needed or that the
14
+ level of detail provided is insufficient to determine coverage.
15
+ )
16
+ receives_request :order_dispatch
17
+
18
+ input :iss
19
+ input :order_dispatch_selected_response_types,
20
+ title: 'Response types to return from order-dispatch hook requests',
21
+ description: %(
22
+ Select the cards/action response types that the Inferno hook request endpoints will return. The default
23
+ response type that will be returned for this hook is the `Coverage Information` card type.
24
+ ),
25
+ type: 'checkbox',
26
+ default: ['coverage_information'],
27
+ options: {
28
+ list_options: [
29
+ {
30
+ label: 'External Reference',
31
+ value: 'external_reference'
32
+ },
33
+ {
34
+ label: 'Instructions',
35
+ value: 'instructions'
36
+ },
37
+ {
38
+ label: 'Coverage Information',
39
+ value: 'coverage_information'
40
+ },
41
+ {
42
+ label: 'Request Form Completion',
43
+ value: 'request_form_completion'
44
+ },
45
+ {
46
+ label: 'Create/Update Coverage Information',
47
+ value: 'create_update_coverage_info'
48
+ },
49
+ {
50
+ label: 'Launch SMART Application',
51
+ value: 'launch_smart_app'
52
+ },
53
+ {
54
+ label: 'Propose Alternate Request',
55
+ value: 'propose_alternate_request'
56
+ },
57
+ {
58
+ label: 'Additional Orders as Companions/Prerequisites',
59
+ value: 'companions_prerequisites'
60
+ }
61
+ ]
62
+ }
63
+
64
+ run do
65
+ wait(
66
+ identifier: "order-dispatch #{iss}",
67
+ message: %(
68
+ **Order Dispatch CDS Service Test**:
69
+
70
+ Invoke the order-dispatch hook and send a request to:
71
+
72
+ `#{order_dispatch_url}`
73
+
74
+ Inferno will process the request and return CDS cards if successful.
75
+ )
76
+ )
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,76 @@
1
+ require_relative '../urls'
2
+
3
+ module DaVinciCRDTestKit
4
+ class OrderSelectReceiveRequestTest < Inferno::Test
5
+ include URLs
6
+
7
+ id :crd_order_select_request
8
+ title 'Request received for order-select hook'
9
+ description %(
10
+ This test waits for an incoming [order-select](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html#order-select)
11
+ hook request and responds to the client with the response types selected as an input.
12
+ )
13
+ receives_request :order_select
14
+
15
+ input :iss
16
+ input :order_select_selected_response_types,
17
+ title: 'Response types to return from order-select hook requests',
18
+ description: %(
19
+ Select the cards/action response types that the Inferno hook request endpoints will return. The default
20
+ response type that will be returned for this hook is the `Instructions` card type.
21
+ ),
22
+ type: 'checkbox',
23
+ default: ['instructions'],
24
+ options: {
25
+ list_options: [
26
+ {
27
+ label: 'External Reference',
28
+ value: 'external_reference'
29
+ },
30
+ {
31
+ label: 'Instructions',
32
+ value: 'instructions'
33
+ },
34
+ {
35
+ label: 'Coverage Information',
36
+ value: 'coverage_information'
37
+ },
38
+ {
39
+ label: 'Request Form Completion',
40
+ value: 'request_form_completion'
41
+ },
42
+ {
43
+ label: 'Create/Update Coverage Information',
44
+ value: 'create_update_coverage_info'
45
+ },
46
+ {
47
+ label: 'Launch SMART Application',
48
+ value: 'launch_smart_app'
49
+ },
50
+ {
51
+ label: 'Propose Alternate Request',
52
+ value: 'propose_alternate_request'
53
+ },
54
+ {
55
+ label: 'Additional Orders as Companions/Prerequisites',
56
+ value: 'companions_prerequisites'
57
+ }
58
+ ]
59
+ }
60
+
61
+ run do
62
+ wait(
63
+ identifier: "order-select #{iss}",
64
+ message: %(
65
+ **Order Select CDS Service Test**:
66
+
67
+ Invoke the order-select hook and send a request to:
68
+
69
+ `#{order_select_url}`
70
+
71
+ Inferno will process the request and return CDS cards if successful.
72
+ )
73
+ )
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,79 @@
1
+ require_relative '../urls'
2
+
3
+ module DaVinciCRDTestKit
4
+ class OrderSignReceiveRequestTest < Inferno::Test
5
+ include URLs
6
+
7
+ id :crd_order_sign_request
8
+ title 'Request received for order-sign hook'
9
+ description %(
10
+ This test waits for an incoming [order-sign](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html#order-sign)
11
+ hook request and responds to the client with the response types selected as an input. This hook is a 'primary'
12
+ hook, meaning that CRD Servers SHALL, at minimum, return a [Coverage Information](https://hl7.org/fhir/us/davinci-crd/STU2/StructureDefinition-ext-coverage-information.html)
13
+ system action for these hooks, even if the response indicates that further information is needed or that the
14
+ level of detail provided is insufficient to determine coverage.
15
+ )
16
+ receives_request :order_sign
17
+
18
+ input :iss
19
+ input :order_sign_selected_response_types,
20
+ title: 'Response types to return from order-sign hook requests',
21
+ description: %(
22
+ Select the cards/action response types that the Inferno hook request endpoints will return. The default
23
+ response type that will be returned for this hook is the `Coverage Information` card type.
24
+ ),
25
+ type: 'checkbox',
26
+ default: ['coverage_information'],
27
+ options: {
28
+ list_options: [
29
+ {
30
+ label: 'External Reference',
31
+ value: 'external_reference'
32
+ },
33
+ {
34
+ label: 'Instructions',
35
+ value: 'instructions'
36
+ },
37
+ {
38
+ label: 'Coverage Information',
39
+ value: 'coverage_information'
40
+ },
41
+ {
42
+ label: 'Request Form Completion',
43
+ value: 'request_form_completion'
44
+ },
45
+ {
46
+ label: 'Create/Update Coverage Information',
47
+ value: 'create_update_coverage_info'
48
+ },
49
+ {
50
+ label: 'Launch SMART Application',
51
+ value: 'launch_smart_app'
52
+ },
53
+ {
54
+ label: 'Propose Alternate Request',
55
+ value: 'propose_alternate_request'
56
+ },
57
+ {
58
+ label: 'Additional Orders as Companions/Prerequisites',
59
+ value: 'companions_prerequisites'
60
+ }
61
+ ]
62
+ }
63
+
64
+ run do
65
+ wait(
66
+ identifier: "order-sign #{iss}",
67
+ message: %(
68
+ **Order Sign CDS Service Test**:
69
+
70
+ Invoke the order-sign hook and send a request to:
71
+
72
+ `#{order_sign_url}`
73
+
74
+ Inferno will process the request and return CDS cards if successful.
75
+ )
76
+ )
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,65 @@
1
+ module DaVinciCRDTestKit
2
+ class RetrieveJWKSTest < Inferno::Test
3
+ id :crd_retrieve_jwks
4
+ title 'JWKS can be retrieved'
5
+ description %(
6
+ Verify that the JWKS can be retrieved from the JWKS uri if it is present in the `jku` field within the JWT token
7
+ header. As per the [CDS hooks specification](https://cds-hooks.hl7.org/2.0#trusting-cds-clients), if the jku
8
+ header field is ommitted, the CDS Client and CDS Service SHALL communicate the JWK Set out-of-band. Therefore,
9
+ if the client does not make their keys publicly available via a uri in the `jku` field, the user must
10
+ submit the jwk_set as an input to the test.
11
+ )
12
+
13
+ input :auth_token_header_json
14
+ input :jwk_set,
15
+ title: "The Client's JWK Set containing it's public key",
16
+ description: %(
17
+ Must supply if you do not make your keys publicly available via a uri in the authorization JWT header `jku`
18
+ field'
19
+ ),
20
+ type: 'textarea',
21
+ optional: true
22
+ output :crd_jwks_json, :crd_jwks_keys_json
23
+ makes_request :crd_client_jwks
24
+
25
+ run do
26
+ token_header = JSON.parse(auth_token_header_json)
27
+ jku = token_header['jku']
28
+
29
+ if jku.present?
30
+ get(jku, name: :crd_client_jwks)
31
+
32
+ assert_response_status(200)
33
+ assert_valid_json(response[:body])
34
+ output crd_jwks_json: response[:body]
35
+
36
+ jwks = JSON.parse(response[:body])
37
+ else
38
+ skip_if jwk_set.blank?,
39
+ %(JWK Set must be inputted if Client's JWK Set is not available via a URL identified by the jku header
40
+ field)
41
+
42
+ jwks = JSON.parse(jwk_set)
43
+ end
44
+
45
+ keys = jwks['keys']
46
+ assert keys.is_a?(Array), 'JWKS `keys` field must be an array'
47
+
48
+ assert keys.present?, 'The JWK set returned contains no public keys'
49
+
50
+ keys.each do |jwk|
51
+ JWT::JWK.import(jwk.deep_symbolize_keys)
52
+ rescue StandardError
53
+ assert false, "Invalid JWK: #{jwk.to_json}"
54
+ end
55
+
56
+ kid_presence = keys.all? { |key| key['kid'].present? }
57
+ assert kid_presence, '`kid` field must be present in each key if JWKS contains multiple keys'
58
+
59
+ kid_uniqueness = keys.map { |key| key['kid'] }.uniq.length == keys.length
60
+ assert kid_uniqueness, '`kid` must be unique within the client\' JWK Set.'
61
+
62
+ output crd_jwks_keys_json: keys.to_json
63
+ end
64
+ end
65
+ end