subscriptions_test_kit 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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