onc_certification_g10_test_kit 2.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/inferno/terminology/expected_manifest.yml +242 -29
  3. data/lib/inferno/terminology/fhir_package_manager.rb +27 -0
  4. data/lib/inferno/terminology/loader.rb +22 -1
  5. data/lib/inferno/terminology/tasks/create_value_set_validators.rb +1 -1
  6. data/lib/inferno/terminology/tasks/download_fhir_terminology.rb +5 -0
  7. data/lib/inferno/terminology/value_set.rb +51 -5
  8. data/lib/onc_certification_g10_test_kit/base_token_refresh_group.rb +5 -4
  9. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_stu1.rb +5 -0
  10. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_stu2.rb +2 -0
  11. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_validation.rb +206 -28
  12. data/lib/onc_certification_g10_test_kit/bulk_export_validation_tester.rb +25 -40
  13. data/lib/onc_certification_g10_test_kit/encounter_context_test.rb +30 -0
  14. data/lib/onc_certification_g10_test_kit/feature.rb +5 -8
  15. data/lib/onc_certification_g10_test_kit/limited_scope_grant_test.rb +18 -5
  16. data/lib/onc_certification_g10_test_kit/profile_selector.rb +175 -0
  17. data/lib/onc_certification_g10_test_kit/restricted_resource_type_access_group.rb +54 -4
  18. data/lib/onc_certification_g10_test_kit/single_patient_us_core_5_api_group.rb +93 -0
  19. data/lib/onc_certification_g10_test_kit/smart_app_launch_invalid_aud_group.rb +50 -5
  20. data/lib/onc_certification_g10_test_kit/smart_ehr_patient_launch_group.rb +94 -0
  21. data/lib/onc_certification_g10_test_kit/smart_ehr_patient_launch_group_stu2.rb +94 -0
  22. data/lib/onc_certification_g10_test_kit/smart_ehr_practitioner_app_group.rb +197 -13
  23. data/lib/onc_certification_g10_test_kit/smart_invalid_pkce_group.rb +310 -0
  24. data/lib/onc_certification_g10_test_kit/smart_invalid_token_group_stu2.rb +211 -0
  25. data/lib/onc_certification_g10_test_kit/smart_limited_app_group.rb +135 -9
  26. data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group.rb +16 -4
  27. data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group_stu2.rb +130 -0
  28. data/lib/onc_certification_g10_test_kit/smart_scopes_test.rb +134 -67
  29. data/lib/onc_certification_g10_test_kit/smart_standalone_patient_app_group.rb +166 -11
  30. data/lib/onc_certification_g10_test_kit/unrestricted_resource_type_access_group.rb +119 -135
  31. data/lib/onc_certification_g10_test_kit/version.rb +1 -1
  32. data/lib/onc_certification_g10_test_kit/visual_inspection_and_attestations_group.rb +19 -0
  33. data/lib/onc_certification_g10_test_kit/well_known_capabilities_test.rb +7 -1
  34. data/lib/onc_certification_g10_test_kit.rb +115 -74
  35. metadata +19 -11
  36. data/lib/onc_certification_g10_test_kit/profile_guesser.rb +0 -72
