davinci_dtr_test_kit 0.11.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/davinci_dtr_test_kit/auth_groups/token_request_test.rb +1 -1
  3. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_questionnaire_package_request_test.rb +52 -0
  4. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_conformance_test.rb +15 -0
  5. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_correctness_test.rb +30 -0
  6. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_launch_attestation_test.rb +28 -0
  7. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_prepopulation_attestation_test.rb +30 -0
  8. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_prepopulation_override_attestation_test.rb +27 -0
  9. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_questionnaire_workflow_group.rb +91 -0
  10. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_rendering_enabled_questions_attestation_test.rb +30 -0
  11. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_store_attestation_test.rb +29 -0
  12. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{dtr_dinner_questionnaire_package_request_test.rb → dtr_smart_app_dinner_questionnaire_package_request_test.rb} +5 -5
  13. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{prepopulation_attestation_test.rb → dtr_smart_app_prepopulation_attestation_test.rb} +2 -2
  14. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{prepopulation_override_attestation_test.rb → dtr_smart_app_prepopulation_override_attestation_test.rb} +2 -2
  15. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{dtr_questionnaire_response_save_test.rb → dtr_smart_app_questionnaire_response_save_test.rb} +2 -2
  16. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_smart_app_questionnaire_workflow_group.rb +13 -13
  17. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/{rendering_enabled_questions_attestation_test.rb → dtr_smart_app_rendering_enabled_questions_attestation_test.rb} +2 -2
  18. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_package_request_validation_test.rb +1 -1
  19. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_basic_conformance_test.rb +3 -7
  20. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_pre_population_test.rb +9 -5
  21. data/lib/davinci_dtr_test_kit/cql_test.rb +182 -137
  22. data/lib/davinci_dtr_test_kit/docs/dtr_full_ehr_suite_description_v201.md +127 -0
  23. data/lib/davinci_dtr_test_kit/docs/dtr_light_ehr_suite_description_v201.md +29 -0
  24. data/lib/davinci_dtr_test_kit/dtr_full_ehr_suite.rb +4 -12
  25. data/lib/davinci_dtr_test_kit/dtr_light_ehr_suite.rb +38 -25
  26. data/lib/davinci_dtr_test_kit/dtr_options.rb +7 -0
  27. data/lib/davinci_dtr_test_kit/dtr_questionnaire_response_validation.rb +118 -75
  28. data/lib/davinci_dtr_test_kit/dtr_smart_app_suite.rb +6 -3
  29. data/lib/davinci_dtr_test_kit/fixture_loader.rb +6 -84
  30. data/lib/davinci_dtr_test_kit/fixtures.rb +43 -48
  31. data/lib/davinci_dtr_test_kit/mock_auth_server.rb +101 -18
  32. data/lib/davinci_dtr_test_kit/mock_ehr.rb +32 -24
  33. data/lib/davinci_dtr_test_kit/mock_payer.rb +41 -64
  34. data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_next_questionnaire_expressions_test.rb +2 -2
  35. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_request_validation_test.rb +11 -19
  36. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_bundles_validation_test.rb +6 -6
  37. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_search_validation_test.rb +6 -6
  38. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_validation_test.rb +17 -18
  39. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_request_validation_test.rb +6 -7
  40. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_response_validation_test.rb +3 -1
  41. data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_request_validation_test.rb +10 -21
  42. data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_response_validation_test.rb +7 -13
  43. data/lib/davinci_dtr_test_kit/tags.rb +1 -0
  44. data/lib/davinci_dtr_test_kit/urls.rb +13 -10
  45. data/lib/davinci_dtr_test_kit/validation_test.rb +8 -9
  46. data/lib/davinci_dtr_test_kit/version.rb +1 -1
  47. data/lib/davinci_dtr_test_kit.rb +2 -2
  48. metadata +37 -12
  49. data/lib/davinci_dtr_test_kit/client_groups/resp_assist_device/dtr_full_ehr_questionnaire_workflow_group.rb +0 -19
  50. /data/lib/davinci_dtr_test_kit/fixtures/{pre_populated_questionnaire_response.json → respiratory_assist_device/pre_populated_questionnaire_response.json} +0 -0
  51. /data/lib/davinci_dtr_test_kit/fixtures/{questionnaire_package.json → respiratory_assist_device/questionnaire_package.json} +0 -0
@@ -14,25 +14,21 @@ module DaVinciDTRTestKit
14
14
  end
15
15
 
16
16
  def cqf_reference_libraries
