onc_certification_g10_test_kit 2.0.0.rc3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/inferno/terminology/tasks/check_built_terminology.rb +14 -12
- data/lib/onc_certification_g10_test_kit/bulk_data_authorization.rb +7 -4
- data/lib/onc_certification_g10_test_kit/bulk_data_group_export.rb +60 -17
- data/lib/onc_certification_g10_test_kit/bulk_data_group_export_validation.rb +10 -6
- data/lib/onc_certification_g10_test_kit/bulk_export_validation_tester.rb +48 -18
- data/lib/onc_certification_g10_test_kit/configuration_checker.rb +6 -5
- data/lib/onc_certification_g10_test_kit/multi_patient_api.rb +11 -0
- data/lib/onc_certification_g10_test_kit/onc_program_procedure.yml +1451 -0
- data/lib/onc_certification_g10_test_kit/profile_guesser.rb +2 -2
- data/lib/onc_certification_g10_test_kit/restricted_resource_type_access_group.rb +13 -13
- data/lib/onc_certification_g10_test_kit/single_patient_api_group.rb +89 -0
- data/lib/onc_certification_g10_test_kit/smart_app_launch_invalid_aud_group.rb +13 -12
- data/lib/onc_certification_g10_test_kit/smart_ehr_practitioner_app_group.rb +11 -5
- data/lib/onc_certification_g10_test_kit/smart_invalid_launch_group.rb +13 -16
- data/lib/onc_certification_g10_test_kit/smart_invalid_token_group.rb +11 -4
- data/lib/onc_certification_g10_test_kit/smart_limited_app_group.rb +18 -4
- data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group.rb +15 -3
- data/lib/onc_certification_g10_test_kit/smart_standalone_patient_app_group.rb +8 -3
- data/lib/onc_certification_g10_test_kit/tasks/generate_matrix.rb +247 -0
- data/lib/onc_certification_g10_test_kit/tasks/test_procedure.rb +65 -0
- data/lib/onc_certification_g10_test_kit/token_revocation_group.rb +60 -60
- data/lib/onc_certification_g10_test_kit/unrestricted_resource_type_access_group.rb +13 -13
- data/lib/onc_certification_g10_test_kit/version.rb +1 -1
- data/lib/onc_certification_g10_test_kit/visual_inspection_and_attestations_group.rb +7 -6
- data/lib/onc_certification_g10_test_kit.rb +15 -82
- metadata +16 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 645d73a288a5f76e9b94634ed2be17253ce34f0e0eb6df7df6b7410e7a1d9175
|
4
|
+
data.tar.gz: f299ae93813a1b05ef838824bfbb4f7f7734e8695118e37f0eb7b3c08e28a2c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46773dc8eb92ee96afb593506a1b16b5f6af23c62f9ff7f988789d27a38b7a54bab21f69e03f336df1d9c8d3e4f3929b69028a9d99fffebdeb2eda12f66fbce7
|
7
|
+
data.tar.gz: 87c04c10d334d9c469af26d8618cfa7ec83246b008f0f05fae632f8f2029a11f89e00dda164b2aedc318aab02055f4e928d8bb11545d84f9c32f2be70477116a
|
@@ -4,9 +4,11 @@ module Inferno
|
|
4
4
|
module Terminology
|
5
5
|
module Tasks
|
6
6
|
class CheckBuiltTerminology
|
7
|
-
|
7
|
+
NON_UMLS_SYSTEMS = [
|
8
8
|
'http://hl7.org/fhir/ValueSet/mimetypes',
|
9
|
-
'urn:ietf:bcp:13'
|
9
|
+
'urn:ietf:bcp:13',
|
10
|
+
'http://hl7.org/fhir/us/core/ValueSet/simple-language',
|
11
|
+
'urn:ietf:bcp:47'
|
10
12
|
].freeze
|
11
13
|
|
12
14
|
def run
|
@@ -15,16 +17,16 @@ module Inferno
|
|
15
17
|
return
|
16
18
|
end
|
17
19
|
|
18
|
-
if
|
19
|
-
Inferno.logger.info <<~
|
20
|
+
if only_non_umls_mismatch?
|
21
|
+
Inferno.logger.info <<~NON_UMLS
|
20
22
|
Terminology built successfully.
|
21
23
|
|
22
|
-
|
23
|
-
result of
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
Some terminology not based on UMLS did not match, but this can be
|
25
|
+
a result of these terminologies having a different update schedule
|
26
|
+
than UMLS. As long as the actual number of codes is close to the
|
27
|
+
expected number, this does not does not reflect a problem with the
|
28
|
+
terminology build.
|
29
|
+
NON_UMLS
|
28
30
|
else
|
29
31
|
Inferno.logger.info 'Terminology build results different than expected.'
|
30
32
|
end
|
@@ -61,8 +63,8 @@ module Inferno
|
|
61
63
|
new_manifest.find { |value_set| value_set[:url] == url }
|
62
64
|
end
|
63
65
|
|
64
|
-
def
|
65
|
-
mismatched_value_sets.all? { |value_set|
|
66
|
+
def only_non_umls_mismatch?
|
67
|
+
mismatched_value_sets.all? { |value_set| NON_UMLS_SYSTEMS.include? value_set[:url] }
|
66
68
|
end
|
67
69
|
|
68
70
|
def mismatched_value_set_message(expected_value_set)
|
@@ -21,8 +21,9 @@ module ONCCertificationG10TestKit
|
|
21
21
|
|
22
22
|
input :bulk_token_endpoint,
|
23
23
|
title: 'Backend Services Token Endpoint',
|
24
|
-
description:
|
25
|
-
|
24
|
+
description: <<~DESCRIPTION
|
25
|
+
The OAuth 2.0 Token Endpoint used by the Backend Services specification to provide bearer tokens.
|
26
|
+
DESCRIPTION
|
26
27
|
input :bulk_client_id,
|
27
28
|
title: 'Bulk Data Client ID',
|
28
29
|
description: 'Client ID provided at registration to the Inferno application.'
|
@@ -32,8 +33,10 @@ module ONCCertificationG10TestKit
|
|
32
33
|
default: 'system/*.read'
|
33
34
|
input :bulk_encryption_method,
|
34
35
|
title: 'Encryption Method',
|
35
|
-
description:
|
36
|
-
|
36
|
+
description: <<~DESCRIPTION,
|
37
|
+
The server is required to suport either ES384 or RS384 encryption methods for JWT signature verification.
|
38
|
+
Select which method to use.
|
39
|
+
DESCRIPTION
|
37
40
|
type: 'radio',
|
38
41
|
default: 'ES384',
|
39
42
|
options: {
|
@@ -19,11 +19,12 @@ module ONCCertificationG10TestKit
|
|
19
19
|
description: 'The Group ID associated with the group of patients to be exported.'
|
20
20
|
input :bulk_timeout,
|
21
21
|
title: 'Export Times Out after (1-600)',
|
22
|
-
description:
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
description: <<~DESCRIPTION,
|
23
|
+
While testing, Inferno waits for the server to complete the exporting task. If the calculated totalTime is
|
24
|
+
greater than the timeout value specified here, Inferno bulk client stops testing. Please enter an integer
|
25
|
+
for the maximum wait time in seconds. If timeout is less than 1, Inferno uses default value 180. If the
|
26
|
+
timeout is greater than 600 (10 minutes), Inferno uses the maximum value 600.
|
27
|
+
DESCRIPTION
|
27
28
|
default: 180
|
28
29
|
|
29
30
|
output :requires_access_token, :status_output, :bulk_download_url
|
@@ -56,9 +57,30 @@ module ONCCertificationG10TestKit
|
|
56
57
|
test do
|
57
58
|
title 'Bulk Data Server declares support for Group export operation in CapabilityStatement'
|
58
59
|
description <<~DESCRIPTION
|
59
|
-
|
60
|
+
This test verifies that the Bulk Data Server declares support for
|
61
|
+
Group/[id]/$export operation in its server CapabilityStatement.
|
62
|
+
|
63
|
+
Given flexibility in the FHIR specification for declaring constrained
|
64
|
+
OperationDefinitions, this test only verifies that the server declares
|
65
|
+
any operation on the Group resource. It does not verify that it
|
66
|
+
declares the standard group export OperationDefinition provided in the
|
67
|
+
Bulk Data specification, nor does it attempt to resolve any non-standard
|
68
|
+
OperationDefinitions to verify if it is a constrained version of the
|
69
|
+
standard OperationDefintion.
|
70
|
+
|
71
|
+
This test will provide a warning if no operations are declared at
|
72
|
+
`Group/[id]/$export`, via the
|
73
|
+
`CapabilityStatement.rest.resource.operation.name` element. It will
|
74
|
+
also provide an informational message if an operation on the Group
|
75
|
+
resource exists, but does not point to the standard OperationDefinition
|
76
|
+
canonical URL:
|
77
|
+
http://hl7.org/fhir/uv/bulkdata/OperationDefinition/group-export
|
78
|
+
|
79
|
+
Additionally, this test provides a warning if the bulk data server does
|
80
|
+
not include the following URL in its `CapabilityStatement.instantiates`
|
81
|
+
element: http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data
|
82
|
+
|
60
83
|
DESCRIPTION
|
61
|
-
# link 'http://hl7.org/fhir/uv/bulkdata/STU1/OperationDefinition-group-export.html'
|
62
84
|
|
63
85
|
run do
|
64
86
|
fhir_get_capability_statement(client: :bulk_server)
|
@@ -67,18 +89,39 @@ module ONCCertificationG10TestKit
|
|
67
89
|
assert_valid_json(request.response_body)
|
68
90
|
capability_statement = FHIR.from_contents(request.response_body)
|
69
91
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
92
|
+
warning do
|
93
|
+
has_instantiates = capability_statement&.instantiates&.any? do |canonical|
|
94
|
+
canonical.match(%r{^http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data(\|\S+)?$})
|
95
|
+
end
|
96
|
+
assert has_instantiates,
|
97
|
+
'Server did not declare conformance to the Bulk Data IG by including ' \
|
98
|
+
"'http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data' in " \
|
99
|
+
" CapabilityStatement.instantiates element (#{capability_statement&.instantiates})"
|
100
|
+
end
|
101
|
+
|
102
|
+
group_resource_capabilities = nil
|
103
|
+
|
104
|
+
capability_statement&.rest&.each do |rest|
|
105
|
+
group_resource_capabilities = rest.resource&.find do |resource|
|
106
|
+
resource.type == 'Group'
|
77
107
|
end
|
78
108
|
end
|
79
109
|
|
80
|
-
assert
|
81
|
-
'Server CapabilityStatement did not declare support for
|
110
|
+
assert group_resource_capabilities.respond_to?(:operation) && group_resource_capabilities.operation&.any?,
|
111
|
+
'Server CapabilityStatement did not declare support for any operations on the Group resource'
|
112
|
+
|
113
|
+
has_export_operation = group_resource_capabilities.operation&.any? do |operation|
|
114
|
+
name_match = (operation.name == 'export')
|
115
|
+
if name_match && !operation.definition&.match(%r{^http://hl7.org/fhir/uv/bulkdata/OperationDefinition/group-export(\|\S+)?$})
|
116
|
+
info('Server CapabilityStatement does not include export operation with definition http://hl7.org/fhir/uv/bulkdata/OperationDefinition/group-export')
|
117
|
+
end
|
118
|
+
name_match
|
119
|
+
end
|
120
|
+
warning do
|
121
|
+
assert has_export_operation,
|
122
|
+
'Server CapabilityStatement did not declare support for an operation named "export" in the Group ' \
|
123
|
+
' resource (operation.name should be "export")'
|
124
|
+
end
|
82
125
|
end
|
83
126
|
end
|
84
127
|
|
@@ -161,7 +204,7 @@ module ONCCertificationG10TestKit
|
|
161
204
|
start = Time.now
|
162
205
|
|
163
206
|
loop do
|
164
|
-
get(polling_url, headers: { authorization: "Bearer #{bearer_token}" })
|
207
|
+
get(polling_url, headers: { authorization: "Bearer #{bearer_token}", accept: 'application/json' })
|
165
208
|
|
166
209
|
retry_after_val = request.response_header('retry-after')&.value.to_i
|
167
210
|
|
@@ -17,14 +17,18 @@ module ONCCertificationG10TestKit
|
|
17
17
|
optional: true
|
18
18
|
input :bulk_patient_ids_in_group,
|
19
19
|
title: 'Patient IDs in exported Group',
|
20
|
-
description:
|
21
|
-
|
22
|
-
|
20
|
+
description: <<~DESCRIPTION
|
21
|
+
Comma separated list of every Patient ID that is in the specified Group. This information is provided by
|
22
|
+
the system under test to verify that data returned matches expectations. Leave blank to not verify Group
|
23
|
+
inclusion.
|
24
|
+
DESCRIPTION
|
23
25
|
input :bulk_device_types_in_group,
|
24
26
|
title: 'Implantable Device Type Codes in exported Group',
|
25
|
-
description:
|
26
|
-
|
27
|
-
|
27
|
+
description: <<~DESCRIPTION,
|
28
|
+
Comma separated list of every Implantable Device type that is in the specified Group. This information is
|
29
|
+
provided by the system under test to verify that data returned matches expectations. Leave blank to verify
|
30
|
+
all Device resources against the Implantable Device profile.
|
31
|
+
DESCRIPTION
|
28
32
|
optional: true
|
29
33
|
|
30
34
|
test from: :tls_version_test do
|
@@ -13,23 +13,26 @@ module ONCCertificationG10TestKit
|
|
13
13
|
|
14
14
|
def observation_metadata
|
15
15
|
[
|
16
|
-
USCoreTestKit::PediatricBmiForAgeGroup.metadata,
|
17
|
-
USCoreTestKit::PediatricWeightForHeightGroup.metadata,
|
18
|
-
USCoreTestKit::ObservationLabGroup.metadata,
|
19
|
-
USCoreTestKit::PulseOximetryGroup.metadata,
|
20
|
-
USCoreTestKit::SmokingstatusGroup.metadata,
|
21
|
-
USCoreTestKit::HeadCircumferenceGroup.metadata,
|
22
|
-
USCoreTestKit::BpGroup.metadata,
|
23
|
-
USCoreTestKit::BodyheightGroup.metadata,
|
24
|
-
USCoreTestKit::BodytempGroup.metadata,
|
25
|
-
USCoreTestKit::BodyweightGroup.metadata,
|
26
|
-
USCoreTestKit::HeartrateGroup.metadata,
|
27
|
-
USCoreTestKit::ResprateGroup.metadata
|
16
|
+
USCoreTestKit::USCoreV311::PediatricBmiForAgeGroup.metadata,
|
17
|
+
USCoreTestKit::USCoreV311::PediatricWeightForHeightGroup.metadata,
|
18
|
+
USCoreTestKit::USCoreV311::ObservationLabGroup.metadata,
|
19
|
+
USCoreTestKit::USCoreV311::PulseOximetryGroup.metadata,
|
20
|
+
USCoreTestKit::USCoreV311::SmokingstatusGroup.metadata,
|
21
|
+
USCoreTestKit::USCoreV311::HeadCircumferenceGroup.metadata,
|
22
|
+
USCoreTestKit::USCoreV311::BpGroup.metadata,
|
23
|
+
USCoreTestKit::USCoreV311::BodyheightGroup.metadata,
|
24
|
+
USCoreTestKit::USCoreV311::BodytempGroup.metadata,
|
25
|
+
USCoreTestKit::USCoreV311::BodyweightGroup.metadata,
|
26
|
+
USCoreTestKit::USCoreV311::HeartrateGroup.metadata,
|
27
|
+
USCoreTestKit::USCoreV311::ResprateGroup.metadata
|
28
28
|
]
|
29
29
|
end
|
30
30
|
|
31
31
|
def diagnostic_metadata
|
32
|
-
[
|
32
|
+
[
|
33
|
+
USCoreTestKit::USCoreV311::DiagnosticReportLabGroup.metadata,
|
34
|
+
USCoreTestKit::USCoreV311::DiagnosticReportNoteGroup.metadata
|
35
|
+
]
|
33
36
|
end
|
34
37
|
|
35
38
|
def determine_metadata
|
@@ -37,17 +40,21 @@ module ONCCertificationG10TestKit
|
|
37
40
|
return diagnostic_metadata if resource_type == 'DiagnosticReport'
|
38
41
|
|
39
42
|
if resource_type == 'Location' || resource_type == 'Medication'
|
40
|
-
return Array.wrap(USCoreTestKit::USCoreTestSuite.metadata.find do |meta|
|
43
|
+
return Array.wrap(USCoreTestKit::USCoreV311::USCoreTestSuite.metadata.find do |meta|
|
41
44
|
meta.resource == resource_type
|
42
45
|
end)
|
43
46
|
end
|
44
|
-
["USCoreTestKit::#{resource_type}Group".constantize.metadata]
|
47
|
+
["USCoreTestKit::USCoreV311::#{resource_type}Group".constantize.metadata]
|
45
48
|
end
|
46
49
|
|
47
50
|
def metadata_list
|
48
51
|
@metadata_list ||= determine_metadata
|
49
52
|
end
|
50
53
|
|
54
|
+
def resources_from_all_files
|
55
|
+
@resources_from_all_files ||= {}
|
56
|
+
end
|
57
|
+
|
51
58
|
def patient_ids_seen
|
52
59
|
scratch[:patient_ids_seen] ||= []
|
53
60
|
end
|
@@ -58,7 +65,7 @@ module ONCCertificationG10TestKit
|
|
58
65
|
headers
|
59
66
|
end
|
60
67
|
|
61
|
-
def stream_ndjson(endpoint, headers, process_chunk_line, process_response)
|
68
|
+
def stream_ndjson(endpoint, headers, process_chunk_line, process_response) # rubocop:disable Metrics/CyclomaticComplexity
|
62
69
|
hanging_chunk = String.new
|
63
70
|
|
64
71
|
process_body = proc { |chunk|
|
@@ -74,6 +81,24 @@ module ONCCertificationG10TestKit
|
|
74
81
|
|
75
82
|
stream(process_body, endpoint, headers: headers)
|
76
83
|
|
84
|
+
max_redirect = 5
|
85
|
+
|
86
|
+
while [301, 302, 303, 307].include?(response[:status]) &&
|
87
|
+
request.response_header('location')&.value.present? &&
|
88
|
+
max_redirect.positive?
|
89
|
+
|
90
|
+
max_redirect -= 1
|
91
|
+
|
92
|
+
redirect_url = request.response_header('location')&.value
|
93
|
+
|
94
|
+
# handle relative redirects
|
95
|
+
redirect_url = URI.parse(endpoint).merge(redirect_url).to_s unless redirect_url.start_with?('http')
|
96
|
+
|
97
|
+
redirect_headers = headers.reject { |key, _value| key == :authorization }
|
98
|
+
|
99
|
+
stream(process_body, redirect_url, headers: redirect_headers)
|
100
|
+
end
|
101
|
+
|
77
102
|
process_chunk_line.call(hanging_chunk)
|
78
103
|
process_response.call(response)
|
79
104
|
end
|
@@ -105,6 +130,7 @@ module ONCCertificationG10TestKit
|
|
105
130
|
@metadata = meta
|
106
131
|
@missing_elements = nil
|
107
132
|
@missing_slices = nil
|
133
|
+
@missing_extensions = nil
|
108
134
|
begin
|
109
135
|
perform_must_support_test(resources[meta.profile_url])
|
110
136
|
rescue Inferno::Exceptions::PassException
|
@@ -152,8 +178,9 @@ module ONCCertificationG10TestKit
|
|
152
178
|
}
|
153
179
|
|
154
180
|
stream_ndjson(url, build_headers(requires_access_token), process_line, process_headers)
|
155
|
-
|
156
|
-
|
181
|
+
resources_from_all_files.merge!(resources) do |_key, all_resources, file_resources|
|
182
|
+
all_resources | file_resources
|
183
|
+
end
|
157
184
|
line_count
|
158
185
|
end
|
159
186
|
|
@@ -169,11 +196,14 @@ module ONCCertificationG10TestKit
|
|
169
196
|
skip message
|
170
197
|
end
|
171
198
|
|
199
|
+
@resources_from_all_files = {}
|
172
200
|
success_count = 0
|
173
201
|
file_list.each do |file|
|
174
202
|
success_count += check_file_request(file['url'])
|
175
203
|
end
|
176
204
|
|
205
|
+
validate_conformance(resources_from_all_files)
|
206
|
+
|
177
207
|
pass "Successfully validated #{success_count} #{resource_type} resource(s)."
|
178
208
|
end
|
179
209
|
end
|
@@ -55,7 +55,7 @@ module ONCCertificationG10TestKit
|
|
55
55
|
success_messages << "* `#{url}`: #{actual_value_set[:count]} codes"
|
56
56
|
elsif actual_value_set.nil?
|
57
57
|
error_messages << "* `#{url}`: Not loaded"
|
58
|
-
elsif terminology_checker.class::
|
58
|
+
elsif terminology_checker.class::NON_UMLS_SYSTEMS.include? url
|
59
59
|
warning_messages <<
|
60
60
|
"* `#{url}`: Expected codes: #{expected_value_set[:count]} Actual codes: #{actual_value_set[:count]}"
|
61
61
|
else
|
@@ -74,10 +74,11 @@ module ONCCertificationG10TestKit
|
|
74
74
|
|
75
75
|
if warning_messages.present?
|
76
76
|
warning_message = <<~WARNING
|
77
|
-
|
78
|
-
result of
|
79
|
-
|
80
|
-
|
77
|
+
Some terminology not based on UMLS did not match, but this can be a
|
78
|
+
result of these terminologies having a different update schedule than
|
79
|
+
UMLS. As long as the actual number of codes is close to the expected
|
80
|
+
number, this does not does not reflect a problem with the terminology
|
81
|
+
build.
|
81
82
|
WARNING
|
82
83
|
messages << {
|
83
84
|
type: 'warning',
|
@@ -36,6 +36,17 @@ module ONCCertificationG10TestKit
|
|
36
36
|
id :multi_patient_api
|
37
37
|
run_as_group
|
38
38
|
|
39
|
+
input_order :bulk_server_url,
|
40
|
+
:bulk_token_endpoint,
|
41
|
+
:bulk_client_id,
|
42
|
+
:bulk_scope,
|
43
|
+
:bulk_encryption_method,
|
44
|
+
:group_id,
|
45
|
+
:bulk_patient_ids_in_group,
|
46
|
+
:bulk_device_types_in_group,
|
47
|
+
:lines_to_validate,
|
48
|
+
:bulk_timeout
|
49
|
+
|
39
50
|
group from: :bulk_data_authorization
|
40
51
|
group from: :bulk_data_group_export
|
41
52
|
group from: :bulk_data_group_export_validation
|