@@ -0,0 +1,175 @@
1
+ module ONCCertificationG10TestKit
2
+ module ProfileSelector
3
+ def extract_profile(profile)
4
+ case profile
5
+ when 'Medication'
6
+ return versioned_us_core_module.const_get('USCoreTestSuite').metadata.find do |meta|
7
+ meta.resource == profile
8
+ end.profile_url
9
+ when 'Location'
10
+ return 'http://hl7.org/fhir/StructureDefinition/Location'
11
+ end
12
+ versioned_us_core_module.const_get("#{profile}Group").metadata.profile_url
13
+ end
14
+
15
+ def observation_contains_code(observation_resource, code)
16
+ observation_resource&.code&.coding&.any? { |coding| coding&.code == code }
17
+ end
18
+
19
+ def resource_contains_category(resource, category_code, category_system = nil) # rubocop:disable Metrics/CyclomaticComplexity
20
+ resource&.category&.any? do |category|
21
+ category.coding&.any? do |coding|
22
+ coding.code == category_code &&
23
+ (category_system.blank? || coding.system.blank? || category_system == coding.system)
24
+ end
25
+ end
26
+ end
27
+
28
+ def select_profile(resource) # rubocop:disable Metrics/CyclomaticComplexity
29
+ case resource.resourceType
30
+ when 'Condition'
31
+ case suite_options[:us_core_version]
32
+ when 'us_core_5'
33
+ if resource_contains_category(resource, 'encounter-diagnosis', 'http://terminology.hl7.org/CodeSystem/condition-category')
34
+ extract_profile('ConditionEncounterDiagnosis')
35
+ elsif resource_contains_category(resource, 'problem-list-item',
36
+ 'http://terminology.hl7.org/CodeSystem/condition-category') ||
37
+ resource_contains_category(resource, 'health-concern', 'http://terminology.hl7.org/CodeSystem/condition-category')
38
+ extract_profile('ConditionProblemsHealthConcerns')
39
+ end
40
+ else
41
+ extract_profile(resource.resourceType)
42
+ end
43
+ when 'DiagnosticReport'
44
+ return extract_profile('DiagnosticReportLab') if resource_contains_category(resource, 'LAB', 'http://terminology.hl7.org/CodeSystem/v2-0074')
45
+
46
+ extract_profile('DiagnosticReportNote')
47
+ when 'Observation'
48
+ return extract_profile('Smokingstatus') if observation_contains_code(resource, '72166-2')
49
+
50
+ return extract_profile('ObservationLab') if resource_contains_category(resource, 'laboratory', 'http://terminology.hl7.org/CodeSystem/observation-category')
51
+
52
+ return extract_profile('PediatricBmiForAge') if observation_contains_code(resource, '59576-9')
53
+
54
+ return extract_profile('PediatricWeightForHeight') if observation_contains_code(resource, '77606-2')
55
+
56
+ return extract_profile('PulseOximetry') if observation_contains_code(resource, '59408-5')
57
+
58
+ if observation_contains_code(resource, '8289-1')
59
+ case suite_options[:us_core_version]
60
+ when 'us_core_3'
61
+ return extract_profile('HeadCircumference')
62
+ else
63
+ return extract_profile('HeadCircumferencePercentile')
64
+ end
65
+ end
66
+
67
+ return extract_profile('HeadCircumference') if observation_contains_code(resource, '9843-4')
68
+
69
+ # FHIR Vital Signs profiles: https://www.hl7.org/fhir/observation-vitalsigns.html
70
+ # Vital Signs Panel, Oxygen Saturation are not required by USCDI
71
+ # Body Mass Index is replaced by :pediatric_bmi_age Profile
72
+ # Systolic Blood Pressure, Diastolic Blood Pressure are covered by :blood_pressure Profile
73
+ # Head Circumference is replaced by US Core Head Occipital-frontal Circumference Percentile Profile
74
+ if observation_contains_code(resource, '39156-5') && suite_options[:us_core_version] != 'us_core_3'
75
+ return extract_profile('Bmi')
76
+ end
77
+
78
+ if observation_contains_code(resource, '85354-9')
79
+ case suite_options[:us_core_version]
80
+ when 'us_core_3'
81
+ return extract_profile('Bp')
82
+ else
83
+ return extract_profile('BloodPressure')
84
+ end
85
+ end
86
+
87
+ if observation_contains_code(resource, '8302-2')
88
+ case suite_options[:us_core_version]
89
+ when 'us_core_3'
90
+ return extract_profile('Bodyheight')
91
+ else
92
+ return extract_profile('BodyHeight')
93
+ end
94
+ end
95
+
96
+ if observation_contains_code(resource, '8310-5')
97
+ case suite_options[:us_core_version]
98
+ when 'us_core_3'
99
+ return extract_profile('Bodytemp')
100
+ else
101
+ return extract_profile('BodyTemperature')
102
+ end
103
+ end
104
+
105
+ if observation_contains_code(resource, '29463-7')
106
+ case suite_options[:us_core_version]
107
+ when 'us_core_3'
108
+ return extract_profile('Bodyweight')
109
+ else
110
+ return extract_profile('BodyWeight')
111
+ end
112
+ end
113
+
114
+ if observation_contains_code(resource, '8867-4')
115
+ case suite_options[:us_core_version]
116
+ when 'us_core_3'
117
+ return extract_profile('Heartrate')
118
+ else
119
+ return extract_profile('HeartRate')
120
+ end
121
+ end
122
+
123
+ if observation_contains_code(resource, '9279-1')
124
+ case suite_options[:us_core_version]
125
+ when 'us_core_3'
126
+ return extract_profile('Resprate')
127
+ else
128
+ return extract_profile('RespiratoryRate')
129
+ end
130
+ end
131
+
132
+ if suite_options[:us_core_version] == 'us_core_5' &&
133
+ resource_contains_category(
134
+ resource, 'clinical-test', 'http://terminology.hl7.org/CodeSystem/observation-category'
135
+ )
136
+ return extract_profile('ObservationClinicalTest')
137
+ end
138
+
139
+ if suite_options[:us_core_version] == 'us_core_5' && observation_contains_code(resource, '76690-7')
140
+ return extract_profile('ObservationSexualOrientation')
141
+ end
142
+
143
+ if suite_options[:us_core_version] == 'us_core_5' &&
144
+ resource_contains_category(resource, 'social-history',
145
+ 'http://terminology.hl7.org/CodeSystem/observation-category')
146
+ return extract_profile('ObservationSocialHistory')
147
+ end
148
+
149
+ # We will simply match all Observations of category "survey" to SDOH,
150
+ # and do not look at the category "sdoh". US Core spec team says that
151
+ # support for the "sdoh" category is limited, and the validation rules
152
+ # allow for very generic surveys to validate against this profile.
153
+ # And will not validate against the ObservationSurvey profile itself.
154
+ # This may not be exactly precise but it works out the same
155
+
156
+ # if we wanted to be more specific here, we would add:
157
+ # `resource_contains_category(resource, 'sdoh',
158
+ # 'http://terminology.hl7.org/CodeSystem/observation-category') &&`
159
+ # along with a specific extract_profile('ObservationSurvey') to catch non-sdoh.
160
+ if suite_options[:us_core_version] == 'us_core_5' &&
161
+ resource_contains_category(resource, 'survey',
162
+ 'http://terminology.hl7.org/CodeSystem/observation-category')
163
+
164
+ return extract_profile('ObservationSdohAssessment')
165
+ end
166
+
167
+ nil
168
+ else
169
+ extract_profile(resource.resourceType)
170
+ end
171
+ rescue StandardError
172
+ skip "Could not determine profile of \"#{resource.resourceType}\" resource."
173
+ end
174
+ end
175
+ end
@@ -27,6 +27,8 @@ module ONCCertificationG10TestKit
27
27
  * Observation
