davinci_dtr_test_kit 0.11.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/davinci_dtr_test_kit/auth_groups/token_request_test.rb +1 -1
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_questionnaire_package_request_test.rb +52 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_conformance_test.rb +15 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_correctness_test.rb +30 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_launch_attestation_test.rb +28 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_prepopulation_attestation_test.rb +30 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_prepopulation_override_attestation_test.rb +27 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_questionnaire_workflow_group.rb +91 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_rendering_enabled_questions_attestation_test.rb +30 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_store_attestation_test.rb +29 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{dtr_dinner_questionnaire_package_request_test.rb → dtr_smart_app_dinner_questionnaire_package_request_test.rb} +5 -5
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{prepopulation_attestation_test.rb → dtr_smart_app_prepopulation_attestation_test.rb} +2 -2
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{prepopulation_override_attestation_test.rb → dtr_smart_app_prepopulation_override_attestation_test.rb} +2 -2
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{dtr_questionnaire_response_save_test.rb → dtr_smart_app_questionnaire_response_save_test.rb} +2 -2
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_smart_app_questionnaire_workflow_group.rb +13 -13
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{rendering_enabled_questions_attestation_test.rb → dtr_smart_app_rendering_enabled_questions_attestation_test.rb} +2 -2
- data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_package_request_validation_test.rb +1 -1
- data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_basic_conformance_test.rb +3 -7
- data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_pre_population_test.rb +9 -5
- data/lib/davinci_dtr_test_kit/cql_test.rb +182 -137
- data/lib/davinci_dtr_test_kit/docs/dtr_full_ehr_suite_description_v201.md +127 -0
- data/lib/davinci_dtr_test_kit/docs/dtr_light_ehr_suite_description_v201.md +29 -0
- data/lib/davinci_dtr_test_kit/dtr_full_ehr_suite.rb +4 -12
- data/lib/davinci_dtr_test_kit/dtr_light_ehr_suite.rb +38 -25
- data/lib/davinci_dtr_test_kit/dtr_options.rb +7 -0
- data/lib/davinci_dtr_test_kit/dtr_questionnaire_response_validation.rb +118 -75
- data/lib/davinci_dtr_test_kit/dtr_smart_app_suite.rb +6 -3
- data/lib/davinci_dtr_test_kit/fixture_loader.rb +6 -84
- data/lib/davinci_dtr_test_kit/fixtures.rb +43 -48
- data/lib/davinci_dtr_test_kit/mock_auth_server.rb +101 -18
- data/lib/davinci_dtr_test_kit/mock_ehr.rb +32 -24
- data/lib/davinci_dtr_test_kit/mock_payer.rb +41 -64
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_next_questionnaire_expressions_test.rb +2 -2
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_request_validation_test.rb +11 -19
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_bundles_validation_test.rb +6 -6
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_search_validation_test.rb +6 -6
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_validation_test.rb +17 -18
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_request_validation_test.rb +6 -7
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_response_validation_test.rb +3 -1
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_request_validation_test.rb +10 -21
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_response_validation_test.rb +7 -13
- data/lib/davinci_dtr_test_kit/tags.rb +1 -0
- data/lib/davinci_dtr_test_kit/urls.rb +13 -10
- data/lib/davinci_dtr_test_kit/validation_test.rb +8 -9
- data/lib/davinci_dtr_test_kit/version.rb +1 -1
- data/lib/davinci_dtr_test_kit.rb +2 -2
- metadata +37 -12
- data/lib/davinci_dtr_test_kit/client_groups/resp_assist_device/dtr_full_ehr_questionnaire_workflow_group.rb +0 -19
- /data/lib/davinci_dtr_test_kit/fixtures/{pre_populated_questionnaire_response.json → respiratory_assist_device/pre_populated_questionnaire_response.json} +0 -0
- /data/lib/davinci_dtr_test_kit/fixtures/{questionnaire_package.json → respiratory_assist_device/questionnaire_package.json} +0 -0
@@ -1,16 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'urls'
|
2
|
-
require_relative 'fixtures'
|
3
4
|
|
4
5
|
module DaVinciDTRTestKit
|
5
6
|
module MockAuthServer
|
6
|
-
|
7
|
+
AUTHORIZED_PRACTITIONER_ID = 'pra1234' # Must exist on the FHIR_REFERENCE_SERVER (env var)
|
8
|
+
|
9
|
+
RSA_PRIVATE_KEY = OpenSSL::PKey::RSA.generate(2048)
|
10
|
+
RSA_PUBLIC_KEY = RSA_PRIVATE_KEY.public_key
|
11
|
+
SUPPORTED_SCOPES = ['launch', 'patient/*.rs', 'user/*.rs', 'offline_access', 'openid', 'fhirUser'].freeze
|
12
|
+
|
13
|
+
def requests_repo
|
14
|
+
@requests_repo ||= Inferno::Repositories::Requests.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_server_jwks(_env)
|
18
|
+
response_body = {
|
19
|
+
keys: [
|
20
|
+
{
|
21
|
+
kty: 'RSA',
|
22
|
+
alg: 'RS256',
|
23
|
+
n: Base64.urlsafe_encode64(RSA_PUBLIC_KEY.n.to_s(2), padding: false),
|
24
|
+
e: Base64.urlsafe_encode64(RSA_PUBLIC_KEY.e.to_s(2), padding: false),
|
25
|
+
use: 'sig'
|
26
|
+
}
|
27
|
+
]
|
28
|
+
}.to_json
|
29
|
+
|
30
|
+
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
31
|
+
end
|
7
32
|
|
8
33
|
def ehr_smart_config(env)
|
9
|
-
|
10
|
-
host = env['HTTP_HOST']
|
11
|
-
path = env['REQUEST_PATH'] || env['PATH_INFO']
|
12
|
-
path.gsub!(%r{#{SMART_CONFIG_PATH}(/)?}, '')
|
13
|
-
base_url = "#{protocol}://#{host + path}"
|
34
|
+
base_url = env_base_url(env, SMART_CONFIG_PATH)
|
14
35
|
response_body =
|
15
36
|
{
|
16
37
|
authorization_endpoint: base_url + EHR_AUTHORIZE_PATH,
|
@@ -18,7 +39,7 @@ module DaVinciDTRTestKit
|
|
18
39
|
token_endpoint_auth_methods_supported: ['private_key_jwt'],
|
19
40
|
token_endpoint_auth_signing_alg_values_supported: ['RS256'],
|
20
41
|
grant_types_supported: ['authorization_code'],
|
21
|
-
scopes_supported:
|
42
|
+
scopes_supported: SUPPORTED_SCOPES,
|
22
43
|
response_types_supported: ['code'],
|
23
44
|
code_challenge_methods_supported: ['S256'],
|
24
45
|
capabilities: [
|
@@ -34,9 +55,23 @@ module DaVinciDTRTestKit
|
|
34
55
|
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
35
56
|
end
|
36
57
|
|
58
|
+
def ehr_openid_config(env)
|
59
|
+
base_url = env_base_url(env, OPENID_CONFIG_PATH)
|
60
|
+
response_body = {
|
61
|
+
issuer: base_url + FHIR_BASE_PATH,
|
62
|
+
authorization_endpoint: base_url + EHR_AUTHORIZE_PATH,
|
63
|
+
token_endpoint: base_url + EHR_TOKEN_PATH,
|
64
|
+
jwks_uri: base_url + JKWS_PATH,
|
65
|
+
response_types_supported: ['id_token'],
|
66
|
+
subject_types_supported: ['public'],
|
67
|
+
id_token_signing_alg_values_supported: ['RS256']
|
68
|
+
}.to_json
|
69
|
+
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
70
|
+
end
|
71
|
+
|
37
72
|
def ehr_authorize(request, _test = nil, _test_result = nil)
|
38
73
|
# Authorization requests can bet GET or POST
|
39
|
-
params =
|
74
|
+
params = params_hash(request)
|
40
75
|
if params['redirect_uri'].present?
|
41
76
|
redirect_uri = "#{params['redirect_uri']}?" \
|
42
77
|
"code=#{SecureRandom.hex}&" \
|
@@ -57,25 +92,34 @@ module DaVinciDTRTestKit
|
|
57
92
|
|
58
93
|
def ehr_token_response(request, _test = nil, test_result = nil)
|
59
94
|
client_id = extract_client_id_from_token_request(request)
|
60
|
-
|
61
|
-
|
62
|
-
test_input = JSON.parse(test_result.input_json)
|
95
|
+
access_token = JWT.encode({ inferno_client_id: client_id }, nil, 'none')
|
96
|
+
granted_scopes = SUPPORTED_SCOPES & requested_scopes(test_result.test_session_id)
|
63
97
|
|
64
|
-
|
65
|
-
|
98
|
+
response = { access_token:, scope: granted_scopes.join(' '), token_type: 'bearer', expires_in: 3600 }
|
99
|
+
|
100
|
+
if granted_scopes.include?('openid')
|
101
|
+
response.merge!(id_token: create_id_token(request, client_id, fhir_user: granted_scopes.include?('fhirUser')))
|
102
|
+
end
|
103
|
+
|
104
|
+
fhir_context_input = find_test_input(test_result, 'smart_fhir_context')
|
105
|
+
fhir_context_input_value = fhir_context_input['value'] if fhir_context_input
|
66
106
|
begin
|
67
107
|
fhir_context = JSON.parse(fhir_context_input_value)
|
68
108
|
rescue StandardError
|
69
109
|
fhir_context = nil
|
70
110
|
end
|
71
|
-
response.merge!(
|
111
|
+
response.merge!(fhirContext: fhir_context) if fhir_context
|
72
112
|
|
73
|
-
smart_patient_input =
|
113
|
+
smart_patient_input = find_test_input(test_result, 'smart_patient_id')
|
74
114
|
smart_patient_input_value = smart_patient_input['value'] if smart_patient_input.present?
|
75
|
-
response.merge!(
|
115
|
+
response.merge!(patient: smart_patient_input_value) if smart_patient_input_value
|
76
116
|
|
77
117
|
request.response_body = response.to_json
|
78
|
-
request.response_headers = {
|
118
|
+
request.response_headers = {
|
119
|
+
'Cache-Control' => 'no-store',
|
120
|
+
'Pragma' => 'no-cache',
|
121
|
+
'Access-Control-Allow-Origin' => '*'
|
122
|
+
}
|
79
123
|
request.status = 200
|
80
124
|
end
|
81
125
|
|
@@ -141,5 +185,44 @@ module DaVinciDTRTestKit
|
|
141
185
|
def extract_token_from_query_params(request)
|
142
186
|
request.query_parameters['token']
|
143
187
|
end
|
188
|
+
|
189
|
+
def create_id_token(request, client_id, fhir_user: false)
|
190
|
+
# No point in mocking an identity provider, just always use known Practitioner as the authorized user
|
191
|
+
suite_base_url = request.url.split(EHR_TOKEN_PATH).first
|
192
|
+
id_token_hash = {
|
193
|
+
iss: suite_base_url + FHIR_BASE_PATH,
|
194
|
+
sub: AUTHORIZED_PRACTITIONER_ID,
|
195
|
+
aud: client_id,
|
196
|
+
exp: Time.now.to_i + (24 * 60 * 60), # 24 hrs
|
197
|
+
iat: Time.now.to_i
|
198
|
+
}
|
199
|
+
id_token_hash.merge!(fhirUser: "#{suite_base_url}/fhir/Practitioner/#{AUTHORIZED_PRACTITIONER_ID}") if fhir_user
|
200
|
+
|
201
|
+
JWT.encode(id_token_hash, RSA_PRIVATE_KEY, 'RS256')
|
202
|
+
end
|
203
|
+
|
204
|
+
def requested_scopes(test_session_id)
|
205
|
+
auth_request = requests_repo.tagged_requests(test_session_id, [EHR_AUTHORIZE_TAG]).last
|
206
|
+
return [] unless auth_request
|
207
|
+
|
208
|
+
scope_str = params_hash(auth_request)&.dig('scope')
|
209
|
+
scope_str ? URI.decode_www_form_component(scope_str).split : []
|
210
|
+
end
|
211
|
+
|
212
|
+
def find_test_input(test_result, input_name)
|
213
|
+
JSON.parse(test_result.input_json)&.find { |input| input['name'] == input_name }
|
214
|
+
end
|
215
|
+
|
216
|
+
def params_hash(request)
|
217
|
+
request.verb == 'get' ? request.query_parameters : URI.decode_www_form(request.request_body)&.to_h
|
218
|
+
end
|
219
|
+
|
220
|
+
def env_base_url(env, endpoint_path)
|
221
|
+
protocol = env['rack.url_scheme']
|
222
|
+
host = env['HTTP_HOST']
|
223
|
+
path = env['REQUEST_PATH'] || env['PATH_INFO']
|
224
|
+
path.gsub!(%r{#{endpoint_path}(/)?}, '')
|
225
|
+
"#{protocol}://#{host + path}"
|
226
|
+
end
|
144
227
|
end
|
145
228
|
end
|
@@ -27,16 +27,10 @@ module DaVinciDTRTestKit
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_fhir_resource(request, _test = nil, test_result = nil)
|
30
|
-
|
30
|
+
fhir_class, id = fhir_class_and_id_from_url(request.url)
|
31
31
|
request.response_headers = RESPONSE_HEADERS
|
32
32
|
|
33
|
-
|
34
|
-
fhir_class = FHIR.const_get(resource_type)
|
35
|
-
rescue NameError
|
36
|
-
resource_type = nil
|
37
|
-
end
|
38
|
-
|
39
|
-
if resource_type.blank?
|
33
|
+
if fhir_class.nil?
|
40
34
|
request.status = 400
|
41
35
|
request.response_headers = { 'Content-Type': 'application/json' }
|
42
36
|
request.response_body = FHIR::OperationOutcome.new(
|
@@ -49,18 +43,9 @@ module DaVinciDTRTestKit
|
|
49
43
|
end
|
50
44
|
|
51
45
|
# Respond with user-inputted resource if there is one that matches the request
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
ehr_bundle = FHIR.from_contents(ehr_bundle_input_value) if ehr_bundle_input_value.present?
|
56
|
-
rescue StandardError
|
57
|
-
ehr_bundle = nil
|
58
|
-
end
|
59
|
-
|
60
|
-
if id.present? && ehr_bundle.present? && ehr_bundle.is_a?(FHIR::Bundle)
|
61
|
-
matching_resource = ehr_bundle.entry&.find do |entry|
|
62
|
-
entry.resource.is_a?(fhir_class) && entry.resource&.id == id
|
63
|
-
end&.resource
|
46
|
+
ehr_bundle = ehr_input_bundle(test_result)
|
47
|
+
if id.present? && ehr_bundle.present?
|
48
|
+
matching_resource = find_resource_in_bundle(ehr_bundle, fhir_class, id)
|
64
49
|
if matching_resource.present?
|
65
50
|
request.status = 200
|
66
51
|
request.response_headers = { 'Content-Type': 'application/json' }
|
@@ -85,13 +70,36 @@ module DaVinciDTRTestKit
|
|
85
70
|
request.response_body = request.request_body
|
86
71
|
end
|
87
72
|
|
88
|
-
|
89
|
-
|
73
|
+
def ehr_input_bundle(test_result)
|
74
|
+
ehr_bundle_input = JSON.parse(test_result.input_json).find { |input| input['name'] == 'ehr_bundle' }
|
75
|
+
ehr_bundle_input_value = ehr_bundle_input_value = ehr_bundle_input['value'] if ehr_bundle_input.present?
|
76
|
+
ehr_bundle = FHIR.from_contents(ehr_bundle_input_value) if ehr_bundle_input_value.present?
|
77
|
+
ehr_bundle if ehr_bundle.is_a?(FHIR::Bundle)
|
78
|
+
rescue StandardError
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_resource_in_bundle(bundle, fhir_class, id)
|
83
|
+
bundle.entry&.find do |entry|
|
84
|
+
entry.resource.is_a?(fhir_class) && entry.resource&.id == id
|
85
|
+
end&.resource
|
86
|
+
end
|
87
|
+
|
88
|
+
# Pull resource type class and ID from url
|
89
|
+
# e.g. http://example.org/fhir/Patient/123 -> [FHIR::Patient, '123']
|
90
90
|
# @private
|
91
|
-
def
|
91
|
+
def fhir_class_and_id_from_url(url)
|
92
92
|
path = url.split('?').first.split('/fhir/').second
|
93
93
|
path.sub!(%r{/$}, '')
|
94
|
-
path.split('/')
|
94
|
+
resource_type, id = path.split('/')
|
95
|
+
|
96
|
+
begin
|
97
|
+
fhir_class = FHIR.const_get(resource_type)
|
98
|
+
rescue NameError
|
99
|
+
fhir_class = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
[fhir_class, id]
|
95
103
|
end
|
96
104
|
end
|
97
105
|
end
|
@@ -4,14 +4,12 @@ require_relative 'fixtures'
|
|
4
4
|
|
5
5
|
module DaVinciDTRTestKit
|
6
6
|
module MockPayer
|
7
|
-
include Fixtures
|
8
|
-
|
9
7
|
RESPONSE_HEADERS = { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }.freeze
|
10
8
|
|
11
9
|
def questionnaire_package_response(request, _test = nil, test_result = nil)
|
12
10
|
request.status = 200
|
13
11
|
request.response_headers = RESPONSE_HEADERS
|
14
|
-
request.response_body =
|
12
|
+
request.response_body = build_questionnaire_package_response(request, test_result.test_id).to_json
|
15
13
|
end
|
16
14
|
|
17
15
|
def payer_questionnaire_response(request, _test = nil, test_result = nil)
|
@@ -45,79 +43,58 @@ module DaVinciDTRTestKit
|
|
45
43
|
!test.config.options[:accepts_multiple_requests]
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
test_questionnaire_canonical = find_questionnaire_canonical_for_test_id(test_id)
|
50
|
-
test_questionnaire_loaded = false
|
51
|
-
|
52
|
-
bundles = []
|
53
|
-
issues = []
|
54
|
-
|
55
|
-
# first try the parameters - load the questionnaire specified by the questionnaire parameter
|
56
|
-
input_parameters = FHIR.from_contents(request.request_body)
|
57
|
-
input_parameters.parameter.each do |one_parameter|
|
58
|
-
next unless one_parameter.name == 'questionnaire'
|
59
|
-
next unless one_parameter.valueCanonical
|
46
|
+
private
|
60
47
|
|
61
|
-
|
62
|
-
|
48
|
+
def build_questionnaire_package_response(request, test_id)
|
49
|
+
begin
|
50
|
+
input_parameters = FHIR.from_contents(request.request_body)
|
51
|
+
rescue StandardError
|
52
|
+
return operation_outcome('error', 'invalid', 'No valid input parameters')
|
53
|
+
end
|
63
54
|
|
64
|
-
|
55
|
+
questionnaire_package = Fixtures.questionnaire_package_for_test(test_id)
|
56
|
+
unless questionnaire_package
|
57
|
+
return operation_outcome('error', 'business-rule', "No Questionnaire found for Inferno test #{test_id}")
|
65
58
|
end
|
66
59
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
elsif bundles.empty?
|
72
|
-
# no questionnaire for this test ...
|
73
|
-
operation_outcome_issue = FHIR::OperationOutcome::Issue.new
|
74
|
-
operation_outcome_issue.severity = 'error'
|
75
|
-
operation_outcome_issue.code = 'business-rule'
|
76
|
-
details = FHIR::CodeableConcept.new
|
77
|
-
details.text = "no questionnaire found for test #{test_id}"
|
78
|
-
operation_outcome_issue.details = details
|
79
|
-
issues << operation_outcome_issue
|
80
|
-
end
|
60
|
+
questionnaire_canonical = find_questionnaire_canonical(questionnaire_package)
|
61
|
+
|
62
|
+
other_questionnaire_params = input_parameters.parameter.filter do |param|
|
63
|
+
param.name == 'questionnaire' && param.valueCanonical != questionnaire_canonical
|
81
64
|
end
|
82
65
|
|
83
|
-
|
66
|
+
return questionnaire_package unless other_questionnaire_params.any?
|
67
|
+
|
68
|
+
FHIR::Parameters.new(
|
69
|
+
parameter: [
|
70
|
+
FHIR::Parameters::Parameter.new(
|
71
|
+
name: 'PackageBundle',
|
72
|
+
resource: questionnaire_package
|
73
|
+
),
|
74
|
+
FHIR::Parameters::Parameter.new(
|
75
|
+
name: 'Outcome',
|
76
|
+
resource: FHIR::OperationOutcome.new(
|
77
|
+
issue: other_questionnaire_params.map do |param|
|
78
|
+
outcome_issue('warning', 'not-found', "Questionnaire #{param.valueCanonical} does not exist")
|
79
|
+
end
|
80
|
+
)
|
81
|
+
)
|
82
|
+
]
|
83
|
+
)
|
84
84
|
end
|
85
85
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
if questionnaire_bundle
|
90
|
-
bundles << questionnaire_bundle
|
91
|
-
else
|
92
|
-
operation_outcome_issue = FHIR::OperationOutcome::Issue.new
|
93
|
-
operation_outcome_issue.severity = 'warning'
|
94
|
-
operation_outcome_issue.code = 'value'
|
95
|
-
details = FHIR::CodeableConcept.new
|
96
|
-
details.text = "Questionnaire Canonical #{questionnaire_canonical} does not exist"
|
97
|
-
operation_outcome_issue.details = details
|
98
|
-
issues << operation_outcome_issue
|
99
|
-
end
|
86
|
+
def find_questionnaire_canonical(questionnaire_package)
|
87
|
+
questionnaire_package&.entry&.find { |e| e.resource.is_a?(FHIR::Questionnaire) }&.resource&.url
|
100
88
|
end
|
101
89
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
return_param = FHIR::Parameters::Parameter.new
|
106
|
-
return_param.name = 'return'
|
107
|
-
return_param.resource = one_bundle
|
108
|
-
response.parameter << return_param
|
109
|
-
end
|
90
|
+
def operation_outcome(severity, code, text = nil)
|
91
|
+
FHIR::OperationOutcome.new(issue: outcome_issue(severity, code, text))
|
92
|
+
end
|
110
93
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
outcome_param = FHIR::Parameters::Parameter.new
|
115
|
-
outcome_param.name = 'outcome'
|
116
|
-
outcome_param.resource = outcome
|
117
|
-
response.parameter << outcome_param
|
94
|
+
def outcome_issue(severity, code, text = nil)
|
95
|
+
FHIR::OperationOutcome::Issue.new(severity:, code:).tap do |issue|
|
96
|
+
issue.details = FHIR::CodeableConcept.new(text:) if text.present?
|
118
97
|
end
|
119
|
-
|
120
|
-
response
|
121
98
|
end
|
122
99
|
end
|
123
100
|
end
|
data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_next_questionnaire_expressions_test.rb
CHANGED
@@ -6,8 +6,8 @@ module DaVinciDTRTestKit
|
|
6
6
|
id :dtr_v201_payer_adaptive_next_form_expressions_test
|
7
7
|
title 'Questionnaire(s) contains items with expressions necessary for pre-population'
|
8
8
|
description %(
|
9
|
-
Inferno checks that the payer server response has appropriate expressions and that
|
10
|
-
|
9
|
+
Inferno checks that the payer server response to $next-question operation has appropriate expressions and that
|
10
|
+
expressions are written in cql.
|
11
11
|
)
|
12
12
|
|
13
13
|
run do
|
data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_request_validation_test.rb
CHANGED
@@ -3,7 +3,7 @@ module DaVinciDTRTestKit
|
|
3
3
|
class PayerAdaptiveFormRequestTest < Inferno::Test
|
4
4
|
include URLs
|
5
5
|
include DaVinciDTRTestKit::ValidationTest
|
6
|
-
title '
|
6
|
+
title 'User Input Validation: Questionnaire Package request is valid'
|
7
7
|
description %(
|
8
8
|
This test validates the conformance of the client's request to the
|
9
9
|
[DTR Questionnaire Package Input Parameters](http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-qpackage-input-parameters)
|
@@ -13,32 +13,24 @@ module DaVinciDTRTestKit
|
|
13
13
|
values. CodeableConcept element bindings will fail if none of their codings have a code/system belonging
|
14
14
|
to the bound ValueSet. Quantity, Coding, and code element bindings will fail if their code/system are not found in
|
15
15
|
the valueset.
|
16
|
-
|
17
|
-
This test may process multiple resources, labeling messages with the corresponding tested resources
|
18
|
-
in the order that they were received.
|
19
16
|
)
|
20
17
|
id :payer_server_adaptive_questionnaire_request_validation
|
21
18
|
|
22
19
|
run do
|
23
20
|
skip_if retrieval_method == 'Static', 'Performing only static flow tests - only one flow is required.'
|
21
|
+
profile_with_version = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-qpackage-input-parameters|2.0.1'
|
24
22
|
if initial_adaptive_questionnaire_request.nil?
|
25
|
-
|
26
|
-
|
23
|
+
requests = load_tagged_requests(QUESTIONNAIRE_TAG)
|
24
|
+
skip_if requests.blank?, 'No request resource received from the client.'
|
25
|
+
# making the assumption that only one request was made here - if there were multiple, we are only validating the
|
26
|
+
# first
|
27
|
+
resource_is_valid?(resource: FHIR.from_contents(requests[0].request[:body]), profile_url: profile_with_version)
|
27
28
|
else
|
28
|
-
|
29
|
-
|
29
|
+
request = FHIR.from_contents(initial_adaptive_questionnaire_request)
|
30
|
+
resource_is_valid?(resource: request, profile_url: profile_with_version)
|
30
31
|
end
|
31
|
-
|
32
|
-
|
33
|
-
resources,
|
34
|
-
:parameters,
|
35
|
-
'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-qpackage-input-parameters',
|
36
|
-
questionnaire_package_url,
|
37
|
-
using_manual_entry
|
38
|
-
)
|
39
|
-
rescue Inferno::Exceptions::AssertionException => e
|
40
|
-
msg = e.message.to_s.strip
|
41
|
-
skip msg
|
32
|
+
errors_found = messages.any? { |message| message[:type] == 'error' }
|
33
|
+
skip_if errors_found, "Resource does not conform to the profile #{profile_with_version}"
|
42
34
|
end
|
43
35
|
end
|
44
36
|
end
|
@@ -20,21 +20,21 @@ module DaVinciDTRTestKit
|
|
20
20
|
|
21
21
|
run do
|
22
22
|
skip_if retrieval_method == 'Static', 'Performing only static flow tests - only one flow is required.'
|
23
|
-
test_passed =
|
24
|
-
profile_url = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/DTR-QPackageBundle'
|
23
|
+
test_passed = true
|
24
|
+
profile_url = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/DTR-QPackageBundle|2.0.1'
|
25
25
|
assert !scratch[:adaptive_responses].nil?, 'No resources to validate.'
|
26
26
|
scratch[:adaptive_responses].each_with_index do |resource, index|
|
27
27
|
fhir_resource = FHIR.from_contents(resource.response[:body])
|
28
28
|
fhir_resource.parameter.each do |param|
|
29
29
|
resource_is_valid = validate_resource(param.resource, :bundle, profile_url, index)
|
30
|
-
test_passed =
|
30
|
+
test_passed = false unless resource_is_valid
|
31
31
|
rescue StandardError
|
32
32
|
next
|
33
33
|
end
|
34
34
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
if !test_passed && !tests_failed[profile_url].blank?
|
36
|
+
assert test_passed, "Not all returned resources conform to the profile: #{profile_url}"
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -20,23 +20,23 @@ module DaVinciDTRTestKit
|
|
20
20
|
|
21
21
|
run do
|
22
22
|
skip_if retrieval_method == 'Static', 'Performing only static flow tests - only one flow is required.'
|
23
|
-
test_passed =
|
24
|
-
profile_url = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaire-adapt-search'
|
23
|
+
test_passed = true
|
24
|
+
profile_url = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaire-adapt-search|2.0.1'
|
25
25
|
assert !scratch[:adaptive_responses].nil?, 'No resources to validate.'
|
26
26
|
scratch[:adaptive_responses].each_with_index do |resource, index|
|
27
27
|
fhir_resource = FHIR.from_contents(resource.response[:body])
|
28
28
|
fhir_resource.parameter.each do |param|
|
29
29
|
param.resource.entry.each do |entry|
|
30
30
|
resource_is_valid = validate_resource(entry.resource, :questionnaire, profile_url, index)
|
31
|
-
test_passed =
|
31
|
+
test_passed = false unless resource_is_valid
|
32
32
|
end
|
33
33
|
rescue StandardError
|
34
34
|
next
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
if !test_passed && !tests_failed[profile_url].blank?
|
38
|
+
assert test_passed, "Not all returned resources conform to the profile: #{profile_url}"
|
39
|
+
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_validation_test.rb
CHANGED
@@ -15,35 +15,34 @@ module DaVinciDTRTestKit
|
|
15
15
|
to the bound ValueSet. Quantity, Coding, and code element bindings will fail if their code/system are not found in
|
16
16
|
the valueset.
|
17
17
|
|
18
|
-
This test may process multiple resources, labeling messages with the corresponding tested resources
|
19
18
|
This test may process multiple resources, labeling messages with the corresponding tested resources
|
20
19
|
in the order that they were received.
|
21
20
|
)
|
22
21
|
|
23
22
|
run do
|
24
23
|
skip_if retrieval_method == 'Static', 'Performing only static flow tests - only one flow is required.'
|
24
|
+
profile_with_version = 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-qpackage-output-parameters|2.0.1'
|
25
25
|
endpoint = custom_endpoint.blank? ? '/Questionnaire/$questionnaire-package' : custom_endpoint
|
26
26
|
if initial_adaptive_questionnaire_request.nil?
|
27
|
-
|
27
|
+
# making the assumption that only one response was received - if there were multiple, we are only validating the
|
28
|
+
# first
|
29
|
+
response = load_tagged_requests(QUESTIONNAIRE_TAG)[0]
|
30
|
+
scratch[:adaptive_responses] = [response]
|
31
|
+
resource = FHIR.from_contents(response.response[:body])
|
28
32
|
else
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
headers: { 'Content-Type': 'application/json' }))
|
34
|
-
end
|
35
|
-
else
|
36
|
-
resources.push(fhir_operation("#{url}#{endpoint}", body: JSON.parse(initial_adaptive_questionnaire_request),
|
37
|
-
headers: { 'Content-Type': 'application/json' }))
|
38
|
-
end
|
33
|
+
response = fhir_operation("#{url}#{endpoint}", body: JSON.parse(initial_adaptive_questionnaire_request),
|
34
|
+
headers: { 'Content-Type': 'application/json' })
|
35
|
+
resource = FHIR.from_contents(response.response[:body])
|
36
|
+
scratch[:adaptive_responses] = [response]
|
39
37
|
end
|
40
|
-
|
38
|
+
|
41
39
|
assert !scratch[:adaptive_responses].nil?, 'No resources to validate.'
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
assert_response_status([200, 201], response: response.response)
|
41
|
+
assert_resource_type(:parameters, resource:)
|
42
|
+
assert_valid_resource(resource:, profile_url: profile_with_version)
|
43
|
+
questionnaire_bundle = resource.parameter.find { |param| param.resource.resourceType == 'Bundle' }&.resource
|
44
|
+
assert questionnaire_bundle, 'No questionnaire bundle found in the response'
|
45
|
+
assert_valid_resource(resource: questionnaire_bundle, profile_url: 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/DTR-QPackageBundle|2.0.1')
|
47
46
|
end
|
48
47
|
end
|
49
48
|
end
|
data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_request_validation_test.rb
CHANGED
@@ -3,7 +3,7 @@ module DaVinciDTRTestKit
|
|
3
3
|
class PayerAdaptiveFormNextRequestTest < Inferno::Test
|
4
4
|
include URLs
|
5
5
|
include DaVinciDTRTestKit::ValidationTest
|
6
|
-
title '
|
6
|
+
title 'User Input Validation: Next Question request is valid'
|
7
7
|
description %(
|
8
8
|
This test validates the conformance of the client's request to the
|
9
9
|
[SDC Parameters Next Question In](http://hl7.org/fhir/uv/sdc/StructureDefinition/parameters-questionnaire-next-question-in)
|
@@ -49,13 +49,12 @@ module DaVinciDTRTestKit
|
|
49
49
|
using_manual_entry
|
50
50
|
)
|
51
51
|
else
|
52
|
-
|
53
|
-
|
54
|
-
or http://hl7.org/fhir/uv/sdc/StructureDefinition/parameters-questionnaire-next-question-in"
|
52
|
+
messages << { type: 'error',
|
53
|
+
message: format_markdown("No resources were of type 'Parameters' or 'QuestionnaireResponse'") }
|
55
54
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
errors_found = messages.any? { |message| message[:type] == 'error' }
|
56
|
+
skip_if errors_found, "No resources conform to the profiles http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse-adapt
|
57
|
+
or http://hl7.org/fhir/uv/sdc/StructureDefinition/parameters-questionnaire-next-question-in"
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_response_validation_test.rb
CHANGED
@@ -14,7 +14,6 @@ module DaVinciDTRTestKit
|
|
14
14
|
to the bound ValueSet. Quantity, Coding, and code element bindings will fail if their code/system are not found in
|
15
15
|
the valueset.
|
16
16
|
|
17
|
-
This test may process multiple resources, labeling messages with the corresponding tested resources
|
18
17
|
This test may process multiple resources, labeling messages with the corresponding tested resources
|
19
18
|
in the order that they were received.
|
20
19
|
)
|
@@ -38,6 +37,9 @@ module DaVinciDTRTestKit
|
|
38
37
|
:questionnaireResponse,
|
39
38
|
'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse'
|
40
39
|
)
|
40
|
+
errors_found = messages.any? { |message| message[:type] == 'error' }
|
41
|
+
skip_if errors_found, "No resources conform to the profiles http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse-adapt
|
42
|
+
or http://hl7.org/fhir/uv/sdc/StructureDefinition/parameters-questionnaire-next-question-in"
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|