17
- @cqf_reference_libraries ||= Set.new
17
+ scratch[:cqf_reference_libraries] ||= Set.new
18
18
  end
19
19
 
20
20
  def library_urls
21
- @@library_urls ||= Set.new
21
+ scratch[:library_urls] ||= Set.new
22
22
  end
23
23
 
24
24
  def library_names
25
- @@library_names ||= Set.new
25
+ scratch[:library_names] ||= Set.new
26
26
  end
27
27
 
28
28
  def found_questionnaire
29
29
  @found_questionnaire ||= false
30
30
  end
31
31
 
32
- def found_bad_library_reference
33
- @@found_bad_library_reference ||= false
34
- end
35
-
36
32
  def found_duplicate_library_name
37
33
  @found_duplicate_library_name ||= false
38
34
  end
@@ -52,97 +48,63 @@ module DaVinciDTRTestKit
52
48
  extension_presence.each_key { |k| extension_presence[k] = false }
53
49
  end
54
50
 
55
- def questionnaire_extensions_test(response)
56
- resource = process_response(response)
57
- assert !resource.nil?, 'Response is null or not a valid type.'
51
+ def evaluate_responses_extensions(resource)
58
52
  found_questionnaire = false
59
- if resource.instance_of? Array
60
- resource.each do |individual_resource|
61
- next unless individual_resource.resourceType == 'QuestionnaireResponse'
53
+ resource.each do |individual_resource|
54
+ next unless individual_resource.resourceType == 'QuestionnaireResponse'
62
55
 
63
- individual_resource.contained.each_with_index do |questionnaire, q_index|
64
- # Do out put parameters have a bundle?
65
- next unless questionnaire.resourceType == 'Questionnaire'
56
+ individual_resource.contained.each_with_index do |questionnaire, q_index|
57
+ # Do out put parameters have a bundle?
58
+ next unless questionnaire.resourceType == 'Questionnaire'
66
59
 
67
- # check the libraries first so references in questionnaires can be checked after
68
- found_questionnaire = true
69
- check_questionnaire_extensions(questionnaire, q_index)
70
- end
60
+ # check the libraries first so references in questionnaires can be checked after
61
+ found_questionnaire = true
62
+ check_questionnaire_extensions(questionnaire, q_index)
71
63
  end
72
- elsif resource.resourceType == 'Parameters'
73
- resource.parameter.each do |param|
74
- # Do out put parameters have a bundle?
75
- next unless param.resource.resourceType == 'Bundle'
64
+ end
65
+ found_questionnaire
66
+ end
76
67
 
77
- param.resource.entry.each_with_index do |entry, q_index|
78
- # check questionnaire extensions
79
- next unless entry.resource.resourceType == 'Questionnaire'
68
+ def evaluate_parameters_extensions(resource)
69
+ found_questionnaire = false
70
+ resource.parameter.each do |param|
71
+ # Do out put parameters have a bundle?
72
+ next unless param.resource.resourceType == 'Bundle'
80
73
 
81
- found_questionnaire = true
82
- check_questionnaire_extensions(entry.resource, q_index)
83
- ## NEED TO FIGURE OUT HOW TO FAIL TEST WHEN POORLY FORMATTED EXPRESSIONS FOUND
84
- end
74
+ param.resource.entry.each_with_index do |entry, q_index|
75
+ # check questionnaire extensions
76
+ next unless entry.resource.resourceType == 'Questionnaire'
77
+
78
+ found_questionnaire = true
79
+ check_questionnaire_extensions(entry.resource, q_index)
80
+ ## NEED TO FIGURE OUT HOW TO FAIL TEST WHEN POORLY FORMATTED EXPRESSIONS FOUND
85
81
  end
86
82
  end
87
- check_library_references
88
- assert found_questionnaire, 'No questionnaires found.'
89
- assert extension_presence.value?(true), 'No extensions found. Questionnaire must demonstrate prepopulation.'
90
- assert cql_presence['variable'], 'Variable expression logic not written in CQL.'
91
- assert cql_presence['launch_context'], 'Launch context expression logic not written in CQL.'
92
- assert cql_presence['pop_context'], 'Population context expression logic not written in CQL.'
83
+ found_questionnaire
93
84
  end
94
85
 
95
- def questionnaire_items_test(response, final_cql_test)
86
+ # extensions
87
+ def questionnaire_extensions_test(response)
96
88
  resource = process_response(response)
97
89
  assert !resource.nil?, 'Response is null or not a valid type.'
98
- found_bundle = found_questionnaire = false
99
- # are extensions present in any questionnaire?
90
+ found_questionnaire = false
100
91
  if resource.instance_of? Array