28
28
  * Procedure
29
29
 
30
+ If testing against USCDI v2, ServiceRequest is also checked.
31
+
30
32
  For each of the resources that can be mapped to USCDI data class or
31
33
  elements, this set of tests performs a minimum number of requests to
32
34
  determine if access to the resource type is appropriately allowed or
@@ -38,10 +40,18 @@ module ONCCertificationG10TestKit
38
40
  required status search parameter.
39
41
 
40
42
  This set of tests does not attempt to access resources that do not
41
- directly map to USCDI v1, including Encounter, Location, Organization, and
42
- Practitioner. It also does not test Provenance, as this resource type is
43
- accessed by queries through other resource types. These resource types are
44
- accessed in the more comprehensive Single Patient Query tests.
43
+ directly map to USCDI. For USCDI v1 this includes:
44
+
45
+ * Encounter
46
+ * Location
47
+ * Organization
48
+ * Practitioner
49
+
50
+ For USCDI v2 this includes:
51
+
52
+ * Location
53
+ * Organization
54
+ * Practitioner
45
55
 
46
56
  If the tester chooses to not grant access to a resource, the queries
47
57
  associated with that resource must result in either a 401 (Unauthorized)
@@ -299,5 +309,45 @@ module ONCCertificationG10TestKit
299
309
  USCoreTestKit::USCoreV311::ProcedureGroup
