subscriptions_test_kit 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/lib/inferno_requirements_tools/ext/inferno_core/runnable.rb +22 -0
- data/lib/inferno_requirements_tools/tasks/collect_requirements.rb +222 -0
- data/lib/inferno_requirements_tools/tasks/requirements_coverage.rb +264 -0
- data/lib/subscriptions_test_kit/common/notification_conformance_verification.rb +310 -0
- data/lib/subscriptions_test_kit/common/subscription_conformance_verification.rb +87 -0
- data/lib/subscriptions_test_kit/docs/samples/Subscription_empty.json +37 -0
- data/lib/subscriptions_test_kit/docs/samples/Subscription_full-resource.json +37 -0
- data/lib/subscriptions_test_kit/docs/samples/Subscription_id-only.json +38 -0
- data/lib/subscriptions_test_kit/docs/subscriptions_r5_backport_r4_client_suite_description.md +120 -0
- data/lib/subscriptions_test_kit/docs/subscriptions_r5_backport_r4_server_suite_description.md +170 -0
- data/lib/subscriptions_test_kit/endpoints/subscription_create_endpoint.rb +90 -0
- data/lib/subscriptions_test_kit/endpoints/subscription_read_endpoint.rb +32 -0
- data/lib/subscriptions_test_kit/endpoints/subscription_rest_hook_endpoint.rb +66 -0
- data/lib/subscriptions_test_kit/endpoints/subscription_status_endpoint.rb +70 -0
- data/lib/subscriptions_test_kit/igs/README.md +21 -0
- data/lib/subscriptions_test_kit/jobs/send_subscription_notifications.rb +130 -0
- data/lib/subscriptions_test_kit/requirements/generated/subscriptions-test-kit_requirements_coverage.csv +136 -0
- data/lib/subscriptions_test_kit/requirements/subscriptions-test-kit_out_of_scope_requirements.csv +23 -0
- data/lib/subscriptions_test_kit/requirements/subscriptions-test-kit_requirements.csv +145 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils.rb +140 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/fixtures/capability_statement.json +57 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_payload_verification_test.rb +50 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_verification_test.rb +25 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/processing_attestation_test.rb +38 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/subscription_verification_test.rb +28 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification_group.rb +20 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_test.rb +128 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/event_notification_verification_test.rb +40 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/handshake_notification_verification_test.rb +40 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification_group.rb +16 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow_group.rb +33 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client_suite.rb +66 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/creation_response_conformance_test.rb +51 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/notification_delivery_test.rb +56 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/subscription_conformance_test.rb +72 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_group.rb +24 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_verification/notification_conformance_test.rb +73 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_verification_group.rb +19 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/subscription_creation.rb +63 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/subscription_status_operation.rb +58 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement/cs_conformance_test.rb +49 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement/topic_discovery_test.rb +106 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement_group.rb +21 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/empty_content/empty_conformance_test.rb +63 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/empty_content_group.rb +58 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/full_resource_content/full_resource_conformance_test.rb +68 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/full_resource_content_group.rb +58 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/id_only_content/id_only_conformance_test.rb +66 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/id_only_content_group.rb +58 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification_group.rb +25 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat/handshake_conformance_test.rb +67 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat/heartbeat_conformance_test.rb +74 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat_group.rb +19 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/status_operation/status_invocation_test.rb +43 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/status_operation_group.rb +15 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/subscription_rejection/reject_subscriptions_test.rb +181 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/subscription_rejection_group.rb +21 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage_group.rb +26 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/workflow_group.rb +27 -0
- data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server_suite.rb +79 -0
- data/lib/subscriptions_test_kit/tags.rb +10 -0
- data/lib/subscriptions_test_kit/urls.rb +37 -0
- data/lib/subscriptions_test_kit/version.rb +5 -0
- data/lib/subscriptions_test_kit.rb +3 -0
- metadata +194 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
{
|
3
|
+
"resourceType" : "Subscription",
|
4
|
+
"meta" : {
|
5
|
+
"profile" : ["http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription"]
|
6
|
+
},
|
7
|
+
"status" : "requested",
|
8
|
+
"end" : "2020-12-31T12:00:00Z",
|
9
|
+
"reason" : "R4 Topic-Based Workflow Subscription for Patient Admission",
|
10
|
+
"criteria" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_client/topics/patient-admission",
|
11
|
+
"_criteria" : {
|
12
|
+
"extension" : [{
|
13
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-filter-criteria",
|
14
|
+
"valueString" : "Encounter?patient=Patient/123"
|
15
|
+
}]
|
16
|
+
},
|
17
|
+
"channel" : {
|
18
|
+
"extension" : [{
|
19
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout",
|
20
|
+
"valueUnsignedInt" : 60
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-max-count",
|
24
|
+
"valuePositiveInt" : 20
|
25
|
+
}],
|
26
|
+
"type" : "rest-hook",
|
27
|
+
"header": ["Authorization: Bearer SAMPLE_TOKEN"],
|
28
|
+
"endpoint" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_server/subscription/channel/notification_listener",
|
29
|
+
"payload" : "application/json",
|
30
|
+
"_payload" : {
|
31
|
+
"extension" : [{
|
32
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content",
|
33
|
+
"valueCode" : "empty"
|
34
|
+
}]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
{
|
3
|
+
"resourceType" : "Subscription",
|
4
|
+
"meta" : {
|
5
|
+
"profile" : ["http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription"]
|
6
|
+
},
|
7
|
+
"status" : "requested",
|
8
|
+
"end" : "2020-12-31T12:00:00Z",
|
9
|
+
"reason" : "R4 Topic-Based Workflow Subscription for Patient Admission",
|
10
|
+
"criteria" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_client/topics/patient-admission",
|
11
|
+
"_criteria" : {
|
12
|
+
"extension" : [{
|
13
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-filter-criteria",
|
14
|
+
"valueString" : "Encounter?patient=Patient/123"
|
15
|
+
}]
|
16
|
+
},
|
17
|
+
"channel" : {
|
18
|
+
"extension" : [{
|
19
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout",
|
20
|
+
"valueUnsignedInt" : 60
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-max-count",
|
24
|
+
"valuePositiveInt" : 20
|
25
|
+
}],
|
26
|
+
"type" : "rest-hook",
|
27
|
+
"header": ["Authorization: Bearer SAMPLE_TOKEN"],
|
28
|
+
"endpoint" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_server/subscription/channel/notification_listener",
|
29
|
+
"payload" : "application/json",
|
30
|
+
"_payload" : {
|
31
|
+
"extension" : [{
|
32
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content",
|
33
|
+
"valueCode" : "full-resource"
|
34
|
+
}]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
{
|
4
|
+
"resourceType" : "Subscription",
|
5
|
+
"meta" : {
|
6
|
+
"profile" : ["http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-subscription"]
|
7
|
+
},
|
8
|
+
"status" : "requested",
|
9
|
+
"end" : "2020-12-31T12:00:00Z",
|
10
|
+
"reason" : "R4 Topic-Based Workflow Subscription for Patient Admission",
|
11
|
+
"criteria" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_client/topics/patient-admission",
|
12
|
+
"_criteria" : {
|
13
|
+
"extension" : [{
|
14
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-filter-criteria",
|
15
|
+
"valueString" : "Encounter?patient=Patient/123"
|
16
|
+
}]
|
17
|
+
},
|
18
|
+
"channel" : {
|
19
|
+
"extension" : [{
|
20
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-timeout",
|
21
|
+
"valueUnsignedInt" : 60
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-max-count",
|
25
|
+
"valuePositiveInt" : 20
|
26
|
+
}],
|
27
|
+
"type" : "rest-hook",
|
28
|
+
"header": ["Authorization: Bearer SAMPLE_TOKEN"],
|
29
|
+
"endpoint" : "http://inferno.healthit.gov/suites/custom/subscriptions_r5_backport_r4_server/subscription/channel/notification_listener",
|
30
|
+
"payload" : "application/json",
|
31
|
+
"_payload" : {
|
32
|
+
"extension" : [{
|
33
|
+
"url" : "http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content",
|
34
|
+
"valueCode" : "id-only"
|
35
|
+
}]
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
The Subscriptions R5 Backport IG v1.1.0 FHIR R4 Client Test Suite
|
2
|
+
verifies the conformance of
|
3
|
+
client systems to the STU 1.1.0 version of the HL7® FHIR®
|
4
|
+
[Subscriptions R5 Backport IG](https://hl7.org/fhir/uv/subscriptions-backport/STU1.1/).
|
5
|
+
|
6
|
+
## Scope
|
7
|
+
|
8
|
+
These tests are a **DRAFT** intended to allow implementers to perform
|
9
|
+
preliminary checks of their systems against Subscriptions capabilities backported
|
10
|
+
from FHIR R5 to R4 and [provide feedback](https://github.com/inferno-framework/subscriptions-test-kit/issues)
|
11
|
+
on the tests. Future versions of these tests may verify other
|
12
|
+
requirements and may change the test verification logic.
|
13
|
+
|
14
|
+
## Test Methodology
|
15
|
+
|
16
|
+
For these tests Inferno simulates a Subscriptions server application. Inferno will wait for
|
17
|
+
the client under test to request a Subscription and then will send notifications
|
18
|
+
back to the client based on that Subscription using additional information provided by the tester.
|
19
|
+
Using the interactions between Inferno and the client under test, Inferno will verify that
|
20
|
+
the client meets the obligations from the implementation guide both on individual interactions
|
21
|
+
and in aggregate.
|
22
|
+
|
23
|
+
Inferno does not implement a real data repository that recognizes resource-level changes
|
24
|
+
and so relies on the tester to provide an event notification that Inferno will send back
|
25
|
+
to the client under test. Inferno will manipulate this provided notification to generate other
|
26
|
+
notifications, such as handshakes and `$status` operation responses, to send
|
27
|
+
and respond with as directed by the IG. Inferno will verify that the provided event
|
28
|
+
notification is conformant and that it matches the requirements for the Subscription
|
29
|
+
sent by the client under test.
|
30
|
+
|
31
|
+
The content of all Subscription interactions sent by the client,
|
32
|
+
as well as tester-provided notifications, will be checked
|
33
|
+
for conformance to the Subscriptions IG requirements individually and used in
|
34
|
+
aggregate to determine whether required features and functionality are present.
|
35
|
+
HL7® FHIR® resources are checked for conformance using the Java validator with
|
36
|
+
`tx.fhir.org` as the terminology server.
|
37
|
+
|
38
|
+
## Running the Tests
|
39
|
+
|
40
|
+
### Quick Start
|
41
|
+
|
42
|
+
The following inputs must be provided by the tester at a minimum to execute
|
43
|
+
any tests in this suite:
|
44
|
+
1. *Access Token*: A `Bearer` token that the client under test will send in the
|
45
|
+
`Authorization` header of HTTP requests made against Inferno. Inferno uses the
|
46
|
+
value to identify incoming requests that belong to the testing session.
|
47
|
+
1. *Notification Bundle*: an `event-notification` conforming to the [R4 Topic-Based
|
48
|
+
Subscription Notification Bundle profile](https://hl7.org/fhir/uv/subscriptions-backport/STU1.1/StructureDefinition-backport-subscription-notification-r4.html).
|
49
|
+
Inferno uses this as the basis for responses and notifications that it will send
|
50
|
+
to the client.
|
51
|
+
|
52
|
+
All other details needed to interact with the client under test, such as where to send notifications,
|
53
|
+
will be determined from the subscription that the client will create. Once the testing start,
|
54
|
+
Inferno will wait for a Subscription creation request and then send the relevant handshakes
|
55
|
+
and notification requests to the client and verify the interaction.
|
56
|
+
|
57
|
+
Additional inputs described in the *Additional Configuration Details* section below can enable
|
58
|
+
verification of additional content types and some Subscription creation error scenarios.
|
59
|
+
|
60
|
+
### Sample Execution
|
61
|
+
|
62
|
+
To try out these tests without a Subscriptions client implementation or performing steps to
|
63
|
+
create a Subscription, run them against the Subscriptions server test suite included
|
64
|
+
in this test kit. Related presets contain example Subscriptions and event notifications that
|
65
|
+
the test kits can use to interact. The server test suite can simulate
|
66
|
+
a subscriptions client creating a `rest-hook` Subscription responding to appropriate handshakes
|
67
|
+
and notifications.
|
68
|
+
|
69
|
+
To run the client tests against the server tests:
|
70
|
+
1. Start an Inferno session of the Subscriptions Client test suite.
|
71
|
+
1. Select one of the *Inferno Subscription R4 Client [content type] Preset* from the Preset dropdown in the
|
72
|
+
upper left.
|
73
|
+
1. Click the "Run All Tests" button in the upper right and click the "Submit" button in the dialog
|
74
|
+
that appears. The simulated server will then be waiting for an interaction.
|
75
|
+
1. Start an Inferno session of the Subscriptions Server test suite.
|
76
|
+
1. Select the *[content type] Notifications Against The Subscriptions Client Suite* corresponding
|
77
|
+
to the content type selected in the client test from the Preset dropdown in the upper left.
|
78
|
+
1. Click the "Run All Tests" button in the upper right and click the "Submit" button in the
|
79
|
+
dialog that appears. The server tests make a Subscription request will and indicate that
|
80
|
+
it is waiting for notifications to be made.
|
81
|
+
1. Wait 10-15 seconds while the client session makes requests,
|
82
|
+
then click the "Click here" link in the wait dialog to evaluate the notifications.
|
83
|
+
1. Back in the client session, click the "click here to complete the test" link
|
84
|
+
and respond to the attestation based on the results in the server session and review
|
85
|
+
the results.
|
86
|
+
|
87
|
+
NOTE: Inferno uses the `Bearer` token sent in the `Authorization` HTTP header
|
88
|
+
to associate requests with sessions. If multiple concurrent sessions are configured
|
89
|
+
to use the same token, they may interfere with each other. To prevent concurrent executors
|
90
|
+
of these sample executions from disrupting your session it
|
91
|
+
is recommended, but not required, to change all instances of `SAMPLE_TOKEN` in the
|
92
|
+
Inferno inputs for both client server suites (including within the Subscription instance body),
|
93
|
+
to the same unique value so that the session will not interact with other concurrent users.
|
94
|
+
|
95
|
+
## Additional Configuration Details
|
96
|
+
|
97
|
+
The details provided here supplement the documentation of individual fields in the input dialog
|
98
|
+
that appears when initiating a test run.
|
99
|
+
|
100
|
+
### Client Notification Access Token
|
101
|
+
|
102
|
+
If the client under test needs Inferno to send a specific `Bearer` token in the HTTP `Authorization` header
|
103
|
+
when sending notifications and it cannot be specified in the `channel.header` element of the Subscription
|
104
|
+
instance that the client sends to Inferno, then a value can be provided here.
|
105
|
+
|
106
|
+
## Current Limitations
|
107
|
+
|
108
|
+
This test kit is still in draft form and does not test all of the requirements and features
|
109
|
+
described in the Subscriptions IG. You can find information on the requirements
|
110
|
+
that the test kit covers and does not cover in the [Requirements
|
111
|
+
Coverage](https://github.com/inferno-framework/subscriptions-test-kit/blob/main/lib/subscriptions_test_kit/requirements/generated/subscriptions-test-kit_requirements_coverage.csv)
|
112
|
+
CSV document.
|
113
|
+
|
114
|
+
Specific limitations to highlight include
|
115
|
+
- Inferno supports only the `rest-hook` channel type. Support for other channels may be added in the future.
|
116
|
+
If there is a channel type that you would like to see verified, please
|
117
|
+
[provide feedback](https://github.com/inferno-framework/subscriptions-test-kit/issues) to that effect.
|
118
|
+
- Inferno does not test delivery error handling and recovery scenarios, including
|
119
|
+
the optional `$events` API and event numbering details.
|
120
|
+
- Inferno does not support sending heartbeat notifications.
|
@@ -0,0 +1,170 @@
|
|
1
|
+
The Subscriptions R5 Backport IG v1.1.0 FHIR R4 Server Test Suite
|
2
|
+
verifies the conformance of
|
3
|
+
server systems to the STU 1.1.0 version of the HL7® FHIR®
|
4
|
+
[Subscriptions R5 Backport IG](https://hl7.org/fhir/uv/subscriptions-backport/STU1.1/).
|
5
|
+
|
6
|
+
## Scope
|
7
|
+
|
8
|
+
These tests are a **DRAFT** intended to allow implementers to perform
|
9
|
+
preliminary checks of their systems against Subscriptions capabilities backported
|
10
|
+
from FHIR R5 to R4 and [provide feedback](https://github.com/inferno-framework/subscriptions-test-kit/issues)
|
11
|
+
on the tests. Future versions of these tests may verify other
|
12
|
+
requirements and may change the test verification logic.
|
13
|
+
|
14
|
+
## Test Methodology
|
15
|
+
|
16
|
+
For these tests Inferno simulates a Subscriptions client application. Inferno will subscribe
|
17
|
+
to certain updates on the server under test and wait for one or more notifications
|
18
|
+
indicating changes meeting those criteria. Using the interactions between Inferno and
|
19
|
+
the server under test, Inferno will verify that the server under test meets
|
20
|
+
the obligations from the implementation guide both on individual interactions
|
21
|
+
and in aggregate.
|
22
|
+
|
23
|
+
Over the course of a test session, Inferno may request multiple Subscriptions to verify
|
24
|
+
different features of the IG, e.g., different notification content types. Because the
|
25
|
+
choice of what topics and specific features to implement is left to individual servers,
|
26
|
+
Inferno does not dictate specific Subscription features or topics. Instead, the
|
27
|
+
tester will provide Subsription instances that Inferno will submit, modified lightly if needed,
|
28
|
+
e.g., to point to Inferno's notification endpoint, to the server
|
29
|
+
as a part of the tests. In order for the tests to pass, the provided Subscription instances
|
30
|
+
must themselves be conformant and consistent with the notifications subsequently sent,
|
31
|
+
but no other requirements are placed on them,
|
32
|
+
except those related to Inferno limitations (see the Current Limitations section below).
|
33
|
+
|
34
|
+
The content of all interactions sent by the server (responses and notifications),
|
35
|
+
as well as tester-provided requests, will be checked
|
36
|
+
for conformance to the Subscriptions IG requirements individually and used in
|
37
|
+
aggregate to determine whether required features and functionality are present.
|
38
|
+
HL7® FHIR® resources are checked for conformance using the Java validator with
|
39
|
+
`tx.fhir.org` as the terminology server.
|
40
|
+
|
41
|
+
## Running the Tests
|
42
|
+
|
43
|
+
### Quick Start
|
44
|
+
|
45
|
+
The following inputs must be provided by the tester at a minimum to execute
|
46
|
+
any tests in this suite:
|
47
|
+
1. *FHIR Server Base URL*: The server's FHIR base URL. Inferno will use this when
|
48
|
+
performing Subscription creation requests and other interactions against the server
|
49
|
+
under test.
|
50
|
+
1. *OAuth Credentials*: Auth credentials for Inferno to use when making requests against the
|
51
|
+
server under test. Specifically, Inferno will provide the *Access Token* input as a `Bearer`
|
52
|
+
token in the `Authorization` header of HTTP requests. This input is not required if the server
|
53
|
+
does not require authorization.
|
54
|
+
1. *Notification Access Token*: An access token that the server will send
|
55
|
+
as a `Bearer` token in the `Authorization` header of HTTP notifications requests sent to Inferno.
|
56
|
+
Inferno uses the value to identify incoming requests that belong to the testing session.
|
57
|
+
1. *Workflow Subscription Resource*: A Subscription instance for Inferno to create on the server.
|
58
|
+
Inferno will adjust it to be use the `rest-hook` channel and point to its notification endpoint.
|
59
|
+
It must be conformant to the [R4/B Topic-Based Subscription profile](https://hl7.org/fhir/uv/subscriptions-backport/STU1.1/StructureDefinition-backport-subscription.html).
|
60
|
+
|
61
|
+
Using these inputs, Inferno will perform a Subscription interaction loop with the server under test:
|
62
|
+
creating a Subscription, waiting for notifications, and then verifying the interactions. The tester will
|
63
|
+
be responsible for monitoring their system for the new Subscription from Inferno and performing
|
64
|
+
an action in their system that triggers an event notification to be sent to Inferno based on
|
65
|
+
that Subscription.
|
66
|
+
|
67
|
+
Additional inputs described in the *Additional Configuration Details* section below can enable
|
68
|
+
verification of additional content types and some Subscription creation error scenarios.
|
69
|
+
|
70
|
+
### Sample Execution
|
71
|
+
|
72
|
+
To try out these tests without a Subscriptions server implementation or performing steps to
|
73
|
+
generate an event notification, run them against the Subscriptions client test suite included
|
74
|
+
in this test kit. Related presets contain example Subscriptions and event notifications that
|
75
|
+
the test kits can use to interact. The client test suite can run a loop to simulate
|
76
|
+
a subscriptions server using the `rest-hook` channel type including responding to a
|
77
|
+
Subscription creation request and sending appropriate handshakes and notifications.
|
78
|
+
|
79
|
+
To run the server tests against the client tests:
|
80
|
+
1. Start an Inferno session of the Subscriptions Client test suite.
|
81
|
+
1. Select one of the *Inferno Subscription R4 Client [content type] Preset* from the Preset dropdown in the
|
82
|
+
upper left.
|
83
|
+
1. Click the "Run All Tests" button in the upper right and click the "Submit" button in the dialog
|
84
|
+
that appears. The simulated server will then be waiting for an interaction.
|
85
|
+
1. Start an Inferno session of the Subscriptions Server test suite.
|
86
|
+
1. Select the *[content type] Notifications Against The Subscriptions Client Suite* corresponding
|
87
|
+
to the content type selected in the client test from the Preset dropdown in the upper left.
|
88
|
+
1. Click the "Run All Tests" button in the upper right and click the "Submit" button in the
|
89
|
+
dialog that appears. The server tests make a Subscription request will and indicate that
|
90
|
+
it is waiting for notifications to be made.
|
91
|
+
1. Wait 10-15 seconds while the client session makes requests,
|
92
|
+
then click the "Click here" link in the wait dialog to evaluate the notifications.
|
93
|
+
1. Back in the client session, click the "click here to complete the test" link
|
94
|
+
and respond to the attestation
|
95
|
+
1. Meanwhile, the server tests will have completed and will be ready for review.
|
96
|
+
|
97
|
+
NOTE: Inferno uses the `Bearer` token sent in the `Authorization` HTTP header
|
98
|
+
to associate requests with sessions. If multiple concurrent sessions are configured
|
99
|
+
to use the same token, they may interfere with each other. To prevent concurrent executors
|
100
|
+
of these sample executions from disrupting your session it
|
101
|
+
is recommended, but not required, to change all instances of `SAMPLE_TOKEN` in the
|
102
|
+
Inferno inputs for both client server suites (including within the Subscription instance body),
|
103
|
+
to the same unique value so that the session will not interact with other concurrent users.
|
104
|
+
|
105
|
+
## Additional Configuration Details
|
106
|
+
|
107
|
+
The details provided here supplement the documentation of individual fields in the input dialog
|
108
|
+
that appears when initiating a test run.
|
109
|
+
|
110
|
+
### Subscriptions for other content types
|
111
|
+
|
112
|
+
To demonstrate the ability of the server under test to serve notifications of multiple different
|
113
|
+
content types, additional Subscription instances can be provided in one or more of the
|
114
|
+
following inputs:
|
115
|
+
- *Empty Notification Subscription Resource*
|
116
|
+
- *Id-Only Notification Subscription Resource*
|
117
|
+
- *Full-Resource Notification Subscription Resource*
|
118
|
+
|
119
|
+
When populated, Inferno will perform an interation loop with the server as a part of the
|
120
|
+
*Notification Verification* test for the corresponding content type, including requesting that the
|
121
|
+
system under test create a Subscription for Inferno, waiting for notifications, and then
|
122
|
+
continuing with verification.
|
123
|
+
|
124
|
+
Additionally, if previously executed during this testing session, Inferno will include
|
125
|
+
the Subscription interaction performed as a part of the *Demonstrate the Subscription
|
126
|
+
Workflow* group and verify is correctness as a part of the test for the content type
|
127
|
+
indicated in that Subscription. Thus, no additional Subscription needs to be provided
|
128
|
+
for the content type used in the *Workflow Subscription Resource* input.
|
129
|
+
|
130
|
+
Examples of Subscriptions that can be used in each of these inputs can be found within the
|
131
|
+
test kit source code:
|
132
|
+
- [`empty` Notification example](https://github.com/inferno-framework/subscriptions-test-kit/blob/main/lib/subscriptions_test_kit/docs/samples/Subscription_empty.json)
|
133
|
+
- [`id-only` Notification example](https://github.com/inferno-framework/subscriptions-test-kit/blob/main/lib/subscriptions_test_kit/docs/samples/Subscription_id-only.json)
|
134
|
+
- [`full-resource` Notification example](https://github.com/inferno-framework/subscriptions-test-kit/blob/main/lib/subscriptions_test_kit/docs/samples/Subscription_full-resource.json)
|
135
|
+
|
136
|
+
### Unsupported Subscription creation element values
|
137
|
+
|
138
|
+
The Subscriptions Backport IG indicates that when accepting new Subscription creation requests
|
139
|
+
servers [*SHOULD* check the validity of serveral
|
140
|
+
elements](https://hl7.org/fhir/uv/subscriptions-backport/STU1.1/components.html#accepting-subscription-requests).
|
141
|
+
Since which values are unsupported are specific to individual implementations, testers that wish
|
142
|
+
to verify that the system under test meets these requirements can provide specific values
|
143
|
+
in the following input fields:
|
144
|
+
|
145
|
+
- *Unsupported Subscription Topic*
|
146
|
+
- *Unsupported Subscription Filter*
|
147
|
+
- *Unsupported Subscription Channel Type*
|
148
|
+
- *Unsupported Subscription Channel Endpoint*
|
149
|
+
- *Unsupported Subscription Payload Type*
|
150
|
+
- *Unsupported Subscription Channel and Payload Combination*
|
151
|
+
|
152
|
+
In each case, Inferno will take the Subscription provided in the *Workflow Subscription Resource*
|
153
|
+
input, modify the corresponding element with the provided value, and expect that either the server
|
154
|
+
1. Rejects the Subscription, or
|
155
|
+
2. Changes the unsupported element value to something else that is supported
|
156
|
+
|
157
|
+
## Current Limitations
|
158
|
+
|
159
|
+
This test kit is still in draft form and does not test all of the requirements and features
|
160
|
+
described in the Subscriptions IG. You can find information on the requirements
|
161
|
+
that the test kit covers and does not cover in the [Requirements
|
162
|
+
Coverage](https://github.com/inferno-framework/subscriptions-test-kit/blob/main/lib/subscriptions_test_kit/requirements/generated/subscriptions-test-kit_requirements_coverage.csv)
|
163
|
+
CSV document.
|
164
|
+
|
165
|
+
Specific limitations to highlight include
|
166
|
+
- Inferno supports only the `rest-hook` channel type. Support for other channels may be added in the future.
|
167
|
+
If there is a channel type that you would like to see verified, please
|
168
|
+
[provide feedback](https://github.com/inferno-framework/subscriptions-test-kit/issues) to that effect.
|
169
|
+
- Inferno does not test delivery error handling and recovery scenarios, including
|
170
|
+
the optional `$events` API and event numbering details.
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
require_relative '../jobs/send_subscription_notifications'
|
3
|
+
require_relative '../suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils'
|
4
|
+
|
5
|
+
module SubscriptionsTestKit
|
6
|
+
class SubscriptionCreateEndpoint < Inferno::DSL::SuiteEndpoint
|
7
|
+
include SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
|
8
|
+
|
9
|
+
def test_run_identifier
|
10
|
+
request.headers['authorization']&.delete_prefix('Bearer ')
|
11
|
+
end
|
12
|
+
|
13
|
+
def make_response
|
14
|
+
response.format = :json
|
15
|
+
response.status = 400
|
16
|
+
|
17
|
+
begin
|
18
|
+
subscription = FHIR.from_contents(request.body.string)
|
19
|
+
rescue StandardError
|
20
|
+
response.body = operation_outcome('error', 'invalid', 'No recognized R4 Subscription in request body').to_json
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
verification_outcome = verify_subscription(subscription)
|
25
|
+
if verification_outcome.present?
|
26
|
+
response.body = verification_outcome.to_json
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
# Deny subscription if one already created
|
31
|
+
requests = requests_repo.tagged_requests(test_run.test_session_id, tags)
|
32
|
+
existing_subscription_request = requests.find { |r| r.status == 201 }
|
33
|
+
if existing_subscription_request.present?
|
34
|
+
subscription_hash = JSON.parse(existing_subscription_request.response_body)
|
35
|
+
error_text = 'Inferno only supports one subscription per test run. Subscription already created with '\
|
36
|
+
"ID #{subscription_hash['id']}"
|
37
|
+
response.body = operation_outcome('error', 'business-rule', error_text).to_json
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
# Form response
|
42
|
+
notification_json = notification_bundle_input(result)
|
43
|
+
subscription_id = SecureRandom.uuid
|
44
|
+
# We have to manipulate the raw hash so that we don't lose the _payload primitive extension
|
45
|
+
subscription_hash = JSON.parse(request.body.string).merge('id' => subscription_id, 'status' => 'requested')
|
46
|
+
subscription_hash['channel']['payload'] = actual_mime_type(subscription)
|
47
|
+
response.status = 201
|
48
|
+
response.body = subscription_hash.to_json
|
49
|
+
|
50
|
+
# Kick off notification job
|
51
|
+
subscription_url = "#{request.url}/#{subscription_id}"
|
52
|
+
client_endpoint = subscription.channel.endpoint
|
53
|
+
bearer_token = client_access_token_input(result)
|
54
|
+
test_suite_base_url = request.url.chomp('/').chomp(FHIR_SUBSCRIPTION_PATH)
|
55
|
+
Inferno::Jobs.perform(Jobs::SendSubscriptionNotifications, test_run.id, test_run.test_session_id, result.id,
|
56
|
+
subscription_id, subscription_url, client_endpoint, bearer_token, notification_json,
|
57
|
+
test_run_identifier, test_suite_base_url)
|
58
|
+
end
|
59
|
+
|
60
|
+
def tags
|
61
|
+
[SUBSCRIPTION_CREATE_TAG]
|
62
|
+
end
|
63
|
+
|
64
|
+
def verify_subscription(subscription)
|
65
|
+
unless subscription.is_a? FHIR::Subscription
|
66
|
+
return operation_outcome('error', 'invalid', 'No recognized R4 Subscription in request body')
|
67
|
+
end
|
68
|
+
|
69
|
+
unless subscription.channel&.type == 'rest-hook'
|
70
|
+
return operation_outcome('error', 'business-rule', 'channel.type must be rest-hook')
|
71
|
+
end
|
72
|
+
|
73
|
+
unless valid_url?(subscription.channel&.endpoint)
|
74
|
+
return operation_outcome('error', 'value', 'channel.endpoint is not recognized as a conformant URL')
|
75
|
+
end
|
76
|
+
|
77
|
+
heartbeat_period = subscription.channel&.extension&.find do |e|
|
78
|
+
e.url == 'http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period'
|
79
|
+
end
|
80
|
+
operation_outcome('error', 'not-supported', 'heartbeatPeriod is not supported') unless heartbeat_period.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def valid_url?(url)
|
84
|
+
uri = URI.parse(url)
|
85
|
+
%w[http https].include?(uri.scheme)
|
86
|
+
rescue URI::InvalidURIError
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
require_relative '../suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils'
|
3
|
+
|
4
|
+
module SubscriptionsTestKit
|
5
|
+
class SubscriptionReadEndpoint < Inferno::DSL::SuiteEndpoint
|
6
|
+
include SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
|
7
|
+
|
8
|
+
def test_run_identifier
|
9
|
+
request.headers['authorization']&.delete_prefix('Bearer ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def make_response
|
13
|
+
response.format = :json
|
14
|
+
subscription_id = request.params[:id]
|
15
|
+
|
16
|
+
subscription = find_subscription(test_run.test_session_id)
|
17
|
+
|
18
|
+
if subscription.present? && subscription.id == subscription_id
|
19
|
+
status_code = determine_subscription_status_code(subscription_id)
|
20
|
+
response.body = subscription.source_hash.merge('status' => status_code).to_json
|
21
|
+
else
|
22
|
+
response.status = 404
|
23
|
+
response.body = operation_outcome('error', 'not-found',
|
24
|
+
"No subscription exists with ID #{subscription_id}").to_json
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def tags
|
29
|
+
[SUBSCRIPTION_READ_TAG]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
module SubscriptionsTestKit
|
3
|
+
class SubscriptionRestHookEndpoint < Inferno::DSL::SuiteEndpoint
|
4
|
+
def test_run_identifier
|
5
|
+
bearer_token = extract_bearer_token(request)
|
6
|
+
"notification #{bearer_token}"
|
7
|
+
end
|
8
|
+
|
9
|
+
# Header expected to be a bearer token of the form "Bearer <token>"
|
10
|
+
def extract_bearer_token(request)
|
11
|
+
request.headers['authorization']&.delete_prefix('Bearer ')
|
12
|
+
end
|
13
|
+
|
14
|
+
def notification_extract_subscription_status(request)
|
15
|
+
notification_bundle_entries = request.params[:entry]
|
16
|
+
return if notification_bundle_entries.blank?
|
17
|
+
|
18
|
+
subscription_status_entry = notification_bundle_entries.find do |entry|
|
19
|
+
entry[:resource][:resourceType] == 'Parameters'
|
20
|
+
end
|
21
|
+
|
22
|
+
subscription_status_entry[:resource]
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_notification_type(request)
|
26
|
+
subscription_status = notification_extract_subscription_status(request)
|
27
|
+
return unless subscription_status.present?
|
28
|
+
|
29
|
+
notification_type = subscription_status[:parameter].find { |param| param[:name] == 'type' }
|
30
|
+
notification_type[:valueCode]
|
31
|
+
end
|
32
|
+
|
33
|
+
def subscription_status_extract_parameter(parameters, param_name)
|
34
|
+
return unless parameters.present?
|
35
|
+
|
36
|
+
parameters.find do |entry|
|
37
|
+
entry[:name] == param_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def notification_extract_subscription_id(request)
|
42
|
+
subscription_status = notification_extract_subscription_status(request)
|
43
|
+
return unless subscription_status.present?
|
44
|
+
|
45
|
+
subscription_param = subscription_status_extract_parameter(subscription_status[:parameter], 'subscription')
|
46
|
+
return unless subscription_param.present?
|
47
|
+
|
48
|
+
subscription_reference = subscription_param[:valueReference][:reference]
|
49
|
+
subscription_reference.split('/').last
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_response
|
53
|
+
response.status = 200
|
54
|
+
end
|
55
|
+
|
56
|
+
def tags
|
57
|
+
notification_type = extract_notification_type(request)
|
58
|
+
subscription_id = notification_extract_subscription_id(request)
|
59
|
+
[notification_type, subscription_id]
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_result
|
63
|
+
results_repo.update(result.id, result: 'pass') unless test.config.options[:accepts_multiple_requests]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
require_relative '../suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils'
|
3
|
+
|
4
|
+
module SubscriptionsTestKit
|
5
|
+
class SubscriptionStatusEndpoint < Inferno::DSL::SuiteEndpoint
|
6
|
+
include SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
|
7
|
+
|
8
|
+
def test_run_identifier
|
9
|
+
request.headers['authorization']&.delete_prefix('Bearer ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def make_response
|
13
|
+
response.format = :json
|
14
|
+
subscription = find_subscription(test_run.test_session_id)
|
15
|
+
|
16
|
+
unless subscription.present?
|
17
|
+
not_found
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
subscription_id = request.params[:id]
|
22
|
+
|
23
|
+
# Handle resource-level status params
|
24
|
+
unless subscription_id.present?
|
25
|
+
begin
|
26
|
+
params = FHIR.from_contents(request.body.string)
|
27
|
+
rescue StandardError
|
28
|
+
response.status = 400
|
29
|
+
response.body = operation_outcome('error', 'invalid', 'Invalid Parameters in request body').to_json
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
id_params = params&.parameter&.filter { |p| p.name == 'id' }
|
34
|
+
|
35
|
+
if id_params&.any? && id_params&.none? { |p| p.valueString == subscription.id }
|
36
|
+
not_found
|
37
|
+
return
|
38
|
+
else
|
39
|
+
status_params = params&.parameter&.filter { |p| p.name == 'status' }
|
40
|
+
subscription_status = determine_subscription_status_code(subscription.id)
|
41
|
+
if status_params&.any? && status_params.none? { |p| p.valueString == subscription_status }
|
42
|
+
not_found
|
43
|
+
return
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
notification_json = notification_bundle_input(result)
|
49
|
+
subscription_url = "#{base_subscription_url}/#{subscription.id}"
|
50
|
+
status_code = determine_subscription_status_code(subscription_id)
|
51
|
+
event_count = determine_event_count(test_run.test_session_id)
|
52
|
+
response.status = 200
|
53
|
+
response.body = derive_status_bundle(notification_json, subscription_url, status_code, event_count,
|
54
|
+
request.url).to_json
|
55
|
+
end
|
56
|
+
|
57
|
+
def tags
|
58
|
+
[SUBSCRIPTION_STATUS_TAG]
|
59
|
+
end
|
60
|
+
|
61
|
+
def not_found
|
62
|
+
response.status = 404
|
63
|
+
response.body = operation_outcome('error', 'not-found').to_json
|
64
|
+
end
|
65
|
+
|
66
|
+
def base_subscription_url
|
67
|
+
request.url.sub(%r{(#{Regexp.escape(FHIR_SUBSCRIPTION_PATH)}).*}, '\1')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|