davinci_crd_test_kit 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/lib/davinci_crd_test_kit/card_responses/companions_prerequisites.json +58 -0
- data/lib/davinci_crd_test_kit/card_responses/create_update_coverage_information.json +20 -0
- data/lib/davinci_crd_test_kit/card_responses/external_reference.json +21 -0
- data/lib/davinci_crd_test_kit/card_responses/instructions.json +14 -0
- data/lib/davinci_crd_test_kit/card_responses/launch_smart_app.json +21 -0
- data/lib/davinci_crd_test_kit/card_responses/propose_alternate_request.json +71 -0
- data/lib/davinci_crd_test_kit/card_responses/request_form_completion.json +227 -0
- data/lib/davinci_crd_test_kit/cards_validation.rb +234 -0
- data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +762 -0
- data/lib/davinci_crd_test_kit/client_hook_request_validation.rb +15 -0
- data/lib/davinci_crd_test_kit/client_hooks_group.rb +706 -0
- data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +71 -0
- data/lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb +48 -0
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +40 -0
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_read_test.rb +39 -0
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +232 -0
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +40 -0
- data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +60 -0
- data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +40 -0
- data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +68 -0
- data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +68 -0
- data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +41 -0
- data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +40 -0
- data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +63 -0
- data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +151 -0
- data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +79 -0
- data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +76 -0
- data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +79 -0
- data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +65 -0
- data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +34 -0
- data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +61 -0
- data/lib/davinci_crd_test_kit/crd_client_suite.rb +156 -0
- data/lib/davinci_crd_test_kit/crd_jwks.json +59 -0
- data/lib/davinci_crd_test_kit/crd_options.rb +9 -0
- data/lib/davinci_crd_test_kit/crd_server_suite.rb +115 -0
- data/lib/davinci_crd_test_kit/ext/inferno_core/runnable.rb +22 -0
- data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +410 -0
- data/lib/davinci_crd_test_kit/jwks.rb +25 -0
- data/lib/davinci_crd_test_kit/jwt_helper.rb +74 -0
- data/lib/davinci_crd_test_kit/mock_service_response.rb +421 -0
- data/lib/davinci_crd_test_kit/routes/cds-services.json +74 -0
- data/lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb +18 -0
- data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +93 -0
- data/lib/davinci_crd_test_kit/routes/jwk_set_endpoint_handler.rb +15 -0
- data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +173 -0
- data/lib/davinci_crd_test_kit/server_discovery_group.rb +59 -0
- data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +144 -0
- data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +144 -0
- data/lib/davinci_crd_test_kit/server_hook_request_validation.rb +15 -0
- data/lib/davinci_crd_test_kit/server_hooks_group.rb +69 -0
- data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +173 -0
- data/lib/davinci_crd_test_kit/server_order_select_group.rb +169 -0
- data/lib/davinci_crd_test_kit/server_order_sign_group.rb +198 -0
- data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +23 -0
- data/lib/davinci_crd_test_kit/server_tests/additional_orders_validation_test.rb +70 -0
- data/lib/davinci_crd_test_kit/server_tests/card_optional_fields_validation_test.rb +47 -0
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_across_hooks_validation_test.rb +32 -0
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +58 -0
- data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +121 -0
- data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +72 -0
- data/lib/davinci_crd_test_kit/server_tests/discovery_endpoint_test.rb +88 -0
- data/lib/davinci_crd_test_kit/server_tests/discovery_services_validation_test.rb +65 -0
- data/lib/davinci_crd_test_kit/server_tests/external_reference_card_across_hooks_validation_test.rb +28 -0
- data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +36 -0
- data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +79 -0
- data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_across_hooks_test.rb +25 -0
- data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_test.rb +28 -0
- data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +38 -0
- data/lib/davinci_crd_test_kit/server_tests/propose_alternate_request_card_validation_test.rb +65 -0
- data/lib/davinci_crd_test_kit/server_tests/service_call_test.rb +86 -0
- data/lib/davinci_crd_test_kit/server_tests/service_request_context_validation_test.rb +30 -0
- data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +41 -0
- data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +82 -0
- data/lib/davinci_crd_test_kit/suggestion_actions_validation.rb +123 -0
- data/lib/davinci_crd_test_kit/tags.rb +8 -0
- data/lib/davinci_crd_test_kit/test_helper.rb +23 -0
- data/lib/davinci_crd_test_kit/urls.rb +52 -0
- data/lib/davinci_crd_test_kit/version.rb +3 -0
- data/lib/davinci_crd_test_kit.rb +2 -0
- metadata +170 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module DaVinciCRDTestKit
|
2
|
+
class TokenHeaderTest < Inferno::Test
|
3
|
+
id :crd_token_header
|
4
|
+
title 'Authorization token header contains required information'
|
5
|
+
description %(
|
6
|
+
Verify that the JWT header contains the header fields required by the [CDS hooks spec](https://cds-hooks.hl7.org/2.0#trusting-cds-clients).
|
7
|
+
The `alg`, `kid`, and `typ` fields are required. This test also verifies that the `typ` field is set to `JWT` and
|
8
|
+
that the key used to sign the token can be identified in the JWKS.
|
9
|
+
)
|
10
|
+
|
11
|
+
input :auth_token_header_json, :crd_jwks_keys_json
|
12
|
+
output :auth_token_jwk_json
|
13
|
+
|
14
|
+
run do
|
15
|
+
header = JSON.parse(auth_token_header_json)
|
16
|
+
|
17
|
+
algorithm = header['alg']
|
18
|
+
assert algorithm.present?, 'Token header must have the `alg` field'
|
19
|
+
assert algorithm != 'none', 'Token header `alg` field cannot be set to none'
|
20
|
+
|
21
|
+
assert header['typ'].present?, 'Token header must have the `typ` field'
|
22
|
+
assert header['typ'] == 'JWT', "Token header `typ` field must be set to 'JWT', instead was #{header['typ']}"
|
23
|
+
|
24
|
+
assert header['kid'].present?, 'Token header must have the `kid` field'
|
25
|
+
kid = header['kid']
|
26
|
+
keys = JSON.parse(crd_jwks_keys_json)
|
27
|
+
|
28
|
+
jwk = keys.find { |key| key['kid'] == kid }
|
29
|
+
assert jwk.present?, "JWKS did not contain a public key with an id of `#{kid}`"
|
30
|
+
|
31
|
+
output auth_token_jwk_json: jwk.to_json
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module DaVinciCRDTestKit
|
2
|
+
class TokenPayloadTest < Inferno::Test
|
3
|
+
include URLs
|
4
|
+
id :crd_token_payload
|
5
|
+
title 'Authorization token payload has required claims and a valid signature'
|
6
|
+
description %(
|
7
|
+
Verify that the JWT payload contains the payload fields required by the
|
8
|
+
[CDS hooks spec](https://cds-hooks.hl7.org/2.0#trusting-cds-clients).
|
9
|
+
The `iss`, `aud`, `exp`, `iat`, and `jti` claims are required.
|
10
|
+
Additionally:
|
11
|
+
|
12
|
+
- `iss` must match the `issuer` from the `iss` input
|
13
|
+
- `aud` must match the URL of the CDS Service endpoint being invoked
|
14
|
+
- `exp` must represent a time in the future
|
15
|
+
- `jti` must be a non-blank string that uniquely identifies this authentication JWT
|
16
|
+
)
|
17
|
+
|
18
|
+
REQUIRED_CLAIMS = ['iss', 'aud', 'exp', 'iat', 'jti'].freeze
|
19
|
+
|
20
|
+
def required_claims
|
21
|
+
REQUIRED_CLAIMS.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def hook_url
|
25
|
+
base_url + config.options[:hook_path]
|
26
|
+
end
|
27
|
+
|
28
|
+
input :auth_token,
|
29
|
+
:auth_token_jwk_json,
|
30
|
+
:iss
|
31
|
+
|
32
|
+
run do
|
33
|
+
begin
|
34
|
+
jwk = JSON.parse(auth_token_jwk_json).deep_symbolize_keys
|
35
|
+
|
36
|
+
payload, =
|
37
|
+
JWT.decode(
|
38
|
+
auth_token,
|
39
|
+
JWT::JWK.import(jwk).public_key,
|
40
|
+
true,
|
41
|
+
algorithms: [jwk[:alg]],
|
42
|
+
exp_leeway: 60,
|
43
|
+
iss:,
|
44
|
+
aud: hook_url,
|
45
|
+
verify_not_before: false,
|
46
|
+
verify_iat: false,
|
47
|
+
verify_jti: true,
|
48
|
+
verify_iss: true,
|
49
|
+
verify_aud: true
|
50
|
+
)
|
51
|
+
rescue StandardError => e
|
52
|
+
assert false, "Token validation error: #{e.message}"
|
53
|
+
end
|
54
|
+
|
55
|
+
missing_claims = required_claims - payload.keys
|
56
|
+
missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
|
57
|
+
|
58
|
+
assert missing_claims.empty?, "JWT payload missing required claims: #{missing_claims_string}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require_relative 'client_fhir_api_group'
|
2
|
+
require_relative 'client_hooks_group'
|
3
|
+
require_relative 'routes/cds_services_discovery_handler'
|
4
|
+
require_relative 'tags'
|
5
|
+
require_relative 'urls'
|
6
|
+
require_relative 'crd_options'
|
7
|
+
require_relative 'routes/hook_request_endpoint'
|
8
|
+
require_relative 'ext/inferno_core/runnable'
|
9
|
+
require_relative 'version'
|
10
|
+
|
11
|
+
module DaVinciCRDTestKit
|
12
|
+
class CRDClientSuite < Inferno::TestSuite
|
13
|
+
id :crd_client
|
14
|
+
title 'Da Vinci CRD Client Test Suite'
|
15
|
+
description <<~DESCRIPTION
|
16
|
+
The Da Vinci CRD Client Test Suite tests the conformance of client systems
|
17
|
+
to [version 2.0.1 of the Da Vinci Coverage Requirements Discovery (CRD)
|
18
|
+
Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2).
|
19
|
+
|
20
|
+
## Overview
|
21
|
+
This suite contains two groups of tests. The Hooks group receives and
|
22
|
+
responds to incoming CDS Hooks requests from CRD clients. The FHIR API
|
23
|
+
group makes FHIR requests to CRD Clients to verify that they support the
|
24
|
+
FHIR interactions defined in the implementation guide.
|
25
|
+
|
26
|
+
## CDS Services
|
27
|
+
This suite provides basic CDS services for [the six hooks contained in the
|
28
|
+
implementation
|
29
|
+
guide](https://hl7.org/fhir/us/davinci-crd/STU2/hooks.html). The discovery
|
30
|
+
endpoint is located at:
|
31
|
+
|
32
|
+
* `#{Inferno::Application['base_url']}/custom/#{id}/cds-services`
|
33
|
+
|
34
|
+
## SMART App Launch
|
35
|
+
Use this information when registering Inferno as a SMART App:
|
36
|
+
|
37
|
+
* Launch URI: `#{SMARTAppLaunch::AppLaunchTest.config.options[:launch_uri]}`
|
38
|
+
* Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
|
39
|
+
|
40
|
+
If a client receives a SMART App Launch card in a response and would like
|
41
|
+
to test their ability to launch Inferno as a SMART App, first run the
|
42
|
+
SMART on FHIR Discovery and SMART EHR Launch groups under FHIR API >
|
43
|
+
Authorization. When running the SMART EHR Launch group, Inferno will wait
|
44
|
+
for the incoming SMART App Launch request, and this is the time to perform
|
45
|
+
the launch from the client being tested.
|
46
|
+
|
47
|
+
## Running the Tests
|
48
|
+
If you would like to try out the tests against [the public CRD reference
|
49
|
+
client](https://crd-request-generator.davinci.hl7.org/), you can do so by:
|
50
|
+
1. Selecting the *CRD Request Generator RI* option from the Preset
|
51
|
+
dropdown in the upper left.
|
52
|
+
2. Selecting the *order-sign* hook group on the left menu.
|
53
|
+
3. Clicking on the *RUN TESTS* button in the upper right.
|
54
|
+
4. Clicking the *Submit* button at the bottom of the input dialog.
|
55
|
+
5. Follow the instructions in the wait dialog.
|
56
|
+
6. Open the reference client in another tab/browser.
|
57
|
+
7. Update the *CRD Server* field in the client configuration to point to
|
58
|
+
the discovery endpoint of this suite provided above, and the *Order
|
59
|
+
Sign Rest End Point*
|
60
|
+
to the service id provided in the wait dialog.
|
61
|
+
8. Select the patient data to be used to form the request, then submit the
|
62
|
+
request.
|
63
|
+
|
64
|
+
You can run these tests using your own client by updating the inputs with
|
65
|
+
your own data.
|
66
|
+
|
67
|
+
Note that:
|
68
|
+
- You can only sequentially *RUN ALL TESTS* if your system supports all
|
69
|
+
hooks.
|
70
|
+
- Systems are not expected to pass the *FHIR RESTful Capabilities* tests
|
71
|
+
based on the provided inputs, as the resource might not exist on the
|
72
|
+
client's FHIR server.
|
73
|
+
|
74
|
+
## Limitations
|
75
|
+
The test suite does not implement any sort of payer business logic, so the
|
76
|
+
responses to hook calls are simple hard-coded responses. Hook
|
77
|
+
configuration is not tested.
|
78
|
+
DESCRIPTION
|
79
|
+
|
80
|
+
suite_summary <<~SUMMARY
|
81
|
+
The Da Vinci CRD Client Test Suite tests the conformance of client systems
|
82
|
+
to [version 2.0.1 of the Da Vinci Coverage Requirements Discovery (CRD)
|
83
|
+
Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2).
|
84
|
+
SUMMARY
|
85
|
+
|
86
|
+
version VERSION
|
87
|
+
|
88
|
+
links [
|
89
|
+
{
|
90
|
+
label: 'Report Issue',
|
91
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit/issues'
|
92
|
+
},
|
93
|
+
{
|
94
|
+
label: 'Open Source',
|
95
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit'
|
96
|
+
},
|
97
|
+
{
|
98
|
+
label: 'Download',
|
99
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit/releases'
|
100
|
+
}
|
101
|
+
]
|
102
|
+
|
103
|
+
fhir_resource_validator do
|
104
|
+
igs('hl7.fhir.us.davinci-crd', 'hl7.fhir.us.core')
|
105
|
+
|
106
|
+
exclude_message do |message|
|
107
|
+
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
suite_option :smart_app_launch_version,
|
112
|
+
title: 'SMART App Launch Version',
|
113
|
+
list_options: [
|
114
|
+
{
|
115
|
+
label: 'SMART App Launch 1.0.0',
|
116
|
+
value: CRDOptions::SMART_1
|
117
|
+
},
|
118
|
+
{
|
119
|
+
label: 'SMART App Launch 2.0.0',
|
120
|
+
value: CRDOptions::SMART_2
|
121
|
+
}
|
122
|
+
]
|
123
|
+
|
124
|
+
def self.test_resumes?(test)
|
125
|
+
!test.config.options[:accepts_multiple_requests]
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.extract_token_from_query_params(request)
|
129
|
+
request.query_parameters['token']
|
130
|
+
end
|
131
|
+
|
132
|
+
route :get, '/cds-services', Routes::CDSServicesDiscoveryHandler
|
133
|
+
# TODO
|
134
|
+
# route :post, '/cds-services/:cds-service_id', cds_service_handler
|
135
|
+
|
136
|
+
allow_cors APPOINTMENT_BOOK_PATH, ENCOUNTER_START_PATH, ENCOUNTER_DISCHARGE_PATH, ORDER_DISPATCH_PATH,
|
137
|
+
ORDER_SELECT_PATH, ORDER_SIGN_PATH
|
138
|
+
suite_endpoint :post, APPOINTMENT_BOOK_PATH, HookRequestEndpoint
|
139
|
+
suite_endpoint :post, ENCOUNTER_START_PATH, HookRequestEndpoint
|
140
|
+
suite_endpoint :post, ENCOUNTER_DISCHARGE_PATH, HookRequestEndpoint
|
141
|
+
suite_endpoint :post, ORDER_DISPATCH_PATH, HookRequestEndpoint
|
142
|
+
suite_endpoint :post, ORDER_SELECT_PATH, HookRequestEndpoint
|
143
|
+
suite_endpoint :post, ORDER_SIGN_PATH, HookRequestEndpoint
|
144
|
+
|
145
|
+
resume_test_route :get, RESUME_PASS_PATH do |request|
|
146
|
+
CRDClientSuite.extract_token_from_query_params(request)
|
147
|
+
end
|
148
|
+
resume_test_route :get, RESUME_FAIL_PATH, result: 'fail' do |request|
|
149
|
+
CRDClientSuite.extract_token_from_query_params(request)
|
150
|
+
end
|
151
|
+
|
152
|
+
group from: :crd_client_hooks
|
153
|
+
|
154
|
+
group from: :crd_client_fhir_api
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
{
|
2
|
+
"keys": [
|
3
|
+
{
|
4
|
+
"kty": "EC",
|
5
|
+
"crv": "P-384",
|
6
|
+
"x": "JQKTsV6PT5Szf4QtDA1qrs0EJ1pbimQmM2SKvzOlIAqlph3h1OHmZ2i7MXahIF2C",
|
7
|
+
"y": "bRWWQRJBgDa6CTgwofYrHjVGcO-A7WNEnu4oJA5OUJPPPpczgx1g2NsfinK-D2Rw",
|
8
|
+
"use": "sig",
|
9
|
+
"key_ops": [
|
10
|
+
"verify"
|
11
|
+
],
|
12
|
+
"ext": true,
|
13
|
+
"kid": "4b49a739d1eb115b3225f4cf9beb6d1b",
|
14
|
+
"alg": "ES384"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"kty": "EC",
|
18
|
+
"crv": "P-384",
|
19
|
+
"d": "kDkn55p7gryKk2tj6z2ij7ExUnhi0ngxXosvqa73y7epwgthFqaJwApmiXXU2yhK",
|
20
|
+
"x": "JQKTsV6PT5Szf4QtDA1qrs0EJ1pbimQmM2SKvzOlIAqlph3h1OHmZ2i7MXahIF2C",
|
21
|
+
"y": "bRWWQRJBgDa6CTgwofYrHjVGcO-A7WNEnu4oJA5OUJPPPpczgx1g2NsfinK-D2Rw",
|
22
|
+
"key_ops": [
|
23
|
+
"sign"
|
24
|
+
],
|
25
|
+
"ext": true,
|
26
|
+
"kid": "4b49a739d1eb115b3225f4cf9beb6d1b",
|
27
|
+
"alg": "ES384"
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"kty": "RSA",
|
31
|
+
"alg": "RS384",
|
32
|
+
"n": "vjbIzTqiY8K8zApeNng5ekNNIxJfXAue9BjoMrZ9Qy9m7yIA-tf6muEupEXWhq70tC7vIGLqJJ4O8m7yiH8H2qklX2mCAMg3xG3nbykY2X7JXtW9P8VIdG0sAMt5aZQnUGCgSS3n0qaooGn2LUlTGIR88Qi-4Nrao9_3Ki3UCiICeCiAE224jGCg0OlQU6qj2gEB3o-DWJFlG_dz1y-Mxo5ivaeM0vWuodjDrp-aiabJcSF_dx26sdC9dZdBKXFDq0t19I9S9AyGpGDJwzGRtWHY6LsskNHLvo8Zb5AsJ9eRZKpnh30SYBZI9WHtzU85M9WQqdScR69Vyp-6Uhfbvw",
|
33
|
+
"e": "AQAB",
|
34
|
+
"use": "sig",
|
35
|
+
"key_ops": [
|
36
|
+
"verify"
|
37
|
+
],
|
38
|
+
"ext": true,
|
39
|
+
"kid": "b41528b6f37a9500edb8a905a595bdd7"
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"kty": "RSA",
|
43
|
+
"alg": "RS384",
|
44
|
+
"n": "vjbIzTqiY8K8zApeNng5ekNNIxJfXAue9BjoMrZ9Qy9m7yIA-tf6muEupEXWhq70tC7vIGLqJJ4O8m7yiH8H2qklX2mCAMg3xG3nbykY2X7JXtW9P8VIdG0sAMt5aZQnUGCgSS3n0qaooGn2LUlTGIR88Qi-4Nrao9_3Ki3UCiICeCiAE224jGCg0OlQU6qj2gEB3o-DWJFlG_dz1y-Mxo5ivaeM0vWuodjDrp-aiabJcSF_dx26sdC9dZdBKXFDq0t19I9S9AyGpGDJwzGRtWHY6LsskNHLvo8Zb5AsJ9eRZKpnh30SYBZI9WHtzU85M9WQqdScR69Vyp-6Uhfbvw",
|
45
|
+
"e": "AQAB",
|
46
|
+
"d": "rriV9GYimi5by7TOW4xNh6_gYBHVRDBsft2OFF8qapdVHt2GNuRDDxc_B6ga6TY2Enh2MLKLTr1dD3W4FIdTCJiMerrorp07FJS7nJEMgWQDxrfgkX4_EqrhW42L5d4vypYnRXEEW6u4gzkx5uFOkdvJBIK7CsIdSaBFYhochnynNgvbKWasi4rl2hayEH8tdf3B7Z6OIH9alspBTaq3j_zJt_KkrpYEzIUb4UgALB5NTWn5YKr0Avk_asOg8YfjViQwO9ASGaWjQeJ2Rx8OEQwBMQHSDMCSWNiWmYOu9PcwSZFc1vLxqzyIM8QrQSJHCCMo_wGYgke_r0CLeONHEQ",
|
47
|
+
"p": "5hH_QApWGeobRi1n7XbMfJYohB8K3JDPa0MspfplHpJ-17JiGG2sNoBdBcpaPRf9OX48P8VqO0qrSSRAk-I-uO6OO9BHbIukXJILqnY2JmurYzbcYbt5FVbknlHRJojkF6-7sFBazpueUlOnXCw7X7Z_SkfNE4QX5Ejm2Zm5mek",
|
48
|
+
"q": "06bZz7c7K9s1-aEZsxYnLJ9eTpKlt1tIBDA_LwIh5W3w259pes2kUtimbnkyOf-V2ZIERsFCh5s-S9IOEMvAIa6M5j9GW1ILNT7AcHIUfcyFcH-FF8BU_KJdRP5PXnIXFdYcylvsdoIdchy1AaUIzyiKRCU3HBYI75hez0l_F2c",
|
49
|
+
"dp": "h_sVIXW6hCCRND48EedIX06k7conMkxIu_39ErDXOWWeoMAnKIcR5TijQnviL__QxD1vQMXezuKIMHfDz2RGbClbWdD1lhtG7wvG515tDPJQXxia0wzqOQmdoFF9S8hXAAT26vPjaAAkaEZXQaxG_4Au5elgNWu6b0wDXZN1Vpk",
|
50
|
+
"dq": "GqS0YpuUTU8JGmWXUJ4HTGy7eHSpe8134V8ZdRd1oOYYHe2RX64nc25mdR24nuh3uq3Q7_9AGsYGL5E_yAl-JD9O6WUpvDE1y_wcSYty3Os0GRdUb8r8Z9kgmKDS6Pa_xTXw5eBwgfKbNlQ6zPwzgbB-x1lP-K8lbNPni3ybDR0",
|
51
|
+
"qi": "cqQfoi0sM5Su8ZOhznmdWrDIQB28H6fBKiabgaIKkbWZV4e0nwFvLquHjPOvv4Ao8iEGU5dyhvg0n5BKYPi-4mp6M6OA1sy0NrTr7EsKSYGyu2pBq9rw4oAYTM2LXKg6K-awgUUlkc451IwxHBAe15PWCBM3kvLQeijNid0Vz5I",
|
52
|
+
"key_ops": [
|
53
|
+
"sign"
|
54
|
+
],
|
55
|
+
"ext": true,
|
56
|
+
"kid": "b41528b6f37a9500edb8a905a595bdd7"
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module DaVinciCRDTestKit
|
2
|
+
module CRDOptions
|
3
|
+
SMART_1 = 'smart_app_launch_1'.freeze
|
4
|
+
SMART_2 = 'smart_app_launch_2'.freeze
|
5
|
+
|
6
|
+
SMART_1_REQUIREMENT = { smart_app_launch_version: SMART_1 }.freeze
|
7
|
+
SMART_2_REQUIREMENT = { smart_app_launch_version: SMART_2 }.freeze
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require_relative 'jwt_helper'
|
2
|
+
require_relative 'routes/jwk_set_endpoint_handler'
|
3
|
+
require_relative 'server_discovery_group'
|
4
|
+
require_relative 'server_hooks_group'
|
5
|
+
require_relative 'version'
|
6
|
+
|
7
|
+
module DaVinciCRDTestKit
|
8
|
+
class CRDServerSuite < Inferno::TestSuite
|
9
|
+
id :crd_server
|
10
|
+
title 'Da Vinci CRD Server Test Suite'
|
11
|
+
description <<~DESCRIPTION
|
12
|
+
The Da Vinci CRD Server Test Suite tests the conformance of server systems
|
13
|
+
to [version 2.0.1 of the Da Vinci Coverage Requirements Discovery (CRD)
|
14
|
+
Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2).
|
15
|
+
|
16
|
+
## Overview
|
17
|
+
This suite contains two groups of tests. The Discovery group validates a
|
18
|
+
CRD server's discovery response. The Hooks group makes CDS Hooks calls to
|
19
|
+
the server and validates the responses. By default, the hook requests are
|
20
|
+
based on examples from the CDS Hooks specification, but testers can
|
21
|
+
replace these with request bodies to elicit a particular response from
|
22
|
+
their system.
|
23
|
+
|
24
|
+
## Trusting CDS Clients
|
25
|
+
As specified in the [CDS Hooks Spec](https://cds-hooks.hl7.org/2.0/#trusting-cds-clients),
|
26
|
+
Each time a CDS Client transmits a request to a CDS Service which
|
27
|
+
requires authentication, the request MUST include an Authorization
|
28
|
+
header presenting the JWT as a “Bearer” token:
|
29
|
+
`Authorization: Bearer {{JWT}}`
|
30
|
+
|
31
|
+
Inferno self-issues the JWT for each CDS Service call. The following
|
32
|
+
info is needed to register Inferno:
|
33
|
+
|
34
|
+
- **ISS**: `#{Inferno::Application[:base_url]}/custom/crd_server`
|
35
|
+
- **JWK Set Url**:
|
36
|
+
`#{Inferno::Application[:base_url]}/custom/crd_server/jwks.json`
|
37
|
+
|
38
|
+
## Running the Tests
|
39
|
+
Execution of these tests require a significant amount of tester input in
|
40
|
+
the form of requests that Inferno will make against the server under test.
|
41
|
+
|
42
|
+
If you would like to try out the tests using examples from the IG and the
|
43
|
+
[CDS Hooks spec](https://cds-hooks.hl7.org/2.0/) against [the public CRD
|
44
|
+
reference server endpoint](https://crd.davinci.hl7.org/), you can do so
|
45
|
+
by:
|
46
|
+
1. Selecting the *CRD Server RI* option from the
|
47
|
+
Preset dropdown in the upper left
|
48
|
+
2. Clicking the *Run All Tests* button in the upper right
|
49
|
+
3. Clicking the *Submit* button at the bottom of the input dialog
|
50
|
+
|
51
|
+
You can run these tests using your own server by updating the "CRD server
|
52
|
+
base URL" and, if needed, providing requests inputs you wish to use for
|
53
|
+
each hook your server supports.
|
54
|
+
|
55
|
+
Note that the provided inputs for these tests are not complete and systems
|
56
|
+
are not expected to pass the tests based on them.
|
57
|
+
|
58
|
+
|
59
|
+
## Limitations
|
60
|
+
Inferno is unable to determine what requests will result in specific kinds
|
61
|
+
of responses from the server under test (e.g., what will result in
|
62
|
+
Instructions being returned vs. Coverage Information). As a result, the
|
63
|
+
tester must supply the request bodies which will cause the system under
|
64
|
+
test to return the desired response types.
|
65
|
+
|
66
|
+
The ability of a CRD Server to request additional FHIR resources is not
|
67
|
+
tested. Hook configuration is not tested.
|
68
|
+
DESCRIPTION
|
69
|
+
|
70
|
+
suite_summary <<~SUMMARY
|
71
|
+
The Da Vinci CRD Server Test Suite tests the conformance of server systems
|
72
|
+
to [version 2.0.1 of the Da Vinci Coverage Requirements Discovery (CRD)
|
73
|
+
Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2).
|
74
|
+
SUMMARY
|
75
|
+
|
76
|
+
version VERSION
|
77
|
+
|
78
|
+
links [
|
79
|
+
{
|
80
|
+
label: 'Report Issue',
|
81
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit/issues'
|
82
|
+
},
|
83
|
+
{
|
84
|
+
label: 'Open Source',
|
85
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit'
|
86
|
+
},
|
87
|
+
{
|
88
|
+
label: 'Download',
|
89
|
+
url: 'https://github.com/inferno-framework/davinci-crd-test-kit/releases'
|
90
|
+
}
|
91
|
+
]
|
92
|
+
|
93
|
+
input :base_url,
|
94
|
+
title: 'CRD server base URL'
|
95
|
+
|
96
|
+
fhir_resource_validator do
|
97
|
+
igs('hl7.fhir.us.davinci-crd', 'hl7.fhir.us.core')
|
98
|
+
|
99
|
+
exclude_message do |message|
|
100
|
+
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def inferno_base_url
|
105
|
+
suite_id = self.class.suite.id
|
106
|
+
@inferno_base_url ||= "#{Inferno::Application['base_url']}/custom/#{suite_id}"
|
107
|
+
end
|
108
|
+
|
109
|
+
route :get, '/jwks.json', Routes::JWKSetEndpointHandler
|
110
|
+
|
111
|
+
group from: :crd_server_discovery_group
|
112
|
+
|
113
|
+
group from: :crd_server_hooks
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module Runnable
|
4
|
+
PRE_FLIGHT_HANDLER = proc do
|
5
|
+
[
|
6
|
+
200,
|
7
|
+
{
|
8
|
+
'Access-Control-Allow-Origin' => '*',
|
9
|
+
'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
|
10
|
+
},
|
11
|
+
['']
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
def allow_cors(*paths)
|
16
|
+
paths.each do |path|
|
17
|
+
route(:options, path, PRE_FLIGHT_HANDLER)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|