300
310
  end
301
311
  end
312
+
313
+ test from: :g10_restricted_access_test do
314
+ title 'Access to Encounter resources are restricted properly based on patient-selected scope'
315
+ description %(
316
+ This test ensures that access to the Encounter is granted or
317
+ denied based on the selection by the tester prior to the execution of
318
+ the test. If the tester indicated that access will be granted to this
319
+ resource, this test verifies that a search by patient in this resource
320
+ does not result in an access denied result. If the tester indicated that
321
+ access will be denied for this resource, this verifies that search by
322
+ patient in the resource results in an access denied result.
323
+ )
324
+ id :g10_encounter_restricted_access
325
+
326
+ required_suite_options us_core_version: 'us_core_5'
327
+
328
+ def resource_group
329
+ USCoreTestKit::USCoreV501::EncounterGroup
330
+ end
331
+ end
332
+
333
+ test from: :g10_restricted_access_test do
334
+ title 'Access to ServiceRequest resources are restricted properly based on patient-selected scope'
335
+ description %(
336
+ This test ensures that access to the ServiceRequest is granted or
337
+ denied based on the selection by the tester prior to the execution of
338
+ the test. If the tester indicated that access will be granted to this
339
+ resource, this test verifies that a search by patient in this resource
340
+ does not result in an access denied result. If the tester indicated that
341
+ access will be denied for this resource, this verifies that search by
342
+ patient in the resource results in an access denied result.
343
+ )
344
+ id :g10_service_request_restricted_access
345
+
346
+ required_suite_options us_core_version: 'us_core_5'
347
+
348
+ def resource_group
349
+ USCoreTestKit::USCoreV501::ServiceRequestGroup
350
+ end
351
+ end
302
352
  end
303
353
  end