101
- resource.each_with_index do |individual_resource, q_index|
102
- next unless individual_resource.resourceType == 'QuestionnaireResponse'
103
-
104
- individual_resource.contained.each do |questionnaire|
105
- # Do out put parameters have a bundle?
106
- next unless questionnaire.resourceType == 'Questionnaire'
107
-
108
- # check the libraries first so references in questionnaires can be checked after
109
- found_questionnaire = true
110
- check_questionnaire_items(questionnaire, q_index)
111
- end
112
- end
92
+ found_questionnaire = evaluate_responses_extensions(resource)
113
93
  elsif resource.resourceType == 'Parameters'
114
- resource.parameter.each do |param|
115
- # Do out put parameters have a bundle?
116
- next unless param.resource.resourceType == 'Bundle'
117
-
118
- found_bundle = true
119
- # check the libraries first so references in questionnaires can be checked after
120
- param.resource.entry.each_with_index do |entry, q_index|
121
- if entry.resource.resourceType == 'Questionnaire'
122
- found_questionnaire = true
123
- check_questionnaire_items(entry.resource, q_index)
124
- end
125
- end
126
- end
127
- assert found_bundle, 'No questionnaire bundles found.'
128
- end
129
- begin
130
- assert found_questionnaire, 'No questionnaires found.'
131
- assert !found_non_cql_expression, 'Found non-cql expression.'
132
- assert !found_bad_library_reference, 'Found expression with no or incorrect reference to library name.'
133
- assert extension_presence.value?(true), 'No extensions found. Questionnaire must demonstrate prepopulation.'
134
- assert cql_presence['init_expression'], 'Initial expression logic not written in CQL.'
135
- assert cql_presence['candidate_expression'], 'Candidate expression logic not written in CQL.'
136
- assert cql_presence['context_expression'], 'Context expression logic not written in CQL.'
137
- ensure
138
- reset_cql_tests if final_cql_test
94
+ found_questionnaire = evaluate_parameters_extensions(resource)
139
95
  end
96
+ check_library_references
97
+ assert found_questionnaire, 'No questionnaires found.'
98
+ assert extension_presence.value?(true), 'No extensions found. Questionnaire must demonstrate prepopulation.'
99
+ assert cql_presence['variable'], 'Variable expression logic not written in CQL.'
100
+ assert cql_presence['launch_context'], 'Launch context expression logic not written in CQL.'
101
+ assert cql_presence['pop_context'], 'Population context expression logic not written in CQL.'
140
102
  end
141
103
 
142
104
  def check_questionnaire_extensions(questionnaire, q_index)
143
105
  # are extensions present in this questionnaire?
144
106
  found_launch_context = found_variable = found_pop_context = found_cqf_lib = false
145
- cqf_count = 0
107
+ cqf_count = total_cqf_libs(questionnaire.extension)
146
108
  misformatted_expressions = []
147
109
  questionnaire.extension.each_with_index do |extension, index|
148
110
  if extension.url == 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext'
@@ -165,13 +127,77 @@ module DaVinciDTRTestKit
165
127
  end
166
128
  next unless extension.url == 'http://hl7.org/fhir/StructureDefinition/cqf-library'
167
129
 
168
- cqf_count += 1
169
130
  cqf_reference_libraries.add(extension.valueCanonical)
170
131
  found_cqf_lib = true
171
132
  extension_presence['found_min_cqf_lib'] = true
172
133
 
173
134
  check_for_cql(extension, '', index, q_index, extension.url)
174
135
  end
