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.
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