@@ -0,0 +1,93 @@
1
+ module ONCCertificationG10TestKit
2
+ class SinglePatientUSCore5APIGroup < Inferno::TestGroup
3
+ id :g10_single_patient_us_core_5_api
4
+ title 'Single Patient API (US Core 5.0.1)'
5
+ description %(
6
+ For each of the relevant USCDI data elements provided in the
7
+ CapabilityStatement, this test executes the [required supported
8
+ searches](http://hl7.org/fhir/us/core/STU4/CapabilityStatement-us-core-server.html)
9
+ as defined by the US Core Implementation Guide v4.0.0.
10
+
11
+ The test begins by searching by one or more patients, with the expectation
12
+ that the Bearer token provided to the test grants access to all USCDI
13
+ resources. It uses results returned from that query to generate other
14
+ queries and checks that the results are consistent with the provided
15
+ search parameters. It then performs a read on each Resource returned and
16
+ validates the response against the relevant
17
+ [profile](http://hl7.org/fhir/us/core/STU4/profiles-and-extensions.html)
18
+ as currently defined in the US Core Implementation Guide.
19
+
20
+ All MUST SUPPORT elements must be seen before the test can pass, as well
21
+ as Data Absent Reason to demonstrate that the server can properly handle
22
+ missing data. Note that Encounter, Organization and Practitioner resources
23
+ must be accessible as references in some US Core profiles to satisfy must
24
+ support requirements, and those references will be validated to their US
25
+ Core profile. These resources will not be tested for FHIR search support.
26
+ )
27
+ run_as_group
28
+
29
+ input :url,
30
+ title: 'FHIR Endpoint',
31
+ description: 'URL of the FHIR endpoint used by SMART applications'
32
+ input :patient_id,
33
+ title: 'Patient ID from SMART App Launch',
34
+ locked: true
35
+ input :additional_patient_ids,
36
+ title: 'Additional Patient IDs',
37
+ description: <<~DESCRIPTION,
38
+ Comma separated list of Patient IDs that together with the Patient
39
+ ID from the SMART App Launch contain all MUST SUPPORT elements.
40
+ DESCRIPTION
41
+ optional: true
42
+ input :smart_credentials,
43
+ title: 'SMART App Launch Credentials',
44
+ type: :oauth_credentials,
45
+ locked: true
46
+
47
+ fhir_client do
48
+ url :url
49
+ oauth_credentials :smart_credentials
50
+ end
51
+
52
+ input_order :url, :patient_id, :additional_patient_ids, :implantable_device_codes, :smart_credentials
53
+
54
+ test do
55
+ id :g10_patient_id_setup
56
+ title 'Manage patient id list'
57
+
58
+ input :patient_id, :additional_patient_ids
59
+ output :patient_ids
60
+
61
+ run do
62
+ smart_app_launch_patient_id = patient_id.presence
63
+ additional_patient_ids_list =
64
+ if additional_patient_ids.present?
65
+ additional_patient_ids
66
+ .split(',')
67
+ .map(&:strip)
68
+ .map(&:presence)
69
+ .compact
70
+ else
71
+ []
72
+ end
73
+
74
+ all_patient_ids = ([smart_app_launch_patient_id] + additional_patient_ids_list).compact.uniq
75
+
76
+ output patient_ids: all_patient_ids.join(',')
77
+ end
78
+ end
79
+
80
+ USCoreTestKit::USCoreV501::USCoreTestSuite.groups.each do |group|
81
+ test_group = group.ancestors[1]
82
+ id = test_group.id
83
+
84
+ group_config = {}
85
+ if test_group.respond_to?(:metadata) && test_group.metadata.delayed?
86
+ test_group.children.reject! { |child| child.include? USCoreTestKit::SearchTest }
87
+ group_config[:options] = { read_all_resources: true }
88
+ end
89
+
90
+ group(from: id, exclude_optional: true, config: group_config)
91
+ end
92
+ end
93
+ end
@@ -11,11 +11,10 @@ module ONCCertificationG10TestKit
11
11
  # Background
12
12
 
13
13
  The Invalid AUD Sequence verifies that a SMART Launch Sequence,
14
- specifically the [Standalone
15
- Launch](http://hl7.org/fhir/smart-app-launch/1.0.0/index.html#standalone-launch-sequence)
16
- Sequence, does not work in the case where the client sends an invalid FHIR
17
- server as the `aud` parameter during launch. This must fail to ensure that
18
- a genuine bearer token is not leaked to a counterfit resource server.
14
+ specifically the Standalone Launch Sequence, does not work in the case
15
+ where the client sends an invalid FHIR server as the `aud` parameter
16
+ during launch. This must fail to ensure that a genuine bearer token is not
17
+ leaked to a counterfit resource server.
19
18
 
20
19
  This test is not included as part of a regular SMART Launch Sequence
21
20
  because it requires the browser of the user to be redirected to the
@@ -28,6 +27,11 @@ module ONCCertificationG10TestKit
28
27
  Note that this test will launch a new browser window. The user is required
29
28
  to 'Attest' in the Inferno user interface after the launch does not
30
29
  succeed, if the server does not return an error code.
30
+
31
+ * [Standalone Launch Sequence
32
+ (STU1)](http://hl7.org/fhir/smart-app-launch/1.0.0/index.html#standalone-launch-sequence)
33
+ * [Standalone Launch
34
+ (STU2)](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
31
35
  )
32
36
  id :g10_smart_invalid_aud
33
37
  run_as_group
@@ -87,6 +91,47 @@ module ONCCertificationG10TestKit
87
91
  :smart_authorization_url
88
92
 
89
93
  test from: :smart_app_redirect do
94
+ required_suite_options smart_app_launch_version: 'smart_app_launch_1'
95
+
96
+ input :client_secret,
97
+ name: :standalone_client_secret,
98
+ title: 'Standalone Client Secret',
99
+ description: 'Client Secret provided during registration of Inferno as a standalone application'
100
+
101
+ def aud
102
+ 'https://inferno.healthit.gov/invalid_aud'
103
+ end
104
+
105
+ def wait_message(auth_url)
106
+ %(
107
+ Inferno will redirect you to an external website for authorization.
108
+ **It is expected this will fail**. If the server does not return to
109
+ Inferno automatically, but does provide an error message, you may
110
+ return to Inferno and confirm that an error was presented in this
111
+ window.
112
+
113
+ * [Perform Invalid Launch](#{auth_url})
114
+ * [Attest launch
115
+ failed](#{Inferno::Application['base_url']}/custom/smart/redirect?state=#{state}&confirm_fail=true)
116
+ )
117
+ end
118
+ end
119
+
120
+ test from: :smart_app_redirect_stu2 do
121
+ required_suite_options smart_app_launch_version: 'smart_app_launch_2'
122
+
123
+ config(
124
+ inputs: {
125
+ use_pkce: {
126
+ default: 'true',
127
+ locked: true
128
+ },
129
+ pkce_code_challenge_method: {
130
+ locked: true
131
+ }
132
+ }
133
+ )
134
+
90
135
  input :client_secret,
91
136
  name: :standalone_client_secret,
92
137
  title: 'Standalone Client Secret',
@@ -0,0 +1,94 @@
1
+ module ONCCertificationG10TestKit
2
+ class SMARTEHRPatientLaunchGroup < SMARTAppLaunch::EHRLaunchGroup
3
+ title 'EHR Launch with Patient Scopes'
4
+ description %(
5
+ # Background
6
+
7
+ If an application launched from an EHR requests and is granted a clinical
8
+ scope restricted to a single patient, the EHR SHALL establish a patient in
9
+ context.
10
+
11
+ # Test Methodology
12
+
13
+ Inferno will attempt an EHR Launch with a clinical scope restricted to a
14
+ single patient and verify that a patient id is received.
15
+
16
+ For more information on the #{title}
17
+
18
+ * [Apps that launch from the
19
+ EHR](http://hl7.org/fhir/smart-app-launch/STU2/scopes-and-launch-context.html#apps-that-launch-from-the-ehr)
20
+ )
21
+ id :g10_ehr_patient_launch
22
+ run_as_group
23
+
24
+ config(
25
+ inputs: {
26
+ client_id: {
27
+ name: :ehr_patient_client_id
28
+ },
29
+ client_secret: {
30
+ name: :ehr_patient_client_secret
31
+ },
32
+ requested_scopes: {
33
+ name: :ehr_patient_requested_scopes,
34
+ default: 'launch openid fhirUser offline_access patient/Patient.read',
35
+ locked: true
36
+ },
37
+ code: {
38
+ name: :ehr_patient_code
39
+ },
40
+ state: {
41
+ name: :ehr_patient_state
42
+ },
43
+ launch: {
44
+ name: :ehr_patient_launch
45
+ },
46
+ smart_credentials: {
47
+ name: :ehr_patient_smart_credentials
48
+ },
49
+ smart_authorization_url: {
50
+ title: 'OAuth 2.0 Authorize Endpoint',
51
+ description: 'OAuth 2.0 Authorize Endpoint provided during the EHR launch'
52
+ },
53
+ smart_token_url: {
54
+ title: 'OAuth 2.0 Token Endpoint',
55
+ description: 'OAuth 2.0 Token Endpoint provided during the EHR launch'
56
+ }
57
+ },
58
+ outputs: {
59
+ launch: { name: :ehr_patient_launch },
60
+ code: { name: :ehr_patient_code },
61
+ token_retrieval_time: { name: :ehr_patient_token_retrieval_time },
62
+ state: { name: :ehr_patient_state },
63
+ id_token: { name: :ehr_patient_id_token },
64
+ refresh_token: { name: :ehr_patient_refresh_token },
65
+ access_token: { name: :ehr_patient_access_token },
66
+ expires_in: { name: :ehr_patient_expires_in },
67
+ patient_id: { name: :ehr_patient_patient_id },
68
+ encounter_id: { name: :ehr_patient_encounter_id },
69
+ received_scopes: { name: :ehr_patient_received_scopes },
70
+ intent: { name: :ehr_patient_intent },
71
+ smart_credentials: { name: :ehr_patient_smart_credentials }
72
+ },
73
+ requests: {
74
+ redirect: { name: :ehr_patient_redirect },
75
+ token: { name: :ehr_patient_token }
76
+ }
77
+ )
78
+
79
+ input_order :url,
80
+ :ehr_patient_client_id,
81
+ :ehr_patient_client_secret,
82
+ :smart_authorization_url,
83
+ :smart_token_url,
84
+ :authorization_method
85
+
86
+ test from: :g10_patient_context,
87
+ config: {
88
+ inputs: {
89
+ patient_id: { name: :ehr_patient_patient_id },
90
+ smart_credentials: { name: :ehr_patient_smart_credentials }
91
+ }
92
+ }
93
+ end
94
+ end
@@ -0,0 +1,94 @@
1
+ module ONCCertificationG10TestKit
2
+ class SMARTEHRPatientLaunchGroupSTU2 < SMARTAppLaunch::EHRLaunchGroupSTU2
3
+ title 'EHR Launch with Patient Scopes'
4
+ description %(
5
+ # Background
6
+
7
+ If an application launched from an EHR requests and is granted a clinical
8
+ scope restricted to a single patient, the EHR SHALL establish a patient in
9
+ context.
10
+
11
+ # Test Methodology
12
+
13
+ Inferno will attempt an EHR Launch with a clinical scope restricted to a
14
+ single patient and verify that a patient id is received.
15
+
16
+ For more information on the #{title}
17
+
18
+ * [Apps that launch from the
19
+ EHR](http://hl7.org/fhir/smart-app-launch/STU2/scopes-and-launch-context.html#apps-that-launch-from-the-ehr)
20
+ )
21
+ id :g10_ehr_patient_launch_stu2
22
+ run_as_group
23
+
24
+ config(
25
+ inputs: {
26
+ client_id: {
27
+ name: :ehr_patient_client_id
28
+ },
29
+ client_secret: {
30
+ name: :ehr_patient_client_secret
31
+ },
32
+ requested_scopes: {
33
+ name: :ehr_patient_requested_scopes,
34
+ default: 'launch openid fhirUser offline_access patient/Patient.rs',
35
+ locked: true
36
+ },
37
+ code: {
38
+ name: :ehr_patient_code
39
+ },
40
+ state: {
41
+ name: :ehr_patient_state
42
+ },
43
+ launch: {
44
+ name: :ehr_patient_launch
45
+ },
46
+ smart_credentials: {
47
+ name: :ehr_patient_smart_credentials
48
+ },
49
+ smart_authorization_url: {
50
+ title: 'OAuth 2.0 Authorize Endpoint',
51
+ description: 'OAuth 2.0 Authorize Endpoint provided during the EHR launch'
52
+ },
53
+ smart_token_url: {
54
+ title: 'OAuth 2.0 Token Endpoint',
55
+ description: 'OAuth 2.0 Token Endpoint provided during the EHR launch'
56
+ }
57
+ },
58
+ outputs: {
59
+ launch: { name: :ehr_patient_launch },
60
+ code: { name: :ehr_patient_code },
61
+ token_retrieval_time: { name: :ehr_patient_token_retrieval_time },
62
+ state: { name: :ehr_patient_state },
63
+ id_token: { name: :ehr_patient_id_token },
64
+ refresh_token: { name: :ehr_patient_refresh_token },
65
+ access_token: { name: :ehr_patient_access_token },
66
+ expires_in: { name: :ehr_patient_expires_in },
67
+ patient_id: { name: :ehr_patient_patient_id },
68
+ encounter_id: { name: :ehr_patient_encounter_id },
69
+ received_scopes: { name: :ehr_patient_received_scopes },
70
+ intent: { name: :ehr_patient_intent },
71
+ smart_credentials: { name: :ehr_patient_smart_credentials }
72
+ },
73
+ requests: {
74
+ redirect: { name: :ehr_patient_redirect },
75
+ token: { name: :ehr_patient_token }
76
+ }
77
+ )
78
+
79
+ input_order :url,
80
+ :ehr_patient_client_id,
81
+ :ehr_patient_client_secret,
82
+ :smart_authorization_url,
83
+ :smart_token_url,
84
+ :authorization_method
85
+
86
+ test from: :g10_patient_context,
87
+ config: {
88
+ inputs: {
89
+ patient_id: { name: :ehr_patient_patient_id },
90
+ smart_credentials: { name: :ehr_patient_smart_credentials }
91
+ }
92
+ }
93
+ end
94
+ end