davinci_pdex_test_kit 0.9.1
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_pdex_test_kit/docs/payer_client_suite_description_v200.md +91 -0
- data/lib/davinci_pdex_test_kit/docs/payer_server_suite_description_v200.md +119 -0
- data/lib/davinci_pdex_test_kit/ext/inferno_core/record_response_route.rb +98 -0
- data/lib/davinci_pdex_test_kit/ext/inferno_core/request.rb +19 -0
- data/lib/davinci_pdex_test_kit/ext/inferno_core/runnable.rb +18 -0
- data/lib/davinci_pdex_test_kit/fhir_resource_navigation.rb +154 -0
- data/lib/davinci_pdex_test_kit/group_metadata.rb +109 -0
- data/lib/davinci_pdex_test_kit/metadata/mock_capability_statement.json +1052 -0
- data/lib/davinci_pdex_test_kit/metadata/mock_operation_outcome_resource.json +16 -0
- data/lib/davinci_pdex_test_kit/mock_server.rb +247 -0
- data/lib/davinci_pdex_test_kit/must_support_test.rb +252 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_submit_test.rb +24 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_validation_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_submit_test.rb +26 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_validation_test.rb +32 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb +94 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/allergyintolerance_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careplan_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careteam_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/condition_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/device_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/diagnosticreport_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/documentreference_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/encounter_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/explanationofbenefit_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/goal_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/immunization_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb +34 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb +28 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/location_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationdispense_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationrequest_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/observation_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/organization_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/patient_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitioner_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitionerrole_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/procedure_clinical_data_request_test.rb +23 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client/collection.rb +46 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb +152 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_conformance_test.rb +33 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_local_references_test.rb +35 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_has_minimal_data_test.rb +52 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_must_support_test.rb +28 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_id_search_test.rb +58 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_identifier_search_test.rb +58 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_must_support_test.rb +40 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_last_updated_search_test.rb +63 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_service_date_search_test.rb +63 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_type_search_test.rb +63 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_use_search_test.rb +68 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_provenance_revinclude_search_test.rb +52 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_read_test.rb +26 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_reference_resolution_test.rb +43 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_validation_test.rb +40 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit_group.rb +105 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/export_patient_group.rb +103 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/export_validation_group.rb +59 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/multiple_member_matches_group.rb +66 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/no_member_matches_group.rb +69 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_clinical_data.rb +66 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_everything.rb +184 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_export.rb +67 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_member_match.rb +171 -0
- data/lib/davinci_pdex_test_kit/pdex_payer_server_suite.rb +158 -0
- data/lib/davinci_pdex_test_kit/pdex_provider_client_suite.rb +36 -0
- data/lib/davinci_pdex_test_kit/tags.rb +9 -0
- data/lib/davinci_pdex_test_kit/urls.rb +67 -0
- data/lib/davinci_pdex_test_kit/user_input_response.rb +32 -0
- data/lib/davinci_pdex_test_kit/version.rb +5 -0
- data/lib/davinci_pdex_test_kit.rb +8 -0
- metadata +218 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'abstract_member_match_request_conformance_test'
|
3
|
+
require_relative 'abstract_member_match_request_local_references_test'
|
4
|
+
require_relative 'coverage_to_link_has_minimal_data_test'
|
5
|
+
require_relative 'coverage_to_link_must_support_test'
|
6
|
+
|
7
|
+
module DaVinciPDexTestKit
|
8
|
+
module PDexPayerServer
|
9
|
+
class NoMemberMatchesGroup < Inferno::TestGroup
|
10
|
+
id :no_member_matches_group
|
11
|
+
title '$member-match with no matches'
|
12
|
+
|
13
|
+
run_as_group
|
14
|
+
|
15
|
+
input :no_member_match_request,
|
16
|
+
title: 'Member Match Request for no matches',
|
17
|
+
description: "A JSON payload for server's $member-match endpoint that has **no matches**",
|
18
|
+
type: 'textarea',
|
19
|
+
optional: true
|
20
|
+
|
21
|
+
group_config = { inputs: { member_match_request: { name: :no_member_match_request } } }
|
22
|
+
|
23
|
+
test from: :abstract_member_match_request_conformance do
|
24
|
+
id :no_member_match_request_conformance
|
25
|
+
config(group_config)
|
26
|
+
|
27
|
+
title '[USER INPUT VALIDATION] Member match request for no matches is valid'
|
28
|
+
description %{
|
29
|
+
Regardless of having no intended matches, this test validates the conformity of the user input to the
|
30
|
+
[HRex Member Match Request Profile](https://hl7.org/fhir/us/davinci-hrex/STU1/StructureDefinition-hrex-parameters-member-match-in.html),
|
31
|
+
ensuring subsequent tests can accurately simulate content. It also checks conformance to the [Parameters Resource](https://hl7.org/fhir/R4/parameters.html),
|
32
|
+
mandatory elements, and terminology. It also checks that the Patient reference with the Consent and CoverageToMatch parameters are local references.
|
33
|
+
}
|
34
|
+
|
35
|
+
# Inherits run
|
36
|
+
end
|
37
|
+
|
38
|
+
test from: :abstract_member_match_request_local_references do
|
39
|
+
id :no_member_match_request_local_references
|
40
|
+
config(group_config)
|
41
|
+
end
|
42
|
+
|
43
|
+
test from: :coverage_to_link_has_minimal_data, config: group_config
|
44
|
+
test from: :coverage_to_link_must_support, config: group_config
|
45
|
+
|
46
|
+
|
47
|
+
test do
|
48
|
+
id :member_match_has_no_matches
|
49
|
+
title 'Server $member-match operation returns 422 Unprocessable Content if no matches are found'
|
50
|
+
description %{
|
51
|
+
See [member matching logic](https://hl7.org/fhir/us/davinci-hrex/STU1/OperationDefinition-member-match.html#member-matching-logic)
|
52
|
+
for specification.
|
53
|
+
|
54
|
+
Server will receive `POST [baseURL]/Patient/$member-match` with the no-member-match-request parameters and
|
55
|
+
is expected to return 422.
|
56
|
+
}
|
57
|
+
|
58
|
+
run do
|
59
|
+
skip_if !no_member_match_request, "No member match request inputted for no-match tests."
|
60
|
+
member_match_request_resource = FHIR.from_contents(no_member_match_request)
|
61
|
+
|
62
|
+
fhir_operation('/Patient/$member-match', body: member_match_request_resource)
|
63
|
+
assert_response_status(422)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DaVinciPDexTestKit
|
4
|
+
module PDexPayerServer
|
5
|
+
class WorkflowClinicalData < Inferno::TestGroup
|
6
|
+
id :workflow_clinical_data
|
7
|
+
title 'Server can respond to search requests for clinical data on the matched patient'
|
8
|
+
short_title 'Clinical data query'
|
9
|
+
description %{
|
10
|
+
# Background
|
11
|
+
The [PDex Implementation Guide](https://hl7.org/fhir/us/davinci-pdex/STU2/payertopayerexchange.html#query-all-clinical-resources-individually)
|
12
|
+
requires Payer servers to support querying of individual clinical resources from
|
13
|
+
its Patient's Member Identifier (aka Patient FHIR ID). This test sequence simulates that step
|
14
|
+
in the workflow by querying for encounters. The full read and search API is tested in
|
15
|
+
its own test group.
|
16
|
+
|
17
|
+
# Test Methodology
|
18
|
+
If the $member-match tests were run and returned a valid member-match-response, then it's
|
19
|
+
patient's id will be used for the query. If not, then the user inputted Patient id is used
|
20
|
+
for this test. If neither is available tests are skipped.
|
21
|
+
}
|
22
|
+
|
23
|
+
input :patient_id,
|
24
|
+
title: 'Patient ID',
|
25
|
+
description: 'Manual Patient ID for testing Clinical Query and $everything without $member-match.',
|
26
|
+
optional: true
|
27
|
+
|
28
|
+
test do
|
29
|
+
id :workflow_clinical_encounter_query
|
30
|
+
title 'Server can provide clinical Encounter data from matched member identifier'
|
31
|
+
description %{
|
32
|
+
Server receives request `GET [baseURL]/Encounter?subject=Patient/[id]` and returns 200.
|
33
|
+
}
|
34
|
+
|
35
|
+
makes_request :pdex_clinical_query
|
36
|
+
|
37
|
+
run do
|
38
|
+
skip_if !patient_id,
|
39
|
+
'No Patient FHIR ID was derived from $member-match response or supplied by user input'
|
40
|
+
|
41
|
+
fhir_search(FHIR::Encounter, params: {patient: "Patient/#{patient_id}"}, name: :pdex_clinical_query)
|
42
|
+
assert_response_status(200)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
test do
|
47
|
+
id :workflow_clinical_encounter_validation
|
48
|
+
title 'Server returned Search bundle with valid Encounter data'
|
49
|
+
description %{
|
50
|
+
Server returned search Bundle of Encounters with least 1 resource entry.
|
51
|
+
}
|
52
|
+
|
53
|
+
uses_request :pdex_clinical_query
|
54
|
+
|
55
|
+
run do
|
56
|
+
assert_valid_resource
|
57
|
+
assert_resource_type('Bundle')
|
58
|
+
assert resource.entry.length > 0, 'Response Bundle must have at least one entry'
|
59
|
+
assert resource.entry.map{|entry| entry.resource}.all? { |res| res.resourceType == 'Encounter' },
|
60
|
+
'Response Bundle must only have Encounter resources'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DaVinciPDexTestKit
|
4
|
+
module PDexPayerServer
|
5
|
+
class WorkflowEverythingTestGroup < Inferno::TestGroup
|
6
|
+
title 'Server can respond to $everything requests on matched patient'
|
7
|
+
short_title '$everything'
|
8
|
+
id :workflow_everything
|
9
|
+
optional
|
10
|
+
description %{
|
11
|
+
# Background
|
12
|
+
|
13
|
+
The Patient $everything operation for Payer-to-Payer exchange. See
|
14
|
+
[PDex Implementation Guide](https://hl7.org/fhir/us/davinci-pdex/STU2/payertopayerexchange.html#everything-operation)
|
15
|
+
and [FHIR R4 documentation](https://hl7.org/fhir/R4/patient-operation-everything.html).
|
16
|
+
|
17
|
+
# Test Methodology
|
18
|
+
|
19
|
+
This test sequence takes a patient id input and executes the `$everything` FHIR operation on it.
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
input :patient_id,
|
24
|
+
title: 'Patient ID',
|
25
|
+
description: 'Manual Patient ID for testing Clinical Query and $everything $export without $member-match.',
|
26
|
+
optional: true
|
27
|
+
|
28
|
+
|
29
|
+
test do
|
30
|
+
title 'Server asserts Patient instance operation $everything in Capability Statement'
|
31
|
+
|
32
|
+
run do
|
33
|
+
fhir_get_capability_statement
|
34
|
+
|
35
|
+
assert_response_status 200
|
36
|
+
assert(
|
37
|
+
resource.rest.one? do |rest_metadata|
|
38
|
+
rest_metadata.resource.select { |resource_metadata| resource_metadata.type == 'Patient' }.first
|
39
|
+
.operation.any? do |operation_metadata|
|
40
|
+
operation_metadata.name == 'everything' && operation_metadata.definition == 'http://hl7.org/fhir/OperationDefinition/Patient-everything'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
test do
|
48
|
+
title 'Server can handle GET /Patient/[ID]/$everything'
|
49
|
+
|
50
|
+
# input :patient_id # borrows properties from workflow_clinical_data
|
51
|
+
|
52
|
+
makes_request :pdex_patient_everything
|
53
|
+
|
54
|
+
run do
|
55
|
+
skip_if !patient_id,
|
56
|
+
"No Patient FHIR ID was derived from $member-match response or supplied by user input"
|
57
|
+
|
58
|
+
fhir_operation("/Patient/#{patient_id}/$everything", operation_method: :get, name: :pdex_patient_everything)
|
59
|
+
|
60
|
+
assert_response_status 200
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
test do
|
65
|
+
title 'Server returns a Bundle resource with requested Patient resource, and all resources conform to FHIR R4'
|
66
|
+
|
67
|
+
input :patient_id # borrows properties from workflow_clinical_data
|
68
|
+
|
69
|
+
uses_request :pdex_patient_everything
|
70
|
+
|
71
|
+
run do
|
72
|
+
skip_if response[:status] != 200, 'Skipped because previous test did not pass'
|
73
|
+
skip_if !patient_id
|
74
|
+
'No Patient ID was derived from $member-match nor supplied from user input'
|
75
|
+
|
76
|
+
assert_valid_resource
|
77
|
+
assert_resource_type(:bundle)
|
78
|
+
assert_valid_bundle_entries
|
79
|
+
assert resource.entry.map(&:resource).map(&:resourceType).any?('Patient'),
|
80
|
+
'Bundle does not include a Patient resource'
|
81
|
+
assert(resource.entry.map(&:resource).one? do |resource|
|
82
|
+
resource.resourceType == 'Patient' && resource.id == patient_id
|
83
|
+
end)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
test do
|
88
|
+
title %{
|
89
|
+
The resources returned SHALL include all the data covered by the meaningful use common data elements as
|
90
|
+
defined in the US Core Implementation Guide
|
91
|
+
}
|
92
|
+
description %{
|
93
|
+
See FHIR R4 documentation for [patient-everything](https://hl7.org/fhir/R4/patient-operation-everything.html).
|
94
|
+
The US realm has now replaced meaningful use common data elements with [USCDI](https://www.healthit.gov/isa/united-states-core-data-interoperability-uscdi).
|
95
|
+
|
96
|
+
This test currently uses `meta.profile` to validate that a resource is compliant with its intended profile,
|
97
|
+
which includes checking for the profile's required elements.
|
98
|
+
|
99
|
+
It is the servers responsiblity to return all resources necessary to cover all USDCI elements known by
|
100
|
+
the server.
|
101
|
+
}
|
102
|
+
|
103
|
+
uses_request :pdex_patient_everything
|
104
|
+
|
105
|
+
run do
|
106
|
+
skip_if resource.resourceType != 'Bundle'
|
107
|
+
|
108
|
+
(0...resource.entry.length).each do |i|
|
109
|
+
assert_valid_resource(resource: resource.entry[i].resource,
|
110
|
+
profile_url: resource.entry[i].resource.meta.profile)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO: make attestations clearer, possibly change UI
|
116
|
+
# test do
|
117
|
+
# title %{
|
118
|
+
# Attestation: Server returns all resources necessary to cover all USDCI elements known by the server if
|
119
|
+
# operating in US Realm.
|
120
|
+
# }
|
121
|
+
# description 'See previous test for details.'
|
122
|
+
|
123
|
+
# input :workflow_everything_uscdi_attestation,
|
124
|
+
# title: %{
|
125
|
+
# Server's $everything operation returns all resources necessary to cover all USDCI
|
126
|
+
# elements known by the server if operating in US Realm
|
127
|
+
# },
|
128
|
+
# type: :radio,
|
129
|
+
# options: {
|
130
|
+
# list_options: [
|
131
|
+
# {
|
132
|
+
# label: 'Yes',
|
133
|
+
# value: 'yes'
|
134
|
+
# },
|
135
|
+
# {
|
136
|
+
# label: 'No',
|
137
|
+
# value: 'no'
|
138
|
+
# }
|
139
|
+
# ]
|
140
|
+
# },
|
141
|
+
# default: '',
|
142
|
+
# optional: true
|
143
|
+
|
144
|
+
# run do
|
145
|
+
# assert workflow_everything_uscdi_attestation == 'yes', 'Developer did not agree to attestation'
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
|
149
|
+
# test do
|
150
|
+
# title %{
|
151
|
+
# Attestation: The use of the Bulk FHIR specification for transmission of member $everything data SHALL
|
152
|
+
# honor jurisdictional and personal privacy restrictions that are relevant to a member’s health record.
|
153
|
+
# }
|
154
|
+
|
155
|
+
# input :workflow_everything_privacy_attestation,
|
156
|
+
# title: %Q(
|
157
|
+
# Server's $everything operation shall honor jurisdictional and personal privacy
|
158
|
+
# restriction that are relevant to a member's health record
|
159
|
+
# ),
|
160
|
+
# type: :radio,
|
161
|
+
# options: {
|
162
|
+
# list_options: [
|
163
|
+
# {
|
164
|
+
# label: 'Yes',
|
165
|
+
# value: 'yes'
|
166
|
+
# },
|
167
|
+
# {
|
168
|
+
# label: 'No',
|
169
|
+
# value: 'no'
|
170
|
+
# }
|
171
|
+
# ]
|
172
|
+
# },
|
173
|
+
# default: '',
|
174
|
+
# optional: true
|
175
|
+
|
176
|
+
# run do
|
177
|
+
# assert workflow_everything_privacy_attestation == 'yes', 'Developer did not agree to attestation'
|
178
|
+
# end
|
179
|
+
# end
|
180
|
+
|
181
|
+
# TODO: consider parameter tests
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bulk_data_test_kit/v2.0.0/bulk_data_patient_export_test_group'
|
4
|
+
# require 'bulk_data_test_kit/v1.0.1/patient/bulk_data_patient_export_group'
|
5
|
+
require_relative 'export_patient_group'
|
6
|
+
require_relative 'export_validation_group'
|
7
|
+
|
8
|
+
module DaVinciTestKit
|
9
|
+
module PDexPayerServer
|
10
|
+
class WorkflowExportTestGroup < Inferno::TestGroup
|
11
|
+
id :workflow_export
|
12
|
+
title 'Server can respond to FHIR Bulk $export requests on the matched patient'
|
13
|
+
short_title 'Bulk $export'
|
14
|
+
optional
|
15
|
+
description %{
|
16
|
+
# Background
|
17
|
+
|
18
|
+
The Patient $export operation for Payer-to-Payer exchange. See
|
19
|
+
[PDex Implementation Guide](https://hl7.org/fhir/us/davinci-pdex/STU2/payertopayerexchange.html#bulk-fhir-asynchronous-protocols),
|
20
|
+
[Bulk Data Patient Export Operation](https://hl7.org/fhir/uv/bulkdata/OperationDefinition-patient-export.html), and
|
21
|
+
[Bulk Data Export Request Flow](https://hl7.org/fhir/uv/bulkdata/export.html#bulk-data-export-operation-request-flow).
|
22
|
+
|
23
|
+
# Methodology
|
24
|
+
|
25
|
+
This test sequence leverages the [Bulk Data Test Kit](https://github.com/inferno-framework/bulk-data-test-kit)
|
26
|
+
for patient-level tests conforming to the Bulk Data Access Implementation Guide v2.0.0, and does not require more than 1 patient
|
27
|
+
to be returned by Patient-level export. The tests require a Bulk Data Autthorization Bearer Token.
|
28
|
+
}
|
29
|
+
|
30
|
+
config({
|
31
|
+
inputs: {
|
32
|
+
url: { name: :bulk_server_url },
|
33
|
+
# bulk_server_url: { name: :url },
|
34
|
+
bulk_export_url: { default: 'Patient/$export' },
|
35
|
+
bearer_token: { description: 'The authorization bearer token for $export access that is scoped to the same patient found by $member-match or entered as patient id. This is not necessarily the same OAuth token that allows access to the server\'s $member-match. If omitted $export tests will be skipped.' }
|
36
|
+
}
|
37
|
+
})
|
38
|
+
|
39
|
+
input :url # inherit properties from test suite
|
40
|
+
|
41
|
+
input :bearer_token # inherit properties from parent
|
42
|
+
|
43
|
+
input :patient_id,
|
44
|
+
title: 'Patient ID',
|
45
|
+
description: 'Manual Patient ID for testing Clinical Query, $everything, and $export without $member-match.',
|
46
|
+
optional: true
|
47
|
+
|
48
|
+
# Required by Bulk Data tests
|
49
|
+
fhir_client :bulk_server do
|
50
|
+
url :url
|
51
|
+
bearer_token :bearer_token
|
52
|
+
end
|
53
|
+
|
54
|
+
http_client :bulk_server do
|
55
|
+
url :url
|
56
|
+
headers {'Authorization' => "Bearer #{bearer_token}"}
|
57
|
+
end
|
58
|
+
|
59
|
+
group from: :pdex_patient_export_group
|
60
|
+
|
61
|
+
group from: :pdex_export_validation,
|
62
|
+
title: 'Patient Export Validation Tests',
|
63
|
+
optional: true
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'abstract_member_match_request_conformance_test'
|
3
|
+
require_relative 'abstract_member_match_request_local_references_test'
|
4
|
+
require_relative 'coverage_to_link_has_minimal_data_test'
|
5
|
+
require_relative 'coverage_to_link_must_support_test'
|
6
|
+
|
7
|
+
module DaVinciPDexTestKit
|
8
|
+
module PDexPayerServer
|
9
|
+
class WorkflowMemberMatch < Inferno::TestGroup
|
10
|
+
title 'Server can return a matching member in response to $member-match request'
|
11
|
+
short_title '$member-match'
|
12
|
+
id :workflow_member_match
|
13
|
+
description %{
|
14
|
+
# Background
|
15
|
+
|
16
|
+
Server must implement the [$member-match operation](https://hl7.org/fhir/us/davinci-hrex/OperationDefinition-member-match.html)
|
17
|
+
for payer-to-payer workflows as
|
18
|
+
[required by the PDex Implementation Guide](https://hl7.org/fhir/us/davinci-pdex/STU2/payertopayerexchange.html#member-match-with-consent).
|
19
|
+
|
20
|
+
# Testing Methodology
|
21
|
+
|
22
|
+
The developer must supply JSON FHIR input parameter conforming to the
|
23
|
+
[member match request profile](https://hl7.org/fhir/us/davinci-hrex/STU1/StructureDefinition-hrex-parameters-member-match-in.html). This
|
24
|
+
test sequence will:
|
25
|
+
|
26
|
+
1. Request the server's capability statement and verify that it asserts support for the $member-match operation
|
27
|
+
2. Validate user's member match request input
|
28
|
+
+ validate Parameters profile conformance
|
29
|
+
+ validate references as local references
|
30
|
+
+ optional: validate CoverageToLink parameter
|
31
|
+
+ optional: validate minimal data on CoverageToLink parameter
|
32
|
+
3. POST request to server and validate HTTP 200 response status
|
33
|
+
4. validate memeber match response conforms to profile
|
34
|
+
5. use member identifier from response to query for patient id
|
35
|
+
}
|
36
|
+
|
37
|
+
run_as_group
|
38
|
+
|
39
|
+
# TODO factorize cap statement tests
|
40
|
+
test do
|
41
|
+
id :member_match_in_capability_statement
|
42
|
+
title 'Server asserts Patient $member_match operation in capability statement.'
|
43
|
+
|
44
|
+
run do
|
45
|
+
fhir_get_capability_statement
|
46
|
+
|
47
|
+
assert_response_status(200)
|
48
|
+
assert_resource_type(:capability_statement)
|
49
|
+
|
50
|
+
assert(
|
51
|
+
resource.rest.one? do |rest_metadata|
|
52
|
+
rest_metadata.resource.select { |resource_metadata| resource_metadata.type == 'Patient' }.first
|
53
|
+
.operation.any? do |operation_metadata|
|
54
|
+
operation_metadata.name == 'member-match' && operation_metadata.definition == 'http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
input :member_match_request,
|
62
|
+
title: 'Member Match Request for one match',
|
63
|
+
description: "A JSON payload for server's $member-match endpoint that has **exactly one match**",
|
64
|
+
type: 'textarea'
|
65
|
+
|
66
|
+
test from: :abstract_member_match_request_conformance do
|
67
|
+
id :member_match_request_request_conformance
|
68
|
+
title '[USER INPUT VALIDATION] Member match request for exactly one match is valid'
|
69
|
+
description %{
|
70
|
+
This test validates the conformity of the user input to the
|
71
|
+
[HRex Member Match Request Profile](https://hl7.org/fhir/us/davinci-hrex/STU1/StructureDefinition-hrex-parameters-member-match-in.html),
|
72
|
+
ensuring subsequent tests can accurately simulate content. It also checks conformance to the [Parameters Resource](https://hl7.org/fhir/R4/parameters.html),
|
73
|
+
mandatory elements, and terminology.
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
test from: :abstract_member_match_request_local_references do
|
78
|
+
id :member_match_request_local_references
|
79
|
+
title '[USER INPUT VALIDATION] Member match request only uses local references'
|
80
|
+
end
|
81
|
+
|
82
|
+
test from: :coverage_to_link_must_support
|
83
|
+
test from: :coverage_to_link_has_minimal_data
|
84
|
+
|
85
|
+
test do
|
86
|
+
id :member_match_on_server
|
87
|
+
title 'Server handles $member-match operation successfully'
|
88
|
+
description 'Server receives request `POST [baseURL]/Patient/$member-match` and returns 200'
|
89
|
+
|
90
|
+
input :member_match_request
|
91
|
+
|
92
|
+
makes_request :member_match
|
93
|
+
|
94
|
+
run do
|
95
|
+
fhir_operation('/Patient/$member-match', body: FHIR.from_contents(member_match_request), name: :member_match)
|
96
|
+
assert_response_status(200)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
test do
|
101
|
+
id :member_match_response_conformance
|
102
|
+
title 'Server $member-match response conforms to profile'
|
103
|
+
description %{
|
104
|
+
The response body from the previous POST request to $member-match must be valid FHIR JSON conforming to
|
105
|
+
[HRex Member Match Response Profile](https://hl7.org/fhir/us/davinci-hrex/STU1/StructureDefinition-hrex-parameters-member-match-out.html).
|
106
|
+
}
|
107
|
+
|
108
|
+
output :member_identifier
|
109
|
+
output :member_identifier_system
|
110
|
+
|
111
|
+
uses_request :member_match
|
112
|
+
|
113
|
+
run do
|
114
|
+
assert response[:body], 'Server HTTP response is missing body'
|
115
|
+
assert_valid_json(response[:body])
|
116
|
+
assert_resource_type('Parameters')
|
117
|
+
|
118
|
+
# We should save the output before validating every detail of the payload
|
119
|
+
output member_identifier: resource.parameter.find{|p| p.name=='MemberIdentifier'}&.valueIdentifier&.value
|
120
|
+
output member_identifier_system: resource.parameter.find{|p| p.name=='MemberIdentifier'}&.valueIdentifier&.system
|
121
|
+
|
122
|
+
assert_valid_resource(profile_url: 'http://hl7.org/fhir/us/davinci-hrex/StructureDefinition/hrex-parameters-member-match-out')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
test do
|
127
|
+
id :member_match_identifier_to_id
|
128
|
+
title 'Server member identifier from $member-match yields logical Patient id'
|
129
|
+
description %Q{
|
130
|
+
The $member-match operation returns a Member Identifier, and subsequent clinical queries and operations
|
131
|
+
are only required to support logical Patient FHIR ID. Hence server must be able to provide a Patient
|
132
|
+
FHIR ID from Member Identifier.
|
133
|
+
|
134
|
+
Server receives request `GET [baseURL]/Patient?identifier=[member_identifier]`
|
135
|
+
and returns 200 and a Bundle with a single FHIR Patient. The `[member_identifier]` is from Member Match Response
|
136
|
+
`Parameters.parameter:MemberIdentifier.valueIdentifier.value`.
|
137
|
+
}
|
138
|
+
|
139
|
+
input :member_identifier
|
140
|
+
input :member_identifier_system, optional: true
|
141
|
+
output :patient_id
|
142
|
+
|
143
|
+
run do
|
144
|
+
skip_if !member_identifier, "No member identifier obtained from $member-match request"
|
145
|
+
|
146
|
+
# We only query by identifier.value, and preset information happens to return a value with a system within it
|
147
|
+
# which may be a bug.
|
148
|
+
# Other options are to query by system|value or type-of:
|
149
|
+
# But future PDex IG is intending to include logical FHIR id with MemberMatchResponse so this won't be necessary
|
150
|
+
fhir_search(FHIR::Patient, params: { 'identifier' => member_identifier })
|
151
|
+
|
152
|
+
assert response[:body], 'Server HTTP response is missing body'
|
153
|
+
assert_valid_json(response[:body])
|
154
|
+
assert_resource_type('Bundle')
|
155
|
+
|
156
|
+
assert resource.entry.find{ |entry| entry.resource&.resourceType == 'Patient' }, "Bundle has no Patient resource."
|
157
|
+
|
158
|
+
patient_id = resource.entry.reverse_each.find{ |entry| entry.resource&.resourceType == 'Patient' }&.resource&.id
|
159
|
+
assert patient_id, "Patient resource in Bundle has no logical resource id"
|
160
|
+
|
161
|
+
info "Multiple patients found, using the last patient id." if resource.entry.select{ |entry| entry.resource&.resourceType == 'Patient' }.length > 1
|
162
|
+
|
163
|
+
output :patient_id => patient_id
|
164
|
+
|
165
|
+
assert_valid_resource
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|