subscriptions_test_kit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/inferno_requirements_tools/ext/inferno_core/runnable.rb +22 -0
  4. data/lib/inferno_requirements_tools/tasks/collect_requirements.rb +222 -0
  5. data/lib/inferno_requirements_tools/tasks/requirements_coverage.rb +264 -0
  6. data/lib/subscriptions_test_kit/common/notification_conformance_verification.rb +310 -0
  7. data/lib/subscriptions_test_kit/common/subscription_conformance_verification.rb +87 -0
  8. data/lib/subscriptions_test_kit/docs/samples/Subscription_empty.json +37 -0
  9. data/lib/subscriptions_test_kit/docs/samples/Subscription_full-resource.json +37 -0
  10. data/lib/subscriptions_test_kit/docs/samples/Subscription_id-only.json +38 -0
  11. data/lib/subscriptions_test_kit/docs/subscriptions_r5_backport_r4_client_suite_description.md +120 -0
  12. data/lib/subscriptions_test_kit/docs/subscriptions_r5_backport_r4_server_suite_description.md +170 -0
  13. data/lib/subscriptions_test_kit/endpoints/subscription_create_endpoint.rb +90 -0
  14. data/lib/subscriptions_test_kit/endpoints/subscription_read_endpoint.rb +32 -0
  15. data/lib/subscriptions_test_kit/endpoints/subscription_rest_hook_endpoint.rb +66 -0
  16. data/lib/subscriptions_test_kit/endpoints/subscription_status_endpoint.rb +70 -0
  17. data/lib/subscriptions_test_kit/igs/README.md +21 -0
  18. data/lib/subscriptions_test_kit/jobs/send_subscription_notifications.rb +130 -0
  19. data/lib/subscriptions_test_kit/requirements/generated/subscriptions-test-kit_requirements_coverage.csv +136 -0
  20. data/lib/subscriptions_test_kit/requirements/subscriptions-test-kit_out_of_scope_requirements.csv +23 -0
  21. data/lib/subscriptions_test_kit/requirements/subscriptions-test-kit_requirements.csv +145 -0
  22. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils.rb +140 -0
  23. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/fixtures/capability_statement.json +57 -0
  24. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_payload_verification_test.rb +50 -0
  25. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_verification_test.rb +25 -0
  26. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/processing_attestation_test.rb +38 -0
  27. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/subscription_verification_test.rb +28 -0
  28. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification_group.rb +20 -0
  29. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_test.rb +128 -0
  30. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/event_notification_verification_test.rb +40 -0
  31. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/handshake_notification_verification_test.rb +40 -0
  32. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification_group.rb +16 -0
  33. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow_group.rb +33 -0
  34. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client_suite.rb +66 -0
  35. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/creation_response_conformance_test.rb +51 -0
  36. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/notification_delivery_test.rb +56 -0
  37. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/subscription_conformance_test.rb +72 -0
  38. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_group.rb +24 -0
  39. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_verification/notification_conformance_test.rb +73 -0
  40. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_verification_group.rb +19 -0
  41. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/subscription_creation.rb +63 -0
  42. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/subscription_status_operation.rb +58 -0
  43. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement/cs_conformance_test.rb +49 -0
  44. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement/topic_discovery_test.rb +106 -0
  45. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/capability_statement_group.rb +21 -0
  46. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/empty_content/empty_conformance_test.rb +63 -0
  47. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/empty_content_group.rb +58 -0
  48. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/full_resource_content/full_resource_conformance_test.rb +68 -0
  49. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/full_resource_content_group.rb +58 -0
  50. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/id_only_content/id_only_conformance_test.rb +66 -0
  51. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification/id_only_content_group.rb +58 -0
  52. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/event_notification_group.rb +25 -0
  53. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat/handshake_conformance_test.rb +67 -0
  54. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat/heartbeat_conformance_test.rb +74 -0
  55. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/handshake_heartbeat_group.rb +19 -0
  56. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/status_operation/status_invocation_test.rb +43 -0
  57. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/status_operation_group.rb +15 -0
  58. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/subscription_rejection/reject_subscriptions_test.rb +181 -0
  59. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage/subscription_rejection_group.rb +21 -0
  60. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/coverage_group.rb +26 -0
  61. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/workflow_group.rb +27 -0
  62. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server_suite.rb +79 -0
  63. data/lib/subscriptions_test_kit/tags.rb +10 -0
  64. data/lib/subscriptions_test_kit/urls.rb +37 -0
  65. data/lib/subscriptions_test_kit/version.rb +5 -0
  66. data/lib/subscriptions_test_kit.rb +3 -0
  67. 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