davinci_pdex_test_kit 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/davinci_pdex_test_kit/docs/payer_client_suite_description_v200.md +91 -0
  4. data/lib/davinci_pdex_test_kit/docs/payer_server_suite_description_v200.md +119 -0
  5. data/lib/davinci_pdex_test_kit/ext/inferno_core/record_response_route.rb +98 -0
  6. data/lib/davinci_pdex_test_kit/ext/inferno_core/request.rb +19 -0
  7. data/lib/davinci_pdex_test_kit/ext/inferno_core/runnable.rb +18 -0
  8. data/lib/davinci_pdex_test_kit/fhir_resource_navigation.rb +154 -0
  9. data/lib/davinci_pdex_test_kit/group_metadata.rb +109 -0
  10. data/lib/davinci_pdex_test_kit/metadata/mock_capability_statement.json +1052 -0
  11. data/lib/davinci_pdex_test_kit/metadata/mock_operation_outcome_resource.json +16 -0
  12. data/lib/davinci_pdex_test_kit/mock_server.rb +247 -0
  13. data/lib/davinci_pdex_test_kit/must_support_test.rb +252 -0
  14. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_submit_test.rb +24 -0
  15. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_validation_test.rb +23 -0
  16. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_submit_test.rb +26 -0
  17. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_validation_test.rb +32 -0
  18. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb +94 -0
  19. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/allergyintolerance_clinical_data_request_test.rb +23 -0
  20. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careplan_clinical_data_request_test.rb +23 -0
  21. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careteam_clinical_data_request_test.rb +23 -0
  22. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/condition_clinical_data_request_test.rb +23 -0
  23. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/device_clinical_data_request_test.rb +23 -0
  24. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/diagnosticreport_clinical_data_request_test.rb +23 -0
  25. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/documentreference_clinical_data_request_test.rb +23 -0
  26. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/encounter_clinical_data_request_test.rb +23 -0
  27. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/explanationofbenefit_clinical_data_request_test.rb +23 -0
  28. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/goal_clinical_data_request_test.rb +23 -0
  29. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/immunization_clinical_data_request_test.rb +23 -0
  30. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb +34 -0
  31. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb +28 -0
  32. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/location_clinical_data_request_test.rb +23 -0
  33. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationdispense_clinical_data_request_test.rb +23 -0
  34. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationrequest_clinical_data_request_test.rb +23 -0
  35. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/observation_clinical_data_request_test.rb +23 -0
  36. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/organization_clinical_data_request_test.rb +23 -0
  37. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/patient_clinical_data_request_test.rb +23 -0
  38. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitioner_clinical_data_request_test.rb +23 -0
  39. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitionerrole_clinical_data_request_test.rb +23 -0
  40. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/procedure_clinical_data_request_test.rb +23 -0
  41. data/lib/davinci_pdex_test_kit/pdex_payer_client/collection.rb +46 -0
  42. data/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb +152 -0
  43. data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_conformance_test.rb +33 -0
  44. data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_local_references_test.rb +35 -0
  45. data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_has_minimal_data_test.rb +52 -0
  46. data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_must_support_test.rb +28 -0
  47. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_id_search_test.rb +58 -0
  48. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_identifier_search_test.rb +58 -0
  49. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_must_support_test.rb +40 -0
  50. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_last_updated_search_test.rb +63 -0
  51. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_service_date_search_test.rb +63 -0
  52. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_type_search_test.rb +63 -0
  53. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_use_search_test.rb +68 -0
  54. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_provenance_revinclude_search_test.rb +52 -0
  55. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_read_test.rb +26 -0
  56. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_reference_resolution_test.rb +43 -0
  57. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_validation_test.rb +40 -0
  58. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit_group.rb +105 -0
  59. data/lib/davinci_pdex_test_kit/pdex_payer_server/export_patient_group.rb +103 -0
  60. data/lib/davinci_pdex_test_kit/pdex_payer_server/export_validation_group.rb +59 -0
  61. data/lib/davinci_pdex_test_kit/pdex_payer_server/multiple_member_matches_group.rb +66 -0
  62. data/lib/davinci_pdex_test_kit/pdex_payer_server/no_member_matches_group.rb +69 -0
  63. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_clinical_data.rb +66 -0
  64. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_everything.rb +184 -0
  65. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_export.rb +67 -0
  66. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_member_match.rb +171 -0
  67. data/lib/davinci_pdex_test_kit/pdex_payer_server_suite.rb +158 -0
  68. data/lib/davinci_pdex_test_kit/pdex_provider_client_suite.rb +36 -0
  69. data/lib/davinci_pdex_test_kit/tags.rb +9 -0
  70. data/lib/davinci_pdex_test_kit/urls.rb +67 -0
  71. data/lib/davinci_pdex_test_kit/user_input_response.rb +32 -0
  72. data/lib/davinci_pdex_test_kit/version.rb +5 -0
  73. data/lib/davinci_pdex_test_kit.rb +8 -0
  74. 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