136
+ add_launch_context_messages(found_launch_context, found_variable, found_pop_context, found_cqf_lib, q_index)
137
+ return if cqf_count < 1
138
+
139
+ add_formatting_messages(misformatted_expressions, q_index)
140
+ assert misformatted_expressions.compact.empty?, 'Expression in questionnaire misformatted.'
141
+ end
142
+
143
+ # items
144
+ def evaluate_responses_items(resource)
145
+ found_questionnaire = false
146
+ resource.each_with_index do |individual_resource, q_index|
147
+ next unless individual_resource.resourceType == 'QuestionnaireResponse'
148
+
149
+ individual_resource.contained.each do |questionnaire|
150
+ next unless questionnaire.resourceType == 'Questionnaire'
151
+
152
+ # check the libraries first so references in questionnaires can be checked after
153
+ found_questionnaire = true
154
+ check_questionnaire_items(questionnaire, q_index)
155
+ end
156
+ end
157
+ found_questionnaire
158
+ end
159
+
160
+ def evaluate_parameters_items(resource)
161
+ found_bundle = found_questionnaire = false
162
+ resource.parameter.each do |param|
163
+ next unless param.resource.resourceType == 'Bundle'
164
+
165
+ found_bundle = true
166
+ # check the libraries first so references in questionnaires can be checked after
167
+ param.resource.entry.each_with_index do |entry, q_index|
168
+ if entry.resource.resourceType == 'Questionnaire'
169
+ found_questionnaire = true
170
+ check_questionnaire_items(entry.resource, q_index)
171
+ end
172
+ end
173
+ end
174
+ [found_bundle, found_questionnaire]
175
+ end
176
+
177
+ def questionnaire_items_test(response, final_cql_test)
178
+ resource = process_response(response)
179
+ assert !resource.nil?, 'Response is null or not a valid type.'
180
+ found_questionnaire = false
181
+ # are extensions present in any questionnaire?
182
+ if resource.instance_of? Array
183
+ found_questionnaire = evaluate_responses_items(resource)
184
+ elsif resource.resourceType == 'Parameters'
185
+ found_bundle, found_questionnaire = evaluate_parameters_items(resource)
186
+ assert found_bundle, 'No questionnaire bundles found.'
187
+ end
188
+ begin
189
+ assert found_questionnaire, 'No questionnaires found.'
190
+ assert !found_non_cql_expression, 'Found non-cql expression.'
191
+ assert extension_presence.value?(true), 'No extensions found. Questionnaire must demonstrate prepopulation.'
192
+ assert cql_presence['init_expression'], 'Initial expression logic not written in CQL.'
193
+ assert cql_presence['candidate_expression'], 'Candidate expression logic not written in CQL.'
194
+ assert cql_presence['context_expression'], 'Context expression logic not written in CQL.'
195
+ ensure
196
+ reset_cql_tests if final_cql_test
197
+ end
198
+ end
199
+
200
+ def add_launch_context_messages(found_launch_context, found_variable, found_pop_context, found_cqf_lib, q_index)
175
201
  unless found_launch_context
176
202
  messages << { type: 'info',
177
203
  message: format_markdown("[questionnaire #{q_index + 1}] included no launch context.") }
