davinci_pas_test_kit 0.12.0 → 0.12.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 +4 -4
- data/lib/davinci_pas_test_kit/client_suite.rb +24 -0
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_approval_submit_test.rb +29 -1
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_denial_submit_test.rb +27 -4
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pended_pas_inquiry_request_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_inquire_request_bundle_validation_test.rb} +22 -20
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_denial_pas_response_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_inquire_response_bundle_validation_test.rb} +34 -21
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_submit_test.rb +124 -5
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pas_request_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_request_bundle_validation_test.rb} +22 -20
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/{pas_client_approval_submit_response_attest.rb → pas_client_response_attest.rb} +26 -9
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pended_pas_response_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_response_bundle_validation_test.rb} +44 -20
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_subscription_create_test.rb +49 -0
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_subscription_pas_conformance_test.rb +48 -0
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_approval_group.rb +21 -9
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_authentication_group.rb +2 -2
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_denial_group.rb +21 -22
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_pended_group.rb +97 -31
- data/lib/davinci_pas_test_kit/docs/client_suite_description_v201.md +213 -72
- data/lib/davinci_pas_test_kit/endpoints/claim_endpoint.rb +85 -134
- data/lib/davinci_pas_test_kit/endpoints/subscription_create_endpoint.rb +96 -0
- data/lib/davinci_pas_test_kit/endpoints/subscription_status_endpoint.rb +90 -0
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_request_bundle/metadata.yml +0 -2
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_request_bundle/server_pas_inquiry_request_bundle_validation_test.rb +3 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_response_bundle/server_pas_inquiry_response_bundle_validation_test.rb +2 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_request_bundle/metadata.yml +0 -2
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_request_bundle/server_pas_request_bundle_validation_test.rb +3 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_response_bundle/metadata.yml +0 -4
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_response_bundle/server_pas_response_bundle_validation_test.rb +2 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_server_denial_use_case_group.rb +1 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_server_pended_use_case_group.rb +6 -5
- data/lib/davinci_pas_test_kit/generated/v2.0.1/server_suite.rb +1 -0
- data/lib/davinci_pas_test_kit/generator/group_generator.rb +9 -8
- data/lib/davinci_pas_test_kit/generator/group_metadata_extractor.rb +7 -3
- data/lib/davinci_pas_test_kit/generator/templates/suite.rb.erb +1 -0
- data/lib/davinci_pas_test_kit/generator/validation_test_generator.rb +19 -56
- data/lib/davinci_pas_test_kit/generator/value_extractor.rb +4 -1
- data/lib/davinci_pas_test_kit/generator.rb +1 -1
- data/lib/davinci_pas_test_kit/jobs/send_pas_subscription_notification.rb +136 -0
- data/lib/davinci_pas_test_kit/jobs/send_subscription_handshake.rb +139 -0
- data/lib/davinci_pas_test_kit/pas_bundle_validation.rb +8 -7
- data/lib/davinci_pas_test_kit/response_generator.rb +397 -0
- data/lib/davinci_pas_test_kit/tags.rb +9 -0
- data/lib/davinci_pas_test_kit/urls.rb +8 -0
- data/lib/davinci_pas_test_kit/user_input_response.rb +11 -8
- data/lib/davinci_pas_test_kit/validation_test.rb +0 -1
- data/lib/davinci_pas_test_kit/version.rb +2 -2
- metadata +30 -14
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_denial_submit_response_attest.rb +0 -38
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_inquire_response_attest.rb +0 -39
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_inquire_test.rb +0 -35
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_submit_response_attest.rb +0 -39
- data/lib/davinci_pas_test_kit/generator/templates/validation_client.rb.erb +0 -50
@@ -16,27 +16,23 @@ Inferno will simulate a PAS server for the client under test to interact with. T
|
|
16
16
|
will be expected to initiate requests to the server and demonstrate its ability to react
|
17
17
|
to the returned responses. Over the course of these interactions,
|
18
18
|
Inferno will seek to observe conformant handling of PAS requirements, including
|
19
|
-
- The ability of the client to initiate and react to
|
20
|
-
- The approval of
|
21
|
-
- The denial of
|
22
|
-
- The pending of
|
19
|
+
- The ability of the client to initiate a prior authorization submission and react to
|
20
|
+
- The approval of the request
|
21
|
+
- The denial of the request
|
22
|
+
- The pending of the request and a subsequent notification that a final decision was made
|
23
23
|
- The ability of the client to provide data covering the full scope of required by PAS, including
|
24
24
|
- The ability to send prior auth requests and inquiries with all PAS profiles and all must support elements on
|
25
25
|
those profiles
|
26
26
|
- The ability to handle responses that contain all PAS profiles and all must support elements on those
|
27
27
|
profiles (not included in the current version of these tests)
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
and
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
response for Inferno to send back
|
37
|
-
- The current tests do not return to the client all PAS profiles and must support elements to confirm support.
|
38
|
-
|
39
|
-
For further details on limitations of these tests, see the *Testing Limitations* section below.
|
29
|
+
Inferno contains basic logic to generate approval, denial, and pended responses, along with a
|
30
|
+
notification that a final decision was made, as a part of the above workflows.
|
31
|
+
These responses are based on examples available in the PAS Implementation Guide
|
32
|
+
and are conformant, but may not meet the needs of actual implementations. Thus,
|
33
|
+
testers may provide Inferno with specific responses for Inferno to echo. If responses
|
34
|
+
are provided, Inferno will check them for conformance to ensure that they demonstrate
|
35
|
+
a fully conformant exchange.
|
40
36
|
|
41
37
|
All requests and responses will be checked for conformance to the PAS
|
42
38
|
IG requirements individually and used in aggregate to determine whether
|
@@ -47,67 +43,204 @@ validated with the Java validator using `tx.fhir.org` as the terminology server.
|
|
47
43
|
|
48
44
|
### Quick Start
|
49
45
|
|
50
|
-
For Inferno to simulate a server that
|
46
|
+
For Inferno to simulate a server that returns mocked conformant responses, it needs
|
51
47
|
only to know the bearer token that the client will send on requests, for which there are two options.
|
52
48
|
|
53
|
-
1. If you want to choose your own bearer token, then
|
54
|
-
1. Select the
|
49
|
+
1. If you want to choose your own bearer token, then:
|
50
|
+
1. Select the **2.** PAS Client Validation test from the list on the left.
|
55
51
|
2. Click the '*Run All Tests*' button on the right.
|
56
|
-
3. In the "
|
57
|
-
|
52
|
+
3. In the "Access Token" input field, enter the bearer token that will be sent by the client under test
|
53
|
+
in the Authorization HTTP header (format: `Bearer <provided value>`) for all requests to Inferno.
|
58
54
|
4. Click the '*Submit*' button at the bottom of the dialog.
|
59
|
-
2. If you want to use a
|
55
|
+
2. If you want to use a Client ID to obtain an access token, then:
|
60
56
|
1. Click the '*Run All Tests*' button on the right.
|
61
|
-
2. Provide the client's registered id "
|
57
|
+
2. Provide the client's registered id "Client ID" input field (NOTE: Inferno doesn't support the
|
62
58
|
registration API, so this must be obtained from another system or configured manually).
|
63
59
|
3. Click the '*Submit*' button at the bottom of the dialog.
|
64
|
-
4. Make a token request that includes the specified
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
4. Make a token request that includes the specified Client ID to the
|
61
|
+
`<inferno host>/custom/davinci_pas_client_suite_v201/mock_auth/token` endpoint to get
|
62
|
+
an access token from Inferno which the client will need to provide in the
|
63
|
+
Authorization HTTP header (format: `Bearer <provided value>`) for all subsequent
|
64
|
+
requests to Inferno for this test session. See the documentation in the
|
65
|
+
**1.** Demonstration Authorization test for details on the supported access token request.
|
66
|
+
|
67
|
+
In either case, the tests will continue from that point, requesting the tester to
|
69
68
|
direct the client to make certain requests to demonstrate PAS client capabilities.
|
70
69
|
|
71
70
|
Note: authentication options for these tests have not been finalized and are subject to change.
|
72
71
|
|
73
|
-
### Complete Setup
|
74
|
-
|
75
|
-
The *Quick Start* approach does not test pended or deny workflows. To test these, provide a
|
76
|
-
json-encoded FHIR bundle in the "Claim pended response JSON" and "Claim deny response JSON" input field after
|
77
|
-
clicking the '*Run All Tests*' button. These responses will be echoed back when a request
|
78
|
-
is made during the corresponding test.
|
79
|
-
|
80
|
-
Selecting the *Example PAS Server Responses* Preset in the dropdown in the upper left will fill in example
|
81
|
-
FHIR bundles for the pended and deny responses. It will also fill in a sample value for the Client ID,
|
82
|
-
which is only necessary for the *Demonstrate Authorization* test group, which can be skipped in favor of
|
83
|
-
manual bearer token input in subsequent tests as described above.
|
84
|
-
|
85
72
|
### Postman-based Demo
|
86
73
|
|
87
74
|
If you do not have a PAS client but would like to try the tests out, you can use
|
88
75
|
[this postman collection](https://github.com/inferno-framework/davinci-pas-test-kit/blob/main/config/PAS%20Test%20Kit%20Client%20Test%20Demo.postman_collection.json)
|
89
|
-
to make requests against Inferno
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
76
|
+
to make requests against Inferno and see the mocked responses provided by Inferno. To use, load
|
77
|
+
the collection into the [Postman app](https://www.postman.com/downloads/) and follow these steps:
|
78
|
+
|
79
|
+
1. Select the *PAS Client Test Suite Demo* Collection in postman and go to the "Variables" tab
|
80
|
+
(see the Overview tab for more details on what the variables control).
|
81
|
+
1. Note the "Current value" of the **client_id** variable for use in configuring Inferno. Update it
|
82
|
+
to another value to use instead, if desired.
|
83
|
+
1. Start a Da Vinci PAS Client Suite v2.0.1 session from the [PAS Test Kit page on
|
84
|
+
inferno.healthit.gov](https://inferno.healthit.gov/test-kits/davinci-pas/).
|
85
|
+
1. Click the *Run All Tests* button in the upper right hand corner of the suite.
|
86
|
+
1. In the **Client ID** input, enter the value from the **client_id** Postman variable and click the
|
87
|
+
*Submit* button.
|
88
|
+
1. When the "User Action" dialog appears, return to Postman and change to the Authorization tab. Scroll down
|
89
|
+
to find the *Get New Access Token* button at the bottom and click it.
|
90
|
+
1. When a success message appears, click the *Proceed* button and then the *Use Token* button.
|
91
|
+
1. Back in Inferno, a new "User Action" dialog will appear requesting a Subscription. When it does, return to Postman,
|
92
|
+
open the "Create Subscription Request" entry under the "Subscription Setup" folder in the collection,
|
93
|
+
and click the *Send* button.
|
94
|
+
1. Back in Inferno, an **Approval Workflow Test** "User Action" will appear. When it does, return to Postman,
|
95
|
+
open the "Prior Auth Request For Approval" entry under the "Approval Workflow Requests" folder in the
|
96
|
+
collection, and click the *Send* button.
|
97
|
+
1. Back in Inferno, an attestation "User Action" will appear asking you to confirm that the prior auth
|
98
|
+
request is listed as approved in the client app based on Inferno's response to the request. Search
|
99
|
+
in the response returned to Postman for the string "Certified in total" which indicates the prior
|
100
|
+
auth request was approved and click the link in Inferno indicating the attestation statement is true.
|
101
|
+
1. Next, a **Denial Workflow Test** "User Action" will appear. When it does, return to Postman,
|
102
|
+
open the "Prior Auth Request For Denial" entry under the "Denial Workflow Requests" folder in the
|
103
|
+
collection, and click the *Send* button.
|
104
|
+
1. Back in Inferno, an attestation "User Action" will appear asking you to confirm that the prior auth
|
105
|
+
request is listed as denied in the client app based on Inferno's response to the request. Search
|
106
|
+
in the response returned to Postman for the string "Not Certified" which indicates the prior
|
107
|
+
auth request was denied and click the link in Inferno indicating the attestation statement is true.
|
108
|
+
1. Next, a **Pended Workflow Test** "User Action" will appear. When it does, return to Postman,
|
109
|
+
open the "Prior Auth Request For Pended" entry under the "Pended Workflow Requests" folder in the
|
110
|
+
collection, and click the *Send* button.
|
111
|
+
1. Search in the response returned to Postman for the string "Pending" which indicates the prior
|
112
|
+
auth request was pended and a final decision will be made later. You'll use this information in
|
113
|
+
a later attestation.
|
114
|
+
1. Meanwhile, Inferno sent a notification indicating that a final decision was made (5-10 seconds
|
115
|
+
after the prior auth request). If interested in seeing the notification message, it can be found
|
116
|
+
on the [notifications
|
117
|
+
page](https://subscriptions.argo.run/subscriptions/notifications-received?store=r4) for the
|
118
|
+
[Argonaut Subscriptions Reference Implementation](https://subscriptions.argo.run/), which
|
119
|
+
hosts a notification endpoint that is used to receive Subscription notifications for this demo.
|
120
|
+
Note that when looking for recent notifications, **Received** timestamps are in UTC which is
|
121
|
+
5 hours ahead of Eastern Standard Time (4 hours ahead of Eastern Daylight Time).
|
122
|
+
1. Return to Postman, open the "Prior Auth Inquiry For Pended" entry under the "Pended Workflow Requests"
|
123
|
+
folder in the collection, and click the *Send* button.
|
124
|
+
1. Search in the response returned to Postman for the string "Certified in total" which indicates the prior
|
125
|
+
auth request was approved. You'll use this information in a later attestation.
|
126
|
+
1. Return to Inferno, scroll down in the "User Action" dialog and click the "click here to complete the test"
|
127
|
+
link to allow Inferno to evaluate the pended workflow.
|
128
|
+
1. Two attestations will appear, the first stating that the prior auth request was registered in the
|
129
|
+
client as pended and that it was subsequently finalized. You checked these above and can use the
|
130
|
+
true link for both.
|
131
|
+
1. Two additional "User Action" dialogs will appear requesting additional `$submit` and `$inquire`
|
132
|
+
requests to demonstrate must support elements. This demo does not have any additional requests
|
133
|
+
and does not attempt to demonstrate all must support elements, so click the link to indicate
|
134
|
+
you are done submitting requests for each. Note that requests submitted during the workflow section
|
135
|
+
will be evaluated and you can inspect the results under test **3.2** *Demonstrate Element Support*
|
136
|
+
to see both passing and failing tests.
|
137
|
+
1. Once Inferno finishes evaluating the requests, the test will complete allowing you to review the
|
138
|
+
results, including warning and error messages as well as requests associated with each test.
|
139
|
+
|
140
|
+
#### Optional Demo Modifications
|
141
|
+
|
142
|
+
This demo uses `id-only` notifications for Pended workflow. To see a demonstration of `full-resource`
|
143
|
+
notifications, replace the string `id-only` in the "Create Subscription Request" entry under the
|
144
|
+
"Subscription Setup" folder in the collection with the string `full-resource` (found in an extension
|
145
|
+
under the `_payload` element).
|
146
|
+
|
147
|
+
## Response and Notification Content
|
148
|
+
|
149
|
+
To assist in testers getting started with the PAS Client tests quickly, Inferno will generate
|
150
|
+
conformant mocked `$submit` and `$inquire` operation responses and Subscription notifications.
|
151
|
+
However, the simple mocked messages may not drive the workflows of real systems in a way that allows
|
152
|
+
them to demonstrate their implementation of the PAS specification. Thus, Inferno also allows each message
|
153
|
+
returned or initiated by Inferno to be specified by the tester. These messages must themselves be
|
154
|
+
conformant to PAS specification requirements and additional test requirements in order for a test run to
|
155
|
+
serve as a demonstration of a conformant implementation.
|
156
|
+
|
157
|
+
The rest of this section provides details on how Inferno determines the content to use in responses
|
158
|
+
and notifications.
|
159
|
+
|
160
|
+
### Inferno Modifications of Tester-provided Responses and Notifications
|
161
|
+
|
162
|
+
Requests provided by testers will be modified by Inferno to try and populate details that testers won't
|
163
|
+
know ahead of time. These modifications fall into two categories:
|
164
|
+
- **Timestamps**: creation timestamps, such as those on Bundles, ClaimResponses, and event notifications,
|
165
|
+
will be updated or populated by Inferno so that they are in sync with the time the message is sent.
|
166
|
+
- **Resource Ids**: some resource ids will not be known ahead of time and will be added or updated by Inferno
|
167
|
+
including
|
168
|
+
- *Claim Id*: if the tester provides a `$submit` or `$inquire` response with `ClaimResponse.request` populated,
|
169
|
+
then Inferno will update it with the fullUrl of the Claim provided in the request. This avoids the need for
|
170
|
+
testers to know the Claim Id ahead of time which may be difficult for some systems.
|
171
|
+
- *ClaimResponse Id*: if the tester provides a Notification but has Inferno generate the `$submit` response,
|
172
|
+
then Inferno will update the focus to use the ClaimResponse id that it generates.
|
173
|
+
|
174
|
+
If the tester provides an input that is malformed in some way such that Inferno cannot get the details
|
175
|
+
that Inferno needs to make the modifications, then the raw input will be used.
|
176
|
+
|
177
|
+
### Response and Notification Correspondence Requirements
|
178
|
+
|
179
|
+
Beyond the minor modifications described above, Inferno does not modify provided content to ensure that
|
180
|
+
they are consistent with each other or the time they are executed. For example, in the pended
|
181
|
+
workflow, it is up to the tester to ensure that if they provide responses for the `$submit` and `$inquire`
|
182
|
+
operations that they share whatever details, such as identifiers, needed to connect them together and drive
|
183
|
+
the workflow in their system. Timestamps not associated with messaging time such as when a prior authorization
|
184
|
+
response is valid are also not modified by Inferno. Unlike details that Inferno modifies as described above,
|
185
|
+
testers should have control over and/or knowledge of the necessary details and values to construct consistent
|
186
|
+
and working messages for Inferno to use.
|
187
|
+
|
188
|
+
### Tester-provided Response and Notification Inputs
|
189
|
+
|
190
|
+
The following test inputs control Inferno messaging behavior:
|
191
|
+
- **Claim approved response JSON**: If populated, this is used in the **2.2.1** "Demonstrate Approval Workflow" tests
|
192
|
+
to respond to `$submit` requests. The response needs to indicate to the system that the prior auth request has
|
193
|
+
been approved.
|
194
|
+
- **Claim denied response JSON**: If populated, this used in the **2.2.2** "Demonstrate Denial Workflow" tests
|
195
|
+
to respond to `$submit` requests. The response needs to indicate to the system that the prior auth request has
|
196
|
+
been denied.
|
197
|
+
- **Claim pended response JSON**: If populated, this used in the **2.2.3** "Demonstrate Pended Workflow" tests
|
198
|
+
to respond to `$submit` requests. The response needs to indicate to the system that the prior auth request has
|
199
|
+
been pended.
|
200
|
+
- **Claim updated notification JSON**: If populated, this used in the **2.2.3** "Demonstrate Pended Workflow" tests
|
201
|
+
as the event notification sent for the Subscription indicating that a decision has been finalized for the
|
202
|
+
pended prior auth request. The content of the notification needs to match the details of the Subscription
|
203
|
+
provided in the **2.1** "Subscription Setup" tests.
|
204
|
+
- **Inquire approved response JSON**: If populated, this used in the **2.2.3** "Demonstrate Pended Workflow"
|
205
|
+
tests to respond to `$inquire` requests. The response needs to indicate to the system that the
|
206
|
+
prior auth request has been approved.
|
207
|
+
|
208
|
+
### Generation Logic
|
209
|
+
|
210
|
+
When generating responses and notifications, Inferno uses the following logic. These conform to the
|
211
|
+
requirements of the PAS specification, but may not make sense in an actual workflow.
|
212
|
+
- **`$submit` and `$inquire` responses**: these responses are created mostly from the incoming request, specific details include:
|
213
|
+
- The Patient, insurer Organization, and requestor entity instances are pulled into the response
|
214
|
+
Bundle and referenced in the `patient`, `insurer`, and `requestor` elements respectively.
|
215
|
+
Note that get found by following references found in the submitted Claim instance. If relative
|
216
|
+
references are used in the Claim, the Claim entry `fullUrl` needs to be a absolute reference
|
217
|
+
and not a UUID, else the entries won't get pulled in correctly.
|
218
|
+
- In the ClaimResponse, the `identifier`, `type`, `status`, and `use` elements are pulled
|
219
|
+
in from the Claim in the request.
|
220
|
+
- The `Bundle.timestamp` and `ClaimResponse.created` timestamps are populated using the current time.
|
221
|
+
- The `ClaimResponse.outcome` is hardcoded to `complete`.
|
222
|
+
- For each `item` entry in the request Claim, a `ClaimResponse.item` entry is created with the
|
223
|
+
`itemSequence` value copied over, `itemPreAuthIssueDate` and `itemPreAuthPeriod` extensions
|
224
|
+
added using the current date and a month starting on the current date respectively,
|
225
|
+
and an adjudication entry with a `category` of `submitted` that contains the `reviewAction` extension with a
|
226
|
+
`reviewActionCode` that matches the current workflow: `A1` ("Certified in total") for approval,
|
227
|
+
`A3` ("Not Certified") for denial, and `A4` ("Pending") for pending.
|
228
|
+
- **Notification Bundle**: Inferno supports mocking both `id-only` and `full-resource` notifications.
|
229
|
+
The following details are relevant:
|
230
|
+
- Inferno pulls in details from the Subscription created for the test session to use in
|
231
|
+
creating the Notification, including the `topic` and the `subscription` reference.
|
232
|
+
- Inferno hardcodes the `status` as `active` and the `type` as `event-notification`.
|
233
|
+
- Inferno will always include a single `notification-event` entry with a `timestamp` of the current
|
234
|
+
time and with a `focus` that points to the ClaimResponse returned on the `$submit`.
|
235
|
+
If it cannot find the ClaimResponse reference from the `$submit` response returned
|
236
|
+
by Inferno, it will generate a random UUID (which isn't likely to work correctly when received).
|
237
|
+
- The subscription's `events-since-subscription-start` and the event's `event-number` will always
|
238
|
+
be 1 as Inferno is not able to track notifications across multiple sessions or runs.
|
239
|
+
- When generating a `full-resource` notification, Inferno will include `additional-context`
|
240
|
+
references for each entry in the `$submit` response Bundle other than the ClaimResponse
|
241
|
+
(which is already in the `focus`). Then it will include Notification Bundle entries for
|
242
|
+
each instance in the `$submit` response Bundle, including the ClaimResponse, with `reviewActionCode`
|
243
|
+
extensions updated to indicate approval using code `A1` ("Certified in total") for approval.
|
111
244
|
|
112
245
|
## Testing Limitations
|
113
246
|
|
@@ -136,26 +269,34 @@ top of the [IG home page](https://hl7.org/fhir/us/davinci-pas/STU2/index.html):
|
|
136
269
|
The implications of this reliance on proprietary information that is not publicly available means that this test
|
137
270
|
kit:
|
138
271
|
|
139
|
-
- Cannot verify the correct usage of X12-based terminology
|
272
|
+
- *Cannot verify the correct usage of X12-based terminology*: terminology requirements for all elements bound to X12
|
140
273
|
value sets will not be validated.
|
141
|
-
- Cannot verify the meaning of codes
|
274
|
+
- *Cannot verify the meaning of codes*: validation that a given response conveys something specific, e.g., approval
|
142
275
|
or pending, is not performed.
|
143
|
-
- Cannot verify matching semantics on inquiries
|
276
|
+
- *Cannot verify matching semantics on inquiries*: no checking of the identity of the ClaimResponse returned for an
|
144
277
|
inquiry, e.g., that it matches the input or the original request.
|
145
278
|
|
146
279
|
These limitations may be removed in future versions of these tests. In the meantime, testers should consider these
|
147
280
|
requirements to be verified through attestation and should not represent their systems to have passed these tests
|
148
281
|
if these requirements are not met.
|
149
282
|
|
150
|
-
###
|
283
|
+
### Subscription Details
|
284
|
+
|
285
|
+
Subscription details in the PAS 2.0.1 specification are relatively underspecified and the
|
286
|
+
[published 2.1.0 version of the
|
287
|
+
specification](https://hl7.org/fhir/us/davinci-pas/STU2.1/specification.html#subscription)
|
288
|
+
makes significant changes, including requiring `full-resource` and updating details such as
|
289
|
+
the filter criteria.
|
151
290
|
|
152
|
-
|
153
|
-
|
154
|
-
-
|
155
|
-
|
291
|
+
Based on the immaturity of the 2.0.1 requirements around Subscriptions, these tests implement and check for
|
292
|
+
the mechanics of Subscriptions and notifications, but do not look closely at the details. For example,
|
293
|
+
`id-only` and `full-resource` are supported and the filter criteria format is not checked. Future versions
|
294
|
+
of these tests may be more stringent.
|
156
295
|
|
157
|
-
|
158
|
-
|
296
|
+
Additionally, to test Subscriptions and pending workflows, a new Subscription must be created for each
|
297
|
+
test session, which may require testers to re-initialize previously-created Subscriptions. Future versions
|
298
|
+
of these tests may relax this requirement and feedback on whether this would reduce burden and how this
|
299
|
+
might look are welcome.
|
159
300
|
|
160
301
|
### Future Details
|
161
302
|
|
@@ -167,7 +308,7 @@ The PAS IG places additional requirements on clients that are not currently test
|
|
167
308
|
- US Core profile support for supporting information
|
168
309
|
- The ability to handle responses containing all PAS-defined profiles and must support elements
|
169
310
|
- Most details requiring manual review of the client system, e.g., the requirement that clinicians can update
|
170
|
-
details of the prior authorization request before submitting them
|
311
|
+
details of the prior authorization request before submitting them
|
171
312
|
|
172
313
|
These and any other requirements found in the PAS IG may be tested in future versions of these tests.
|
173
314
|
|
@@ -1,25 +1,54 @@
|
|
1
1
|
require_relative '../user_input_response'
|
2
|
+
require_relative '../response_generator'
|
3
|
+
require_relative '../urls'
|
4
|
+
require_relative '../jobs/send_pas_subscription_notification'
|
5
|
+
require 'subscriptions_test_kit'
|
2
6
|
|
3
7
|
module DaVinciPASTestKit
|
4
8
|
class ClaimEndpoint < Inferno::DSL::SuiteEndpoint
|
9
|
+
include SubscriptionsTestKit::SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
|
10
|
+
include ResponseGenerator
|
11
|
+
include URLs
|
12
|
+
|
13
|
+
# override the one from URLs
|
14
|
+
def suite_id
|
15
|
+
request.path.split('/custom/')[1].split('/')[0] # request.path = {base inferno path}/custom/{suite_id}/...
|
16
|
+
end
|
17
|
+
|
5
18
|
def test_run_identifier
|
6
19
|
request.headers['authorization']&.delete_prefix('Bearer ')
|
7
20
|
end
|
8
21
|
|
9
22
|
def tags
|
10
|
-
|
23
|
+
operation_tag = operation == 'submit' ? SUBMIT_TAG : INQUIRE_TAG
|
24
|
+
workflow_tag = WORKFLOW_TAG_MAP[workflow]
|
25
|
+
|
26
|
+
return [operation_tag, workflow_tag] if workflow_tag.present?
|
27
|
+
|
28
|
+
[operation_tag]
|
29
|
+
end
|
30
|
+
|
31
|
+
def workflow
|
32
|
+
case test.id
|
33
|
+
when /.*pended.*/
|
34
|
+
:pended
|
35
|
+
when /.*denial.*/
|
36
|
+
:denial
|
37
|
+
when /.*approval.*/
|
38
|
+
:approval
|
39
|
+
end
|
11
40
|
end
|
12
41
|
|
42
|
+
WORKFLOW_TAG_MAP = {
|
43
|
+
pended: PENDED_WORKFLOW_TAG,
|
44
|
+
denial: DENIAL_WORKFLOW_TAG,
|
45
|
+
approval: APPROVAL_WORKFLOW_TAG
|
46
|
+
}.freeze
|
47
|
+
|
13
48
|
def make_response
|
14
49
|
response.status = 200
|
15
50
|
response.format = :json
|
16
51
|
|
17
|
-
user_inputted_response = UserInputResponse.user_inputted_response(test, result)
|
18
|
-
if user_inputted_response.present?
|
19
|
-
response.body = user_inputted_response
|
20
|
-
return
|
21
|
-
end
|
22
|
-
|
23
52
|
req_bundle = FHIR.from_contents(request.body.string)
|
24
53
|
claim_entry = req_bundle&.entry&.find { |e| e&.resource&.resourceType == 'Claim' }
|
25
54
|
claim_full_url = claim_entry&.fullUrl
|
@@ -28,27 +57,26 @@ module DaVinciPASTestKit
|
|
28
57
|
return
|
29
58
|
end
|
30
59
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
response.body = res_bundle.to_json
|
60
|
+
user_inputted_response = UserInputResponse.user_inputted_response(test, operation, result)
|
61
|
+
if user_inputted_response.present?
|
62
|
+
generated_claim_response_uuid = nil
|
63
|
+
response_bundle_json = update_tester_provided_response(user_inputted_response, claim_full_url)
|
64
|
+
else
|
65
|
+
decision = # always use the workflow, except for pended when the inquire will get approved
|
66
|
+
if operation == 'inquire' && workflow == :pended
|
67
|
+
:approval
|
68
|
+
else
|
69
|
+
workflow
|
70
|
+
end
|
71
|
+
generated_claim_response_uuid = SecureRandom.uuid
|
72
|
+
response_bundle_json = mock_response_bundle(req_bundle, operation, decision, generated_claim_response_uuid)
|
73
|
+
end
|
74
|
+
|
75
|
+
response.body = response_bundle_json
|
76
|
+
|
77
|
+
return unless workflow == :pended && operation == 'submit'
|
78
|
+
|
79
|
+
start_notification_job(response_bundle_json, :approval, generated_claim_response_uuid)
|
52
80
|
end
|
53
81
|
|
54
82
|
def update_result
|
@@ -57,78 +85,6 @@ module DaVinciPASTestKit
|
|
57
85
|
|
58
86
|
private
|
59
87
|
|
60
|
-
# Note that references from the claim to other resources in the bundle need to be changed to absolute URLs
|
61
|
-
# if they are relative, because the ClaimResponse's fullUrl is a urn:uuid
|
62
|
-
#
|
63
|
-
# @private
|
64
|
-
def mock_claim_response(claim, bundle, root_url)
|
65
|
-
return FHIR::ClaimResponse.new(id: SecureRandom.uuid) if claim.blank?
|
66
|
-
|
67
|
-
now = Time.now.utc
|
68
|
-
|
69
|
-
FHIR::ClaimResponse.new(
|
70
|
-
id: SecureRandom.uuid,
|
71
|
-
meta: FHIR::Meta.new(profile: if operation == 'submit'
|
72
|
-
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claimresponse'
|
73
|
-
else
|
74
|
-
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claiminquiryresponse'
|
75
|
-
end),
|
76
|
-
identifier: claim.identifier,
|
77
|
-
type: claim.type,
|
78
|
-
status: claim.status,
|
79
|
-
use: claim.use,
|
80
|
-
patient: absolute_reference(claim.patient, bundle.entry, root_url),
|
81
|
-
created: now.iso8601,
|
82
|
-
insurer: absolute_reference(claim.insurer, bundle.entry, root_url),
|
83
|
-
requestor: absolute_reference(claim.provider, bundle.entry, root_url),
|
84
|
-
outcome: 'complete',
|
85
|
-
item: claim.item.map do |item|
|
86
|
-
FHIR::ClaimResponse::Item.new(
|
87
|
-
extension: [
|
88
|
-
FHIR::Extension.new(
|
89
|
-
url: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-itemPreAuthIssueDate',
|
90
|
-
valueDate: now.strftime('%Y-%m-%d')
|
91
|
-
),
|
92
|
-
FHIR::Extension.new(
|
93
|
-
url: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-itemPreAuthPeriod',
|
94
|
-
valuePeriod: FHIR::Period.new(start: now.strftime('%Y-%m-%d'),
|
95
|
-
end: (now + 1.month).strftime('%Y-%m-%d'))
|
96
|
-
)
|
97
|
-
],
|
98
|
-
itemSequence: item.sequence,
|
99
|
-
adjudication: [
|
100
|
-
FHIR::ClaimResponse::Item::Adjudication.new(
|
101
|
-
extension: [
|
102
|
-
FHIR::Extension.new(
|
103
|
-
url: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-reviewAction',
|
104
|
-
extension: [
|
105
|
-
FHIR::Extension.new(
|
106
|
-
url: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-reviewActionCode',
|
107
|
-
valueCodeableConcept: FHIR::CodeableConcept.new(
|
108
|
-
coding: [
|
109
|
-
FHIR::Coding.new(
|
110
|
-
system: 'https://codesystem.x12.org/005010/306',
|
111
|
-
code: 'A1',
|
112
|
-
display: 'Certified in total'
|
113
|
-
)
|
114
|
-
]
|
115
|
-
)
|
116
|
-
)
|
117
|
-
]
|
118
|
-
)
|
119
|
-
],
|
120
|
-
category: FHIR::CodeableConcept.new(
|
121
|
-
coding: [
|
122
|
-
FHIR::Coding.new(system: 'http://terminology.hl7.org/CodeSystem/adjudication', code: 'submitted')
|
123
|
-
]
|
124
|
-
)
|
125
|
-
)
|
126
|
-
]
|
127
|
-
)
|
128
|
-
end
|
129
|
-
)
|
130
|
-
end
|
131
|
-
|
132
88
|
def handle_missing_required_elements(claim_entry, response)
|
133
89
|
response.status = 400
|
134
90
|
details = if claim_entry.blank?
|
@@ -146,49 +102,44 @@ module DaVinciPASTestKit
|
|
146
102
|
request.url&.split('$')&.last
|
147
103
|
end
|
148
104
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
def base_url(url)
|
153
|
-
return unless url&.start_with?('http://', 'https://')
|
105
|
+
def start_notification_job(response_bundle_json, decision, generated_claim_response_uuid)
|
106
|
+
notification_bearer_token = client_access_token_input(result)
|
107
|
+
notification_contents = notification_json(response_bundle_json, decision, generated_claim_response_uuid)
|
154
108
|
|
155
|
-
|
156
|
-
|
109
|
+
Inferno::Jobs.perform(Jobs::SendPASSubscriptionNotification, test_run.id, test_run.test_session_id, result.id,
|
110
|
+
notification_bearer_token, notification_contents, test_run_identifier, suite_id)
|
157
111
|
end
|
158
112
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
attributes.each do |attr|
|
163
|
-
value = resource.send(attr.to_sym)
|
164
|
-
if value.is_a?(FHIR::Reference) && value.reference.present?
|
165
|
-
match = find_matching_entry(value.reference, entries, root_url)
|
166
|
-
if match.present? && matches.none?(match)
|
167
|
-
value.reference = match.fullUrl
|
168
|
-
matches.concat([match], referenced_entities(match.resource, entries, root_url))
|
169
|
-
end
|
170
|
-
elsif value.is_a?(Array) && value.all? { |elmt| elmt.is_a?(FHIR::Model) }
|
171
|
-
value.each { |val| matches.concat(referenced_entities(val, entries, root_url)) }
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
matches
|
176
|
-
end
|
113
|
+
def notification_json(response_bundle_json, decision, generated_claim_response_uuid)
|
114
|
+
user_inputted_notification_json =
|
115
|
+
JSON.parse(result.input_json).find { |i| i['name'] == 'notification_bundle' }['value']
|
177
116
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
117
|
+
if user_inputted_notification_json.present?
|
118
|
+
update_tester_provided_notification(user_inputted_notification_json, generated_claim_response_uuid)
|
119
|
+
else
|
120
|
+
generate_notification(response_bundle_json, decision)
|
121
|
+
end
|
182
122
|
end
|
183
123
|
|
184
|
-
def
|
185
|
-
|
124
|
+
def generate_notification(response_bundle_json, decision)
|
125
|
+
subscription = find_subscription(test_run.test_session_id, as_json: true)
|
126
|
+
subscription_reference = "#{fhir_subscription_url}/#{subscription['id']}"
|
127
|
+
subscription_topic = subscription['criteria']
|
186
128
|
|
187
|
-
|
129
|
+
if find_subscription_content_type(subscription) == 'full-resource'
|
130
|
+
mock_full_resource_notification_bundle(response_bundle_json, subscription_reference, subscription_topic,
|
131
|
+
decision)
|
132
|
+
else # assume id-only since empty not allowed - if asked for empty, other failures will occur
|
133
|
+
mock_id_only_notification_bundle(response_bundle_json, subscription_reference, subscription_topic)
|
134
|
+
end
|
188
135
|
end
|
189
136
|
|
190
|
-
def
|
191
|
-
|
137
|
+
def find_subscription_content_type(subscription)
|
138
|
+
content_ext = subscription['channel']['_payload']['extension']
|
139
|
+
.find do |ext|
|
140
|
+
ext['url'] == 'http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content'
|
141
|
+
end
|
142
|
+
content_ext['valueCode'] if content_ext.present?
|
192
143
|
end
|
193
144
|
end
|
194
145
|
end
|