smart_app_launch_test_kit 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbcdac5d671ebac20ff73778eddb755fb7a3774ef4ac3000260f6d6d6cb4d7f0
4
- data.tar.gz: 1f34741969397758075cab6802652dd7f8895d137d3f9b819c28ea7662a64d27
3
+ metadata.gz: 1565daad4304e65881d1a3a373d29c8674d19d57bf46342a1cdc95db6c29b6f4
4
+ data.tar.gz: 4923648577e4d53631a8efa7ccae1f254957d490e579359415ca4f9d426ac5fe
5
5
  SHA512:
6
- metadata.gz: 8b6bbae2d20d3039b3c2e701e66a85eb4dad83f4f6e8b7f044eac044f7de4582a03a9bbca2d2e38febcf41cf51216fcd0651c863fe90c890fbac2be284ff2bc2
7
- data.tar.gz: cbeca0cdb3c88e163e5e41ab7d8015715d8d4fd1192d69865b0936a1ab98fe6328053f7060f1c4e6aa7d2ef78fb0f0b445ebb1751ee9c3e346b622fa3e8631f1
6
+ metadata.gz: c1266d06f6b1bd55a93e84b93cdd0de9d628ac5d5eb084dc38817e5639869a35ccb2f5173163f0966786937ef3821a5b1e317233af2be5e042afca840f4649c3
7
+ data.tar.gz: 5101dabbab6be10be047d9e378f43fc939d605f7adcb5277c07c738f2aabbf80e5b565cb1a1885ed35184ac5b5bdf2b9a4311396498bb02766ed925986ce2433
@@ -0,0 +1,54 @@
1
+ require_relative 'ehr_launch_group_stu2'
2
+ require_relative 'token_response_body_test_stu2_2'
3
+
4
+ module SMARTAppLaunch
5
+ class EHRLaunchGroupSTU22 < EHRLaunchGroupSTU2
6
+ id :smart_ehr_launch_stu2_2
7
+ description %(
8
+ # Background
9
+
10
+ The [EHR
11
+ Launch](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-ehr-launch)
12
+ is one of two ways in which an app can be launched, the other being
13
+ Standalone launch. In an EHR launch, the app is launched from an
14
+ existing EHR session or portal by a redirect to the registered launch
15
+ URL. The EHR provides the app two parameters:
16
+
17
+ * `iss` - Which contains the FHIR server url
18
+ * `launch` - An identifier needed for authorization
19
+
20
+ # Test Methodology
21
+
22
+ Inferno will wait for the EHR server redirect upon execution. When the
23
+ redirect is received Inferno will check for the presence of the `iss`
24
+ and `launch` parameters. The security of the authorization endpoint is
25
+ then checked and authorization is attempted using the provided `launch`
26
+ identifier.
27
+
28
+ For more information on the #{title} see:
29
+
30
+ * [SMART EHR Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-ehr-launch)
31
+ )
32
+
33
+ config(
34
+ inputs: {
35
+ use_pkce: {
36
+ default: 'true',
37
+ locked: true
38
+ },
39
+ pkce_code_challenge_method: {
40
+ default: 'S256',
41
+ locked: true
42
+ },
43
+ requested_scopes: {
44
+ default: 'launch openid fhirUser offline_access user/*.rs'
45
+ }
46
+ }
47
+ )
48
+
49
+ test from: :smart_token_response_body_stu2_2
50
+
51
+ token_response_body_index = children.find_index { |child| child.id.to_s.end_with? 'token_response_body' }
52
+ children[token_response_body_index] = children.pop
53
+ end
54
+ end
@@ -0,0 +1,198 @@
1
+ {
2
+ "resourceType": "CapabilityStatement",
3
+ "id": "examplehealth-r4",
4
+ "text": {
5
+ "status": "generated",
6
+ "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n\t\t\t<p>The EHR Server supports the following transactions for the resource Person: read, vread, \n update, history, search(name,gender), create and updates.</p>\n\t\t\t<p>The EHR System supports the following message: admin-notify::Person.</p>\n\t\t\t<p>The EHR Application has a \n <a href=\"http://fhir.hl7.org/base/Profilebc054d23-75e1-4dc6-aca5-838b6b1ac81d/_history/b5fdd9fc-b021-4ea1-911a-721a60663796\">general document profile</a>.\n </p>\n\t\t</div>"
7
+ },
8
+ "url": "urn:uuid:68D043B5-9ECF-4559-A57A-396E0D452311",
9
+ "version": "20130510",
10
+ "name": "ACME-EHR",
11
+ "title": "ACME EHR capability statement",
12
+ "status": "draft",
13
+ "experimental": true,
14
+ "date": "2012-01-04",
15
+ "publisher": "ACME Corporation",
16
+ "contact": [
17
+ {
18
+ "name": "System Administrator",
19
+ "telecom": [
20
+ {
21
+ "system": "email",
22
+ "value": "wile@acme.org"
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "description": "This is the FHIR capability statement for the main EHR at ACME for the private interface - it does not describe the public interface",
28
+ "useContext": [
29
+ {
30
+ "code": {
31
+ "system": "http://terminology.hl7.org/CodeSystem/usage-context-type",
32
+ "code": "focus"
33
+ },
34
+ "valueCodeableConcept": {
35
+ "coding": [
36
+ {
37
+ "system": "http://terminology.hl7.org/CodeSystem/variant-state",
38
+ "code": "positive"
39
+ }
40
+ ]
41
+ }
42
+ }
43
+ ],
44
+ "jurisdiction": [
45
+ {
46
+ "coding": [
47
+ {
48
+ "system": "urn:iso:std:iso:3166",
49
+ "code": "US",
50
+ "display": "United States of America (the)"
51
+ }
52
+ ]
53
+ }
54
+ ],
55
+ "purpose": "Main EHR capability statement, published for contracting and operational support",
56
+ "copyright": "Copyright © Acme Healthcare and GoodCorp EHR Systems",
57
+ "kind": "instance",
58
+ "instantiates": [
59
+ "http://ihe.org/fhir/CapabilityStatement/pixm-client"
60
+ ],
61
+ "software": {
62
+ "name": "EHR",
63
+ "version": "0.00.020.2134",
64
+ "releaseDate": "2012-01-04"
65
+ },
66
+ "implementation": {
67
+ "description": "main EHR at ACME",
68
+ "url": "http://10.2.3.4/fhir"
69
+ },
70
+ "fhirVersion": "4.0.1",
71
+ "format": [
72
+ "xml",
73
+ "json"
74
+ ],
75
+ "patchFormat": [
76
+ "application/xml-patch+xml",
77
+ "application/json-patch+json"
78
+ ],
79
+ "implementationGuide": [
80
+ "http://hl7.org/fhir/us/lab"
81
+ ],
82
+ "rest": [
83
+ {
84
+ "mode": "server",
85
+ "documentation": "Main FHIR endpoint for acem health",
86
+ "security": {
87
+ "cors": true,
88
+ "service": [
89
+ {
90
+ "coding": [
91
+ {
92
+ "system": "http://terminology.hl7.org/CodeSystem/restful-security-service",
93
+ "code": "SMART-on-FHIR"
94
+ }
95
+ ]
96
+ }
97
+ ],
98
+ "description": "See Smart on FHIR documentation"
99
+ },
100
+ "resource": [
101
+ {
102
+ "type": "Patient",
103
+ "profile": "http://registry.fhir.org/r4/StructureDefinition/7896271d-57f6-4231-89dc-dcc91eab2416",
104
+ "supportedProfile": [
105
+ "http://registry.fhir.org/r4/StructureDefinition/00ab9e7a-06c7-4f77-9234-4154ca1e3347"
106
+ ],
107
+ "documentation": "This server does not let the clients create identities.",
108
+ "interaction": [
109
+ {
110
+ "code": "read"
111
+ },
112
+ {
113
+ "code": "vread",
114
+ "documentation": "Only supported for patient records since 12-Dec 2012"
115
+ },
116
+ {
117
+ "code": "update"
118
+ },
119
+ {
120
+ "code": "history-instance"
121
+ },
122
+ {
123
+ "code": "create"
124
+ },
125
+ {
126
+ "code": "history-type"
127
+ }
128
+ ],
129
+ "versioning": "versioned-update",
130
+ "readHistory": true,
131
+ "updateCreate": false,
132
+ "conditionalCreate": true,
133
+ "conditionalRead": "full-support",
134
+ "conditionalUpdate": false,
135
+ "conditionalDelete": "not-supported",
136
+ "searchInclude": [
137
+ "Organization"
138
+ ],
139
+ "searchRevInclude": [
140
+ "Person"
141
+ ],
142
+ "searchParam": [
143
+ {
144
+ "name": "identifier",
145
+ "definition": "http://hl7.org/fhir/SearchParameter/Patient-identifier",
146
+ "type": "token",
147
+ "documentation": "Only supports search by institution MRN"
148
+ },
149
+ {
150
+ "name": "general-practitioner",
151
+ "definition": "http://hl7.org/fhir/SearchParameter/Patient-general-practitioner",
152
+ "type": "reference"
153
+ }
154
+ ]
155
+ }
156
+ ],
157
+ "interaction": [
158
+ {
159
+ "code": "transaction"
160
+ },
161
+ {
162
+ "code": "history-system"
163
+ }
164
+ ],
165
+ "compartment": [
166
+ "http://hl7.org/fhir/CompartmentDefinition/patient"
167
+ ]
168
+ }
169
+ ],
170
+ "messaging": [
171
+ {
172
+ "endpoint": [
173
+ {
174
+ "protocol": {
175
+ "system": "http://terminology.hl7.org/CodeSystem/message-transport",
176
+ "code": "mllp"
177
+ },
178
+ "address": "mllp:10.1.1.10:9234"
179
+ }
180
+ ],
181
+ "reliableCache": 30,
182
+ "documentation": "ADT A08 equivalent for external system notifications",
183
+ "supportedMessage": [
184
+ {
185
+ "mode": "receiver",
186
+ "definition": "MessageDefinition/example"
187
+ }
188
+ ]
189
+ }
190
+ ],
191
+ "document": [
192
+ {
193
+ "mode": "consumer",
194
+ "documentation": "Basic rules for all documents in the EHR system",
195
+ "profile": "http://fhir.hl7.org/base/Profilebc054d23-75e1-4dc6-aca5-838b6b1ac81d/_history/b5fdd9fc-b021-4ea1-911a-721a60663796"
196
+ }
197
+ ]
198
+ }
@@ -0,0 +1,59 @@
1
+ require_relative 'smart_access_brands_validation_group'
2
+ require_relative 'smart_access_brands_retrieval_group'
3
+
4
+ module SMARTAppLaunch
5
+ class SMARTAccessBrandsGroup < Inferno::TestGroup
6
+ id :retrieve_and_validate_smart_access_brands
7
+ title 'Retrieve and Validate SMART Access Brands Bundle'
8
+ description %(
9
+ Verify that the Brand Bundle Publisher makes its User-access Brands publication publicly available
10
+ in the format defined by the [User Access Brand Bundle Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brands-bundle.html)
11
+ with valid Endpoint and Organization entries according to the
12
+ [User Access Endpoint Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-endpoint.html)
13
+ and the [User Access Brand Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brand.html)
14
+ respectively. This test group will issue a HTTP GET request against the supplied URL to retrieve the publisher's
15
+ User-access Brands list and ensure the list is publicly accessible. It will then ensure that the returned
16
+ User-access Brands list publication is in the User Access Brand Bundle Profile format with valid User Access
17
+ Brands and User Access Endpoints.
18
+
19
+ For systems that provide the User Access Brands Bundle at a public endpoint, please run
20
+ this test with the User Access Brands Publication URL input populated and the User Access Brands Bundle
21
+ input left blank. While it is the expectation of the specification for the User Access Brands Bundle to be served
22
+ at a public-facing endpoint, testers can validate a User Access Brands Bundle not served at a public endpoint by
23
+ running these tests with the User Access Brands Bundle input populated and the User Access Brands Publication URL
24
+ input left blank. This will cause these group of retrieval group of tests to skip, rather than pass completely,
25
+ as being served at an stable location is considered a requirement of the spec.
26
+ )
27
+
28
+ input_instructions <<~INSTRUCTIONS
29
+ For systems that make their User Access Brand Bundle available at a public endpoint, please input
30
+ the User Access Brand Publication URL to retrieve the Bundle from there in order to perform validation, and leave
31
+ the User Access Brand Bundle input blank.
32
+
33
+ While it is the expectation of the specification for the User Access Brands Bundle to be publicly available,
34
+ for systems that do not have a User Access Brand Bundle served at a public endpoint, testers can validate by
35
+ providing the User Access Brand Bundle as an input and leaving the User Access Brand Publication URL input blank.
36
+ INSTRUCTIONS
37
+
38
+ run_as_group
39
+
40
+ input :user_access_brands_publication_url,
41
+ title: 'User Access Brands Publication URL',
42
+ description: %(The URL to the developer's public User Access Brands Publication. Insert your User Access
43
+ Brands publication URL if you host your Bundle at a public-facing endpoint and want Inferno to retrieve the
44
+ Bundle from there.),
45
+ optional: true
46
+
47
+ input :user_access_brands_bundle,
48
+ title: 'User Access Brands Bundle',
49
+ description: %(The developer's User Access Brands Publication in the JSON string format. If this input is
50
+ populated, Inferno will use the Bundle inserted here to do validation. Insert your User Access Brands
51
+ Bundle in the JSON format in this input to validate your list without Inferno needing to access the Bundle
52
+ via a public-facing endpoint.),
53
+ type: 'textarea',
54
+ optional: true
55
+
56
+ group from: :smart_access_brands_retrieval
57
+ group from: :smart_access_brands_validation
58
+ end
59
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'smart_access_brands_retrieve_bundle_test'
2
+
3
+ module SMARTAppLaunch
4
+ class SMARTAccessBrandsRetrievalGroup < Inferno::TestGroup
5
+ id :smart_access_brands_retrieval
6
+ title 'Retrieve SMART Access Brands Bundle'
7
+ description %(
8
+ A publisher's User Access Brand Bundle must be publicly available. This test
9
+ issues a HTTP GET request against the supplied URL and expects to receive
10
+ the User Access Brand Bundle at this location.
11
+ )
12
+ run_as_group
13
+
14
+ http_client do
15
+ url :user_access_brands_publication_url
16
+ headers Accept: 'application/json, application/fhir+json'
17
+ end
18
+
19
+ test from: :smart_access_brands_retrieve_bundle
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ module SMARTAppLaunch
2
+ class SMARTAccessBrandsRetrievalTest < Inferno::Test
3
+ id :smart_access_brands_retrieve_bundle
4
+ title 'Server returns publicly accessible SMART Access Brands Bundle'
5
+ description %(
6
+ Verify that the publisher's User Access Brands Bundle can be publicly
7
+ accessed at the supplied URL location.
8
+ )
9
+
10
+ makes_request :bundle_request
11
+
12
+ input :user_access_brands_publication_url,
13
+ optional: true
14
+
15
+ run do
16
+ skip_if user_access_brands_publication_url.blank?, %(
17
+ No User Access Brands Publication endpoint URL inputted. It is an expectation of the specification for the
18
+ User Access Brands Bundle to be publicly available'
19
+ )
20
+
21
+ get(tags: ['smart_access_brands_bundle'])
22
+ assert_response_status(200)
23
+ assert(request.headers.any? { |header| header.name == 'access-control-allow-origin' }, %(
24
+ All GET requests must support Cross-Origin Resource Sharing (CORS) for all GET requests to the artifacts
25
+ described in this guide.))
26
+ unless request.headers.any? { |header| header.name == 'etag' }
27
+ add_message('warning', 'Brand Bundle HTTP responses should include an Etag header')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,77 @@
1
+ require_relative 'smart_access_brands_group'
2
+
3
+ module SMARTAppLaunch
4
+ class SMARTAccessBrandsSuite < Inferno::TestSuite
5
+ id 'smart_access_brands'
6
+ title 'SMART User-access Brands and Endpoints STU2.2'
7
+ short_title 'SMART User-access Brands'
8
+ version VERSION
9
+
10
+ description <<~DESCRIPTION
11
+ The SMART User-access Brands Test Suite verifies that Brand Bundle Publishers publish valid User-access
12
+ Brand Bundles according to the SMART App Launch IG
13
+ [User-access Brands and Endpoints](https://hl7.org/fhir/smart-app-launch/STU2.2/brands.html#user-access-brands-and-endpoints)
14
+ requirements.
15
+
16
+ The specification defines FHIR profiles for Endpoint, Organization, and Bundle resources that help users connect
17
+ their apps to health data providers. It outlines the process for data providers to publish FHIR Endpoint and
18
+ Organization resources, where each Organization includes essential branding information such as the organization's
19
+ name, logo, and user access details. Apps present branded Organizations to help users select the right data
20
+ providers.
21
+
22
+ This test suite is currently designed to fetch and validate a single User-Access Brand Bundle. It does not
23
+ currently evaluate the system's ability to allow Health Data Providers to manage all data elements marked
24
+ "Must-Support" in the "User Access Brand" and "User Access Endpoint" profiles.
25
+ DESCRIPTION
26
+
27
+ input_instructions <<~INSTRUCTIONS
28
+ For systems that make their User Access Brand Bundle available at a public endpoint, please input
29
+ the User Access Brand Publication URL to retrieve the Bundle from there in order to perform validation, and leave
30
+ the User Access Brand Bundle input blank.
31
+
32
+ Although it is expected that systems do have their Bundle publicly available, for systems that do not have a
33
+ User Access Brand Bundle served at a public endpoint, testers can validate by providing the User Access Brand
34
+ Bundle as an input and leaving the User Access Brand Publication URL input blank.
35
+ INSTRUCTIONS
36
+
37
+ VALIDATION_MESSAGE_FILTERS = [
38
+ /\A\S+: \S+: URL value '.*' does not resolve/,
39
+ %r{\A\S+: \S+: Bundled or contained reference not found within the bundle/resource} # Validator issue with Brand profile: https://chat.fhir.org/#narrow/stream/291844-FHIR-Validator/topic/SMART.20v2.2E2.20User.20Access.20Brands.3A.20Brand.20validation.20error.3F/near/466321024
40
+ ].freeze
41
+
42
+ fhir_resource_validator do
43
+ igs 'hl7.fhir.uv.smart-app-launch#2.2.0'
44
+
45
+ cli_context({
46
+ # Allow example URLs because we give tester option to follow URLs anyhow
47
+ # (configurable) and its nice to be able to have the examples in the IG pass
48
+ allowExampleUrls: true
49
+ })
50
+
51
+ message_filters = VALIDATION_MESSAGE_FILTERS
52
+
53
+ exclude_message do |message|
54
+ message_filters.any? { |filter| filter.match? message.message }
55
+ end
56
+ end
57
+
58
+ Dir.each_child(File.join(__dir__, '/smart_access_brands_examples/')) do |filename|
59
+ resource_example = File.read(File.join(__dir__, '/smart_access_brands_examples/', filename))
60
+ if filename.end_with?('.erb')
61
+ erb_template = ERB.new(resource_example)
62
+ resource_example = JSON.parse(erb_template.result).to_json
63
+ filename = filename.delete_suffix('.erb')
64
+ headers = { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*',
65
+ 'Etag' => SecureRandom.hex(32) }
66
+ else
67
+ filename = "#{filename.delete_suffix('.json')}/metadata"
68
+ headers = { 'Content-Type' => 'application/json' }
69
+ end
70
+ route_handler = proc { [200, headers, [resource_example]] }
71
+
72
+ route :get, File.join('/examples/', filename), route_handler
73
+ end
74
+
75
+ group from: :retrieve_and_validate_smart_access_brands
76
+ end
77
+ end
@@ -0,0 +1,104 @@
1
+ module SMARTAppLaunch
2
+ class SMARTAccessBrandsValidateBrands < Inferno::Test
3
+ id :smart_access_brands_valid_brands
4
+ title 'Service Base URL List contains valid Brand resources'
5
+ description %(
6
+ Verify that Bundle of User Access Brands and Endpoints contains Brands that are valid
7
+ Organization resources according to the [User Access Brand Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brand.html).
8
+
9
+ Along with validating the Organization resources, this test also ensures:
10
+ - Each endpoint referenced in the Organization portal extension also appear in Organization.endpoint
11
+ - Any endpoints referenced by the Organization must appear in the Bundle
12
+
13
+ This test does not currently validate availability or format of Brand or Portal logos.
14
+ )
15
+
16
+ input :user_access_brands_bundle,
17
+ optional: true
18
+
19
+ def find_referenced_endpoint(bundle_resource, endpoint_id_ref)
20
+ bundle_resource
21
+ .entry
22
+ .map(&:resource)
23
+ .select { |resource| resource.resourceType == 'Endpoint' }
24
+ .map(&:id)
25
+ .select { |endpoint_id| endpoint_id_ref.include? endpoint_id }
26
+ end
27
+
28
+ def find_extension(extension_array, extension_name)
29
+ extension_array.find do |extension|
30
+ extension.url.ends_with?(extension_name)
31
+ end
32
+ end
33
+
34
+ def find_all_extensions(extension_array, extension_name)
35
+ extension_array.select do |extension|
36
+ extension.url == extension_name
37
+ end
38
+ end
39
+
40
+ def check_portal_endpoints(portal_endpoints, organization_endpoints)
41
+ portal_endpoints.each do |portal_endpoint|
42
+ portal_endpoint_found = organization_endpoints.any? do |endpoint_reference|
43
+ portal_endpoint.valueReference.reference == endpoint_reference
44
+ end
45
+ assert(portal_endpoint_found, %(
46
+ Portal endpoints must also appear at Organization.endpoint. The portal endpoint with reference
47
+ #{portal_endpoint.valueReference.reference} was not found at Organization.endpoint.))
48
+ end
49
+ end
50
+
51
+ def skip_message
52
+ %(
53
+ No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
54
+ input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
55
+ request, or provide the Bundle as an input.
56
+ )
57
+ end
58
+
59
+ run do
60
+ bundle_response = if user_access_brands_bundle.blank?
61
+ load_tagged_requests('smart_access_brands_bundle')
62
+ skip skip_message if requests.length != 1
63
+ requests.first.response_body
64
+ else
65
+ user_access_brands_bundle
66
+ end
67
+
68
+ skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
69
+
70
+ assert_valid_json(bundle_response)
71
+ bundle_resource = FHIR.from_contents(bundle_response)
72
+
73
+ skip_if bundle_resource.entry.empty?, 'The given Bundle does not contain any resources'
74
+ assert_valid_bundle_entries(bundle: bundle_resource,
75
+ resource_types: {
76
+ Organization: 'http://hl7.org/fhir/smart-app-launch/StructureDefinition/user-access-brand'
77
+ })
78
+
79
+ organization_resources = bundle_resource
80
+ .entry
81
+ .map(&:resource)
82
+ .select { |resource| resource.resourceType == 'Organization' }
83
+
84
+ organization_resources.each do |organization|
85
+ endpoint_references = organization.endpoint.map(&:reference)
86
+
87
+ if organization.extension.present?
88
+ portal_extension = find_extension(organization.extension, '/organization-portal')
89
+ if portal_extension.present?
90
+ portal_endpoints = find_all_extensions(portal_extension.extension, 'portalEndpoint')
91
+ check_portal_endpoints(portal_endpoints, endpoint_references)
92
+ end
93
+ end
94
+
95
+ endpoint_references.each do |endpoint_id_ref|
96
+ organization_referenced_endpts = find_referenced_endpoint(bundle_resource, endpoint_id_ref)
97
+ assert !organization_referenced_endpts.empty?,
98
+ "Organization with id: #{organization.id} references an Endpoint that is not contained in this
99
+ bundle."
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,45 @@
1
+ module SMARTAppLaunch
2
+ class SMARTAccessBrandsValidateBundle < Inferno::Test
3
+ id :smart_access_brands_valid_bundle
4
+ title 'Server returns valid Bundle resource according to the User Access Brands Bundle Profile'
5
+ description %(
6
+ Verify that the returned Bundle is a valid User Access Brands Bundle according to the
7
+ [User Access Brand Bundle Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brands-bundle.html).
8
+
9
+ This test also ensures the Bundle is the 'collection' type and that it is not empty.
10
+ )
11
+
12
+ input :user_access_brands_bundle,
13
+ optional: true
14
+
15
+ def skip_message
16
+ %(
17
+ No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
18
+ input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
19
+ request, or provide the Bundle as an input.
20
+ )
21
+ end
22
+
23
+ run do
24
+ bundle_response = if user_access_brands_bundle.blank?
25
+ load_tagged_requests('smart_access_brands_bundle')
26
+ skip skip_message if requests.length != 1
27
+ requests.first.response_body
28
+ else
29
+ user_access_brands_bundle
30
+ end
31
+
32
+ skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
33
+
34
+ assert_valid_json(bundle_response)
35
+ bundle_resource = FHIR.from_contents(bundle_response)
36
+ assert_resource_type(:bundle, resource: bundle_resource)
37
+ assert_valid_resource(resource: bundle_resource, profile_url: 'http://hl7.org/fhir/smart-app-launch/StructureDefinition/user-access-brands-bundle')
38
+
39
+ assert(bundle_resource.type == 'collection', 'The SMART Access Brands Bundle must be type `collection`')
40
+ assert(bundle_resource.timestamp.present?,
41
+ 'Bundle.timestamp must be populated to advertise the timestamp of the last change to the contents')
42
+ assert !bundle_resource.entry.empty?, 'The given Bundle does not contain any brands or endpoints.'
43
+ end
44
+ end
45
+ end