@@ -186,19 +212,44 @@ module DaVinciDTRTestKit
186
212
  message: format_markdown("[questionnaire #{q_index + 1}]
187
213
  included no item population context.") }
188
214
  end
189
- unless found_cqf_lib
190
- messages << { type: 'info',
191
- message: format_markdown("[questionnaire #{q_index + 1}]
215
+ return if found_cqf_lib
216
+
217
+ messages << { type: 'info',
218
+ message: format_markdown("[questionnaire #{q_index + 1}]
192
219
  included no cqf library.") }
193
- end
194
- return if cqf_count < 1
220
+ end
195
221
 
222
+ def add_formatting_messages(misformatted_expressions, q_index)
196
223
  misformatted_expressions.compact.each do |idx|
197
224
  messages << { type: 'info',
198
225
  message: format_markdown("[expression #{idx + 1}] in [questionnaire #{q_index + 1}]
199
226
  does not begin with a reference to an included library name.") }
200
227
  end
201
- assert misformatted_expressions.compact.empty?, 'Expression in questionnaire misformatted.'
228
+ end
229
+
230
+ def total_cqf_libs(extensions)
231
+ cqf_count = 0
232
+ extensions.each do |extension|
233
+ next unless extension.url == 'http://hl7.org/fhir/StructureDefinition/cqf-library'
234
+
235
+ cqf_count += 1
236
+ end
237
+ cqf_count
238
+ end
239
+
240
+ def add_item_messages(found_item_expressions, q_index)
241
+ unless found_item_expressions['found_candidate_expression']
242
+ messages << { type: 'info',
243
+ message: format_markdown("[questionnaire #{q_index + 1}] included no candidate expression.") }
244
+ end
245
+ unless found_item_expressions['found_init_expression']
246
+ messages << { type: 'info',
247
+ message: format_markdown("[questionnaire #{q_index + 1}] included no initial expression.") }
248
+ end
249
+ return if found_item_expressions['found_context_expression']
250
+
251
+ messages << { type: 'info',
252
+ message: format_markdown("[questionnaire #{q_index + 1}] included no context expression.") }
202
253
  end
203
254
 
204
255
  def check_questionnaire_items(questionnaire, q_index)
@@ -206,13 +257,9 @@ module DaVinciDTRTestKit
206
257
  found_item_expressions = { 'found_init_expression' => false,
207
258
  'found_candidate_expression' => false,
208
259
  'found_context_expression' => false }
209
- cqf_count = 0
260
+ cqf_count = total_cqf_libs(questionnaire.extension)
210
261
  misformatted_expressions = []
211
- questionnaire.extension.each do |extension|
212
- next unless extension.url == 'http://hl7.org/fhir/StructureDefinition/cqf-library'
213
262
 
214
- cqf_count += 1
215
- end
216
263
  # check questionnaire items
217
264
  questionnaire.item.each_with_index do |item, index|
218
265
  misformatted_expressions.concat(check_nested_items(item, index, q_index, found_item_expressions, item.linkId))
@@ -222,18 +269,8 @@ module DaVinciDTRTestKit
222
269
  index, q_index, found_item_expressions, item.linkId)
223
270
  end
224
271
  end
225
- unless found_item_expressions['found_candidate_expression']
226
- messages << { type: 'info',
227
- message: format_markdown("[questionnaire #{q_index + 1}] included no candidate expression.") }
228
- end
229
- unless found_item_expressions['found_init_expression']
230
- messages << { type: 'info',
231
- message: format_markdown("[questionnaire #{q_index + 1}] included no initial expression.") }
232
- end
233
- unless found_item_expressions['found_context_expression']
234
- messages << { type: 'info',
235
- message: format_markdown("[questionnaire #{q_index + 1}] included no context expression.") }
236
- end
272
+ add_item_messages(found_item_expressions, q_index)
273
+ # only care about formatting when there are multiple cqf libs
237
274
  return if cqf_count < 1
238
275
 
239
276
  misformatted_expressions.compact.to_set.each do |idx|
@@ -244,6 +281,33 @@ module DaVinciDTRTestKit
244
281
  assert misformatted_expressions.compact.to_set.empty?, 'Expression in questionnaire misformatted.'
245
282
  end
246
283
 
284
+ def evaluate_library(entry, lib_index)
285
+ found_cql = found_elm = false
286
+ entry.resource.content.each do |content|
287
+ if content.data.nil?
288
+ messages << { type: 'info',
289
+ message: format_markdown("[library #{lib_index + 1}] content element included no data.") }
290
+ end
291
+ if content.contentType == 'text/cql'
292
+ found_cql = true
293
+ elsif content.contentType == 'application/elm+json'
294
+ found_elm = true
295
+ else
296
+ messages << { type: 'info',
297
+ message: format_markdown("[library #{lib_index + 1}] has non-cql/elm content.") }
298
+ true
299
+ end
300
+ next unless library_names.include? entry.resource.name
301
+
302
+ found_duplicate_library_name = true
303
+ messages << { type: 'info', message: format_markdown("[library #{lib_index + 1}] has a name,
304
+ #{entry.resource.name}, that is already included in the bundle.") }
305
+ assert !found_duplicate_library_name, 'Found duplicate library names - all names must be unique.'
306
+ end
307
+ assert found_cql, "[library #{lib_index + 1}] does not include CQL."
308
+ assert found_elm, "[library #{lib_index + 1}] does not include ELM."
309
+ end
310
+
247
311
  def check_libraries(payer_response)
248
312
  resource = process_response(payer_response)
249
313
  assert !resource.nil?, 'Response is null or not a valid type.'
@@ -259,32 +323,9 @@ module DaVinciDTRTestKit
259
323
  next unless entry.resource.resourceType == 'Library'
260
324
 
261
325
  found_libraries = true
262
- found_cql = found_elm = false
263
326
  library_urls.add(entry.resource.url) unless entry.resource.url.nil?
264
- entry.resource.content.each do |content|
265
- if content.data.nil?
266
- messages << { type: 'info',
267
- message: format_markdown("[library #{index + 1}] content element included no data.") }
268
- end
269
- if content.contentType == 'text/cql'
270
- found_cql = true
271
- elsif content.contentType == 'application/elm+json'
272
- found_elm = true
273
- else
274
- messages << { type: 'info',
275
- message: format_markdown("[library #{index + 1}] has non-cql/elm content.") }
276
- true
277
- end
278
- next unless library_names.include? entry.resource.name
279
-
280
- found_duplicate_library_name = true
281
- messages << { type: 'info', message: format_markdown("[library #{index + 1}] has a name,
282
- #{entry.resource.name}, that is already included in the bundle.") }
283
- assert !found_duplicate_library_name, 'Found duplicate library names - all names must be unique.'
284
- end
327
+ evaluate_library(entry, index)
285
328
  library_names.add(entry.resource.name)
286
- assert found_cql, "[library #{index + 1}] does not include CQL."
287
- assert found_elm, "[library #{index + 1}] does not include ELM."
288
329
  end
289
330
  assert found_libraries, 'No Libraries found.'
290
331
  end
@@ -364,21 +405,25 @@ module DaVinciDTRTestKit
364
405
  end
365
406
  end
366
407
 
408
+ def q_responses(response)
409
+ questionnaire_responses = []
410
+ response.each do |resource|
411
+ fhir_resource = FHIR.from_contents(resource.response_body)
412
+ questionnaire_responses << fhir_resource if fhir_resource.resourceType == 'QuestionnaireResponse'
413
+ next unless resource.instance_of? Inferno::Entities::Request
414
+
415
+ if fhir_resource.resourceType == 'Questionnaire' || fhir_resource.resourceType == 'Parameters'
416
+ return fhir_resource
417
+ end
418
+ end
419
+ questionnaire_responses
420
+ end
421
+
367
422
  def process_response(response)
368
423
  if response.instance_of?(FHIR::Parameters) || response.instance_of?(FHIR::QuestionnaireResponse)
369
424
  return response
370
425
  elsif response.instance_of? Array
371
- questionnaire_responses = []
372
- response.each do |resource|
373
- fhir_resource = FHIR.from_contents(resource.response_body)
374
- questionnaire_responses << fhir_resource if fhir_resource.resourceType == 'QuestionnaireResponse'
375
- next unless resource.instance_of? Inferno::Entities::Request
376
-
377
- if fhir_resource.resourceType == 'Questionnaire' || fhir_resource.resourceType == 'Parameters'
378
- return fhir_resource
379
- end
380
- end
381
- return questionnaire_responses
426
+ return q_responses(response)
382
427
  end
383
428
 
384
429
  nil
@@ -0,0 +1,127 @@
1
+ The Da Vinci DTR Test Kit Full EHR Suite validates the conformance of SMART apps
2
+ to the STU 2 version of the HL7® FHIR®
3
+ [Da Vinci Documentation Templates and Rules (DTR) Implementation Guide](https://hl7.org/fhir/us/davinci-dtr/STU2/).
4
+
5
+ ## Scope
6
+
7
+ These tests are a **DRAFT** intended to allow app implementers to perform
8
+ preliminary checks of their systems against DTR IG requirements and [provide
9
+ feedback](https://github.com/inferno-framework/davinci-dtr-test-kit/issues)
10
+ on the tests. Future versions of these tests may validate other
11
+ requirements and may change the test validation logic.
12
+
13
+ ## Test Methodology
14
+
15
+ Inferno will simulate a DTR payer server that will response to
16
+ requests for questionnaires for the EHR under test to interact with.
17
+ The EHR will be expected to initiate requests to Inferno to elicit responses. Over the
18
+ course of these interactions, Inferno will seek to observe conformant handling of
19
+ DTR workflows and requirements around the retrieval, completion, and storage of
20
+ questionnaires.
21
+
22
+ Tests within this suite are associated with specific questionnaires that the EHR will
23
+ demonstrate completion of. In each case, the EHR under test will initiate a request to
24
+ the payer server simulated by Inferno for a questionnaire using the
25
+ `$questionnaire-package` operation. Inferno will always return the specific questionnaire
26
+ for the test being executed regardless of the input provided by the EHR, though it must
27
+ be conformant. The EHR will then be asked to complete the questionnaire, including
28
+ - Pre-populating answers based on directives in the questionnaire
29
+ - Rendering the questionnaire for users and allowing them to make additional updates.
30
+ These tests can include specific directions on details to include in the completed
31
+ questionnaire.
32
+ - Storing the completed questionnaire for future use as a FHIR QuestionnaireResponse.
33
+
34
+ EHRs will be required to complete all questionnaires in the suite, which in aggregate
35
+ contain all questionnaire features that apps must support. Currently, the suite includes
36
+ one questionnaire:
37
+ 1. A fictious "dinner" questionnaire created for these tests. It tests basic
38
+ item rendering and pre-population.
39
+ Additional questionnaires will be added in the future.
40
+
41
+ All requests sent by the app will be checked
42
+ for conformance to the DTR IG requirements individually and used in aggregate to determine
43
+ whether required features and functionality are present. HL7® FHIR® resources are
44
+ validated with the Java validator using `tx.fhir.org` as the terminology server.
45
+
46
+ ## Running the Tests
47
+
48
+ ### Quick Start
49
+
50
+ In order to run these tests, EHRs must be configured to interact with Inferno's simulated
51
+ payer server endpoint. The endpoint will be `[URL prefix]/custom/dtr_full_ehr/fhir` where
52
+ `[URL prefix]` can be inferred from the URL of the test session which will be of the form
53
+ `[URL prefix]/dtr_full_ehr/[session id]`.
54
+
55
+ In order for Inferno to associate requests sent to locations under these base URLs with this session,
56
+ it needs to know the bearer token that the EHR will send on requests, for which
57
+ there are two options.
58
+
59
+ 1. If you want to choose your own bearer token, then
60
+ 1. Select the "2. Basic Workflows" test from the list on the left (or other target test).
61
+ 2. Click the '*Run All Tests*' button on the right.
62
+ 3. In the "access_token" field, enter the bearer token that will be sent by the client
63
+ under test (as part of the Authorization header - `Bearer <provided value>`).
64
+ 4. Click the '*Submit*' button at the bottom of the dialog.
65
+ 2. If you want to use a client_id to obtain an access token, then
66
+ 1. Click the '*Run All Tests*' button on the right.
67
+ 2. Provide the EHR's registered id "client_id" field of the input (NOTE, Inferno
68
+ doesn't support the registration API, so this must be obtained from another
69
+ system or configured manually).
70
+ 3. Click the '*Submit*' button at the bottom of the dialog.
71
+ 4. Make a token request that includes the specified client id to the
72
+ `[URL prefix]/custom/dtr_full_ehr/mock_auth/token` endpoint to get
73
+ an access token to use on the request of the requests.
74
+
75
+ In either case, the tests will continue from that point. Further executions of tests under
76
+ this session will also use the selected bearer token.
77
+
78
+ Note: authentication options for these tests have not been finalized and are subject to change.
79
+
80
+ ### Postman-based Demo
81
+
82
+ If you do not have a DTR Full EHR but would like to try the tests out, you can use
83
+ [this Postman collection](https://github.com/inferno-framework/davinci-dtr-test-kit/blob/main/config/DTR%20Full%20EHR%20Tests%20Postman%20Demo.postman_collection.json)
84
+ to make requests against Inferno. This does not include the capability to render and complete the
85
+ questionnaires, but does have samples of correctly and incorrectly completed QuestionnaireResponses.
86
+ To run the tests using this approach:
87
+
88
+ 1. Install [postman](https://www.postman.com/downloads/).
89
+ 1. Import [this Postman collection](https://github.com/inferno-framework/davinci-dtr-test-kit/blob/main/config/DTR%20Full%20EHR%20Tests%20Postman%20Demo.postman_collection.json).
90
+ 1. Start a Da Vinci DTR Full EHR Test Suite Session.
91
+ 1. Update the postman collection configuration variables found by opening the "DTR Full EHR
92
+ Tests Postman Demo" collection and selecting the "Variables" tab.
93
+ - **base_url**: corresponds to the where the test suite session is running. Defaults to
94
+ `inferno.healthit.gov`. If running in another location, see guidance on the "Overview" tab
95
+ of the postman collection.
96
+ - **access_token**: note the "Current value" (update if desired) for use later.
97
+ 1. Return to Inferno and in the test list at the left, select *2 Static Questionnaire Workflow*.
98
+ 1. Click the "Run All Tests" button in the upper right.
99
+ 1. Add the **access_token** configured in postman to the Inferno input with the same name
100
+ 1. Click the "Submit" button in Inferno.
101
+ 1. Attest that the EHR has launched its DTR workflow in Inferno by clicking the link for the **true** response.
102
+ 1. Once the next wait dialog has appeared within Inferno asking for a `$questionnaire-package`
103
+ request, use postman to submit the "Questionnaire Package for Dinner (Static)" request. Confirm
104
+ that the response that looks similar to the "Example Working Response" in postman
105
+ and click the link to continue the tests.
106
+ 1. Attest to the remainder of the tests as desired to get a sense for what is involved in testing
107
+ with an actual EHR implementation. To see what a valid QuestionnaireResponse looks like, see
108
+ the "Sample QuestionnaireResponse for Dinner (Static) ..." request in postman.
109
+
110
+ ## Limitations
111
+
112
+ The DTR IG is a complex specification and these tests currently validate conformance to only
113
+ a subset of IG requirements. Future versions of the test suite will test further
114
+ features. A few specific features of interest are listed below.
115
+
116
+ ### Heavy Reliance on Attestations
117
+
118
+ Currently, these test kits do not have access to the QuestionnaireResponse and so validation
119
+ that the EHR performed CQL calculations and generated a conformant QuestionnaireResponse
120
+ based on pre-population and manual answers is left to a user attestation rather than a
121
+ mechanical check. Some level of mechanical checks are expected to be added in the future.
122
+
123
+ ### Questionnaire Feature Coverage
124
+
125
+ Not all questionnaire features that are must support within the DTR IG are currently represented
126
+ in questionnaires tested by the IG. Adaptive questionnaires are a notable omission.
127
+ Additional questionnaires testing additional features will be added in the future.
@@ -0,0 +1,29 @@
1
+ The Da Vinci DTR Test Kit Light EHR Suite validates the conformance of SMART apps
2
+ to the STU 2 version of the HL7® FHIR®
3
+ [Da Vinci Documentation Templates and Rules (DTR) Implementation Guide](https://hl7.org/fhir/us/davinci-dtr/STU2/).
4
+
5
+ ## Scope
6
+
7
+ These tests are a **DRAFT** intended to allow app implementers to perform
8
+ preliminary checks of their systems against DTR IG requirements and [provide
9
+ feedback](https://github.com/inferno-framework/davinci-dtr-test-kit/issues)
10
+ on the tests. Future versions of these tests may validate other
11
+ requirements and may change the test validation logic.
12
+
13
+ ## Test Methodology
14
+
15
+ Inferno will simulate a DTR SMART App that will connect to the DTR Light EHR system under test. The tester will need to launch Inferno using either an EHR launch or a Standalone launch.
16
+
17
+ Once the connection between the DTR SMART App and the DTR Light EHR is established, tests within this suite check that the DTR Light EHR API is conformant to US Core and any other requirements outlined in the [Light DTR EHR Capability Statement](https://hl7.org/fhir/us/davinci-dtr/STU2/CapabilityStatement-light-dtr-ehr.html#root).
18
+
19
+ ## Running the Tests
20
+
21
+ If you would like to try out the tests but don't have a DTR payer server implementation, you can run these tests against the [public instance of the Inferno Reference Server](https://inferno.healthit.gov/reference-server/r4/) by using the Inferno Reference Server preset in the test suite.
22
+
23
+ In order to get the Inferno QA Reference Server to do an EHR launch, navigate to https://inferno.healthit.gov/reference-server/app/app-launch and use https://inferno.healthit.gov/custom/smart/launch as the App Launch URL.
24
+
25
+ ## Limitations
26
+
27
+ The DTR IG is a complex specification and these tests currently validate conformance to only
28
+ a subset of IG requirements. Future versions of the test suite will test further
29
+ features.
@@ -1,7 +1,7 @@
1
1
  require_relative 'ext/inferno_core/runnable'
2
2
  require_relative 'ext/inferno_core/record_response_route'
3
3
  require_relative 'ext/inferno_core/request'
4
- require_relative 'client_groups/resp_assist_device/dtr_full_ehr_questionnaire_workflow_group'
4
+ require_relative 'client_groups/dinner_static/dtr_full_ehr_questionnaire_workflow_group'
5
5
  require_relative 'auth_groups/oauth2_authentication_group'
6
6
  require_relative 'mock_payer'
7
7
  require_relative 'version'
@@ -9,19 +9,11 @@ require_relative 'version'
9
9
  module DaVinciDTRTestKit
10
10
  class DTRFullEHRSuite < Inferno::TestSuite
11
11
  extend MockPayer
12
+ extend MockAuthServer
12
13
 
13
14
  id :dtr_full_ehr
14
15
  title 'Da Vinci DTR Full EHR Test Suite'
15
- description %(
16
- # Da Vinci DTR Full EHR Test Suite
17
-
18
- This suite validates that an EHR or other application can act
19
- as a full DTR application requesting questionnaires from a
20
- payer server and using local data to complete and store them.
21
- Inferno will act as payer server returning questionnaires
22
- in response to queries from the system under test and validating
23
- that they can be completed as expected.
24
- )
16
+ description File.read(File.join(__dir__, 'docs', 'dtr_full_ehr_suite_description_v201.md'))
25
17
 
26
18
  version VERSION
27
19
 
@@ -74,6 +66,6 @@ module DaVinciDTRTestKit
74
66
  end
75
67
 
76
68
  group from: :oauth2_authentication
77
- group from: :dtr_full_ehr_questionnaire_workflow
69
+ group from: :dtr_full_ehr_static_dinner_questionnaire_workflow
78
70
  end
79
71
  end