davinci_pdex_test_kit 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|