davinci_dtr_test_kit 0.15.2 → 0.16.1

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/full_ehr_postman_dinner_order_example.json.erb +41 -0
  3. data/config/presets/full_ehr_postman_respiratory_device_example.json +1 -1
  4. data/config/presets/light_ehr_ref_server_prod.json +7 -10
  5. data/config/presets/payer_server_run_against_smart_app_tests.json +6 -3
  6. data/config/presets/smart_app_postman_dinner_order_example.json +8 -1
  7. data/config/presets/smart_app_postman_respiratory_device_example.json +1 -1
  8. data/lib/davinci_dtr_test_kit/certs/InfernoCA.key +52 -0
  9. data/lib/davinci_dtr_test_kit/certs/InfernoCA.pem +35 -0
  10. data/lib/davinci_dtr_test_kit/certs/TestKit.pem +32 -0
  11. data/lib/davinci_dtr_test_kit/certs/TestKitPrivateKey.key +28 -0
  12. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/custom/dtr_custom_next_question_response_validation_test.rb +95 -0
  13. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/custom/dtr_full_ehr_custom_adaptive_request_test.rb +87 -0
  14. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/custom/dtr_full_ehr_custom_adaptive_workflow_group.rb +136 -0
  15. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/custom/dtr_smart_app_custom_adaptive_request_test.rb +171 -0
  16. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/custom/dtr_smart_app_custom_adaptive_workflow_group.rb +146 -0
  17. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_adaptive_next_question_retrieval_group.rb +3 -3
  18. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_full_ehr_adaptive_dinner_workflow_group.rb +21 -11
  19. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_full_ehr_adaptive_initial_retrieval_group.rb +6 -6
  20. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_smart_app_adaptive_dinner_workflow_group.rb +5 -5
  21. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_smart_app_adaptive_initial_retrieval_group.rb +5 -5
  22. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire}/dtr_adaptive_next_question_request_test.rb +9 -24
  23. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire}/dtr_adaptive_next_question_request_validation_test.rb +36 -17
  24. data/lib/davinci_dtr_test_kit/client_groups/adaptive_questionnaire/dtr_adaptive_response_validation_test.rb +130 -0
  25. data/lib/davinci_dtr_test_kit/client_groups/auth/dtr_client_payer_auth_smart_group.rb +29 -0
  26. data/lib/davinci_dtr_test_kit/client_groups/auth/dtr_client_payer_auth_udap_group.rb +29 -0
  27. data/lib/davinci_dtr_test_kit/client_groups/custom_static/dtr_full_ehr_custom_static_workflow_group.rb +15 -14
  28. data/lib/davinci_dtr_test_kit/client_groups/custom_static/dtr_smart_app_custom_static_workflow_group.rb +5 -5
  29. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_static_dinner_workflow_group.rb +14 -10
  30. data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_smart_app_static_dinner_workflow_group.rb +1 -1
  31. data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → full_ehr}/dtr_full_ehr_adaptive_request_test.rb +11 -14
  32. data/lib/davinci_dtr_test_kit/client_groups/full_ehr/dtr_full_ehr_questionnaire_package_request_test.rb +9 -17
  33. data/lib/davinci_dtr_test_kit/client_groups/full_ehr/dtr_full_ehr_questionnaire_response_correctness_test.rb +10 -1
  34. data/lib/davinci_dtr_test_kit/client_groups/full_ehr/dtr_full_ehr_store_attestation_test.rb +3 -3
  35. data/lib/davinci_dtr_test_kit/client_groups/light_ehr/fhir_context_references_test.rb +4 -5
  36. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_full_ehr_ms_questionnaire_package_request_test.rb +75 -0
  37. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_full_ehr_questionnaire_must_support_group.rb +85 -0
  38. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_must_support_attestation_test.rb +39 -0
  39. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_questionnaire_must_support_test.rb +42 -0
  40. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_smart_app_ms_questionnaire_package_request_test.rb +148 -0
  41. data/lib/davinci_dtr_test_kit/client_groups/must_support/dtr_smart_app_questionnaire_must_support_group.rb +85 -0
  42. data/lib/davinci_dtr_test_kit/client_groups/must_support/questionnaire_must_support_elements.rb +55 -0
  43. data/lib/davinci_dtr_test_kit/client_groups/payer_registration/configuration_display_smart_test.rb +35 -0
  44. data/lib/davinci_dtr_test_kit/client_groups/payer_registration/configuration_display_udap_test.rb +35 -0
  45. data/lib/davinci_dtr_test_kit/client_groups/payer_registration/dtr_client_registration_group.rb +48 -0
  46. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_custom_questionnaire_expressions_test.rb +36 -0
  47. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_custom_questionnaire_extensions_test.rb +32 -0
  48. data/lib/davinci_dtr_test_kit/client_groups/{custom_static → shared}/dtr_custom_questionnaire_libraries_test.rb +7 -2
  49. data/lib/davinci_dtr_test_kit/client_groups/{custom_static → shared}/dtr_custom_questionnaire_package_validation_test.rb +6 -2
  50. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_package_request_validation_test.rb +6 -2
  51. data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_prepopulation_test.rb +7 -1
  52. data/lib/davinci_dtr_test_kit/client_groups/smart_app/dtr_smart_app_questionnaire_response_correctness_test.rb +4 -3
  53. data/lib/davinci_dtr_test_kit/descriptions.rb +8 -0
  54. data/lib/davinci_dtr_test_kit/docs/dtr_full_ehr_suite_description_v201.md +145 -78
  55. data/lib/davinci_dtr_test_kit/docs/dtr_smart_app_suite_description_v201.md +4 -3
  56. data/lib/davinci_dtr_test_kit/dtr_client_options.rb +13 -0
  57. data/lib/davinci_dtr_test_kit/dtr_full_ehr_suite.rb +47 -7
  58. data/lib/davinci_dtr_test_kit/dtr_light_ehr_suite.rb +33 -6
  59. data/lib/davinci_dtr_test_kit/dtr_payer_server_suite.rb +4 -4
  60. data/lib/davinci_dtr_test_kit/dtr_questionnaire_response_validation.rb +33 -21
  61. data/lib/davinci_dtr_test_kit/dtr_smart_app_suite.rb +7 -4
  62. data/lib/davinci_dtr_test_kit/endpoints/mock_ehr.rb +1 -1
  63. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/full_ehr_next_question_endpoint.rb +20 -1
  64. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/full_ehr_questionnaire_package_endpoint.rb +21 -1
  65. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/next_question_endpoint.rb +29 -4
  66. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/next_question_proxy_endpoint.rb +6 -2
  67. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/questionnaire_package_endpoint.rb +1 -1
  68. data/lib/davinci_dtr_test_kit/endpoints/mock_payer/questionnaire_package_proxy_endpoint.rb +6 -2
  69. data/lib/davinci_dtr_test_kit/endpoints/mock_udap_smart_server/token_endpoint.rb +36 -0
  70. data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_group.rb +1 -1
  71. data/lib/davinci_dtr_test_kit/requirements/davinci-dtr-test-kit_out_of_scope_requirements.csv +6 -5
  72. data/lib/davinci_dtr_test_kit/requirements/davinci-dtr-test-kit_requirements.csv +10 -9
  73. data/lib/davinci_dtr_test_kit/requirements/generated/davinci-dtr-test-kit_requirements_coverage.csv +21 -21
  74. data/lib/davinci_dtr_test_kit/urls.rb +21 -4
  75. data/lib/davinci_dtr_test_kit/version.rb +2 -2
  76. metadata +63 -25
  77. data/config/presets/full_ehr_postman_dinner_order_example_postman.json +0 -19
  78. data/lib/davinci_dtr_test_kit/client_groups/custom_static/dtr_custom_questionnaire_expressions_test.rb +0 -22
  79. data/lib/davinci_dtr_test_kit/client_groups/custom_static/dtr_custom_questionnaire_extensions_test.rb +0 -19
  80. data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_response_validation_test.rb +0 -68
  81. /data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_adaptive_completion_group.rb +0 -0
  82. /data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → adaptive_questionnaire/dinner_order}/dtr_adaptive_followup_questions_group.rb +0 -0
  83. /data/lib/davinci_dtr_test_kit/client_groups/{dinner_adaptive → smart_app}/dtr_smart_app_adaptive_request_test.rb +0 -0
@@ -1,12 +1,19 @@
1
+ require 'udap_security_test_kit'
2
+ require 'smart_app_launch_test_kit'
1
3
  require_relative 'client_groups/custom_static/dtr_full_ehr_custom_static_workflow_group'
2
4
  require_relative 'client_groups/dinner_static/dtr_full_ehr_static_dinner_workflow_group'
3
- require_relative 'client_groups/dinner_adaptive/dtr_full_ehr_adaptive_dinner_workflow_group'
4
- require_relative 'auth_groups/oauth2_authentication_group'
5
+ require_relative 'client_groups/adaptive_questionnaire/dinner_order/dtr_full_ehr_adaptive_dinner_workflow_group'
6
+ require_relative 'client_groups/adaptive_questionnaire/custom/dtr_full_ehr_custom_adaptive_workflow_group'
7
+ require_relative 'client_groups/payer_registration/dtr_client_registration_group'
8
+ require_relative 'client_groups/must_support/dtr_full_ehr_questionnaire_must_support_group'
9
+ require_relative 'client_groups/auth/dtr_client_payer_auth_smart_group'
10
+ require_relative 'client_groups/auth/dtr_client_payer_auth_udap_group'
5
11
  require_relative 'endpoints/cors'
6
- require_relative 'endpoints/mock_authorization/simple_token_endpoint'
12
+ require_relative 'endpoints/mock_udap_smart_server/token_endpoint'
7
13
  require_relative 'endpoints/mock_payer/full_ehr_questionnaire_package_endpoint'
8
14
  require_relative 'endpoints/mock_payer/full_ehr_next_question_endpoint'
9
15
  require_relative 'version'
16
+ require_relative 'dtr_client_options'
10
17
 
11
18
  module DaVinciDTRTestKit
12
19
  class DTRFullEHRSuite < Inferno::TestSuite
@@ -35,9 +42,22 @@ module DaVinciDTRTestKit
35
42
  }
36
43
  ]
37
44
 
45
+ suite_option :client_type,
46
+ title: 'Client Security Type',
47
+ list_options: [
48
+ {
49
+ label: 'SMART Backend Services',
50
+ value: DTRClientOptions::SMART_BACKEND_SERVICES_CONFIDENTIAL_ASYMMETRIC
51
+ },
52
+ {
53
+ label: 'UDAP B2B Client Credentials',
54
+ value: DTRClientOptions::UDAP_CLIENT_CREDENTIALS
55
+ }
56
+ ]
57
+
38
58
  # Hl7 Validator Wrapper:
39
59
  fhir_resource_validator do
40
- igs 'hl7.fhir.us.davinci-dtr#2.0.1'
60
+ igs 'igs/davinci_dtr_2.0.1.tgz'
41
61
 
42
62
  exclude_message do |message|
43
63
  message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
@@ -46,7 +66,16 @@ module DaVinciDTRTestKit
46
66
 
47
67
  allow_cors QUESTIONNAIRE_PACKAGE_PATH, NEXT_PATH
48
68
 
49
- suite_endpoint :post, PAYER_TOKEN_PATH, MockAuthorization::SimpleTokenEndpoint
69
+ route(:get, UDAPSecurityTestKit::UDAP_DISCOVERY_PATH, lambda { |_env|
70
+ UDAPSecurityTestKit::MockUDAPServer.udap_server_metadata(id)
71
+ })
72
+ route(:get, SMARTAppLaunch::SMART_DISCOVERY_PATH, lambda { |_env|
73
+ SMARTAppLaunch::MockSMARTServer.smart_server_metadata(id)
74
+ })
75
+
76
+ suite_endpoint :post, UDAPSecurityTestKit::REGISTRATION_PATH,
77
+ UDAPSecurityTestKit::MockUDAPServer::RegistrationEndpoint
78
+ suite_endpoint :post, UDAPSecurityTestKit::TOKEN_PATH, MockUdapSmartServer::TokenEndpoint
50
79
 
51
80
  suite_endpoint :post, QUESTIONNAIRE_PACKAGE_PATH, MockPayer::FullEHRQuestionnairePackageEndpoint
52
81
  suite_endpoint :post, NEXT_PATH, MockPayer::FullEHRNextQuestionEndpoint
@@ -59,13 +88,13 @@ module DaVinciDTRTestKit
59
88
  request.query_parameters['token']
60
89
  end
61
90
 
62
- group from: :oauth2_authentication
91
+ group from: :dtr_client_payer_registration
63
92
  group do
64
93
  id :dtr_full_ehr_basic_workflows
65
94
  title 'Basic Workflows'
66
95
 
67
96
  group from: :dtr_full_ehr_custom_static_workflow
68
- group from: :dtr_full_ehr_adaptive_dinner_workflow
97
+ group from: :dtr_full_ehr_custom_adaptive_workflow
69
98
  end
70
99
  group do
71
100
  id :dtr_full_ehr_questionnaire_functionality
@@ -75,6 +104,17 @@ module DaVinciDTRTestKit
75
104
  covering additional pre-population features of questionnaires.
76
105
  )
77
106
  group from: :dtr_full_ehr_static_dinner_workflow
107
+ group from: :dtr_full_ehr_adaptive_dinner_workflow
78
108
  end
109
+ group from: :dtr_full_ehr_questionnaire_ms
110
+
111
+ group from: :dtr_client_payer_auth_smart,
112
+ required_suite_options: {
113
+ client_type: DTRClientOptions::SMART_BACKEND_SERVICES_CONFIDENTIAL_ASYMMETRIC
114
+ }
115
+ group from: :dtr_client_payer_auth_udap,
116
+ required_suite_options: {
117
+ client_type: DTRClientOptions::UDAP_CLIENT_CREDENTIALS
118
+ }
79
119
  end
80
120
  end
@@ -65,6 +65,24 @@ module DaVinciDTRTestKit
65
65
  group do
66
66
  title 'Authorization'
67
67
 
68
+ config(
69
+ inputs: {
70
+ smart_auth_info: {
71
+ name: :smart_auth_info,
72
+ title: 'EHR Launch Credentials',
73
+ options: {
74
+ mode: 'auth',
75
+ components: [
76
+ Inferno::DSL::AuthInfo.default_auth_type_component_without_backend_services
77
+ ]
78
+ }
79
+ }
80
+ },
81
+ outputs: {
82
+ smart_auth_info: { name: :smart_auth_info }
83
+ }
84
+ )
85
+
68
86
  group from: :smart_discovery_stu2 do
69
87
  required_suite_options DTROptions::SMART_2_REQUIREMENT
70
88
  run_as_group
@@ -98,8 +116,7 @@ module DaVinciDTRTestKit
98
116
  id_token: { name: :id_token },
99
117
  client_id: { name: :client_id },
100
118
  requested_scopes: { name: :requested_scopes },
101
- access_token: { name: :access_token },
102
- smart_credentials: { name: :smart_credentials }
119
+ access_token: { name: :access_token }
103
120
  }
104
121
  }
105
122
  end
@@ -117,6 +134,17 @@ module DaVinciDTRTestKit
117
134
  title: 'FHIR Server Base Url',
118
135
  description: 'URL of the target DTR Light EHR'
119
136
 
137
+ config(
138
+ inputs: {
139
+ smart_auth_info: {
140
+ title: 'OAuth Credentials',
141
+ options: {
142
+ mode: 'access'
143
+ }
144
+ }
145
+ }
146
+ )
147
+
120
148
  group from: :'us_core_v311-us_core_v311_fhir_api',
121
149
  run_as_group: true,
122
150
  verifies_requirements: ['hl7.fhir.us.davinci-dtr_2.0.1@2', 'hl7.fhir.us.davinci-dtr_2.0.1@281']
@@ -131,15 +159,14 @@ module DaVinciDTRTestKit
131
159
  )
132
160
  run_as_group
133
161
 
134
- input :smart_credentials,
135
- title: 'OAuth Credentials',
136
- type: :oauth_credentials,
162
+ input :smart_auth_info,
163
+ type: :auth_info,
137
164
  optional: true
138
165
 
139
166
  # All FHIR requests in this suite will use this FHIR client
140
167
  fhir_client do
141
168
  url :url
142
- oauth_credentials :smart_credentials
169
+ auth_info :smart_auth_info
143
170
  end
144
171
 
145
172
  group from: :questionnaire_response_group
@@ -71,9 +71,9 @@ module DaVinciDTRTestKit
71
71
  title: 'Custom Endpoint for Accessing a Particular Resource',
72
72
  description: 'Either Flow (optional)'
73
73
 
74
- input :credentials,
74
+ input :smart_auth_info,
75
75
  title: 'OAuth Credentials',
76
- type: :oauth_credentials,
76
+ type: :auth_info,
77
77
  optional: true
78
78
 
79
79
  input_order :retrieval_method,
@@ -83,12 +83,12 @@ module DaVinciDTRTestKit
83
83
  :initial_static_questionnaire_request,
84
84
  :initial_adaptive_questionnaire_request,
85
85
  :next_question_requests,
86
- :credentials
86
+ :smart_auth_info
87
87
 
88
88
  # All FHIR requests in this suite will use this FHIR client
89
89
  fhir_client do
90
90
  url :url
91
- oauth_credentials :credentials
91
+ auth_info :smart_auth_info
92
92
  end
93
93
 
94
94
  # Hl7 Validator Wrapper:
@@ -29,14 +29,7 @@ module DaVinciDTRTestKit
29
29
  "Couldn't find Questionnaire #{qr.questionnaire} in the provided custom questionnaire package
30
30
  to validate the QuestionnaireResponse."
31
31
 
32
- missing_origin_sources = ['auto', 'manual', 'override'] - extract_origin_sources(qr.item)
33
- unless missing_origin_sources.empty?
34
- add_message(
35
- 'error',
36
- 'All origin sources (auto, manual, override) must be present in the QuestionnaireResponse. ' \
37
- "Missing #{missing_origin_sources.to_sentence}"
38
- )
39
- end
32
+ check_missing_origin_sources(qr)
40
33
  end
41
34
 
42
35
  check_origin_sources(questionnaire.item, qr.item, expected_overrides:)
@@ -46,6 +39,18 @@ module DaVinciDTRTestKit
46
39
  assert(messages.none? { |m| m[:type] == 'error' }, 'QuestionnaireResponse is not correct, see error message(s)')
47
40
  end
48
41
 
42
+ def check_missing_origin_sources(questionnaire_response, index = nil)
43
+ missing_origin_sources = ['auto', 'manual', 'override'] - extract_origin_sources(questionnaire_response.item)
44
+ return if missing_origin_sources.empty?
45
+
46
+ prefix = index ? "Request #{index}: " : ''
47
+ add_message(
48
+ 'error',
49
+ "#{prefix}All origin sources (auto, manual, override) must be present in the QuestionnaireResponse. " \
50
+ "Missing #{missing_origin_sources.to_sentence}"
51
+ )
52
+ end
53
+
49
54
  def check_is_questionnaire_response(questionnaire_response_json)
50
55
  assert_valid_json(questionnaire_response_json)
51
56
  questionnaire_response = begin
@@ -65,9 +70,10 @@ module DaVinciDTRTestKit
65
70
  end
66
71
 
67
72
  # This only checks answers in the questionnaire response, meaning it does not catch missing answers
68
- def check_origin_sources(questionnaire_items, response_items, expected_overrides: [])
73
+ def check_origin_sources(questionnaire_items, response_items, expected_overrides: [], index: nil)
74
+ prefix = index ? "Request #{index}: " : ''
69
75
  response_items&.each do |response_item|
70
- check_origin_sources(questionnaire_items, response_item.item, expected_overrides:)
76
+ check_origin_sources(questionnaire_items, response_item.item, expected_overrides:, index:)
71
77
  next unless response_item.answer&.any?
72
78
 
73
79
  link_id = response_item.linkId
@@ -76,20 +82,25 @@ module DaVinciDTRTestKit
76
82
  is_cql_expression = item_is_cql_expression?(questionnaire_item)
77
83
 
78
84
  if origin_source.nil?
79
- add_message('error', "Required `origin.source` extension not present on answer to item `#{link_id}`")
85
+ add_message('error', "#{prefix}Required `origin.source` extension not present on answer to item `#{link_id}`")
80
86
  else
81
- check_origin_source(origin_source, link_id, is_cql_expression, override: expected_overrides.include?(link_id))
87
+ check_origin_source(
88
+ origin_source, link_id, is_cql_expression,
89
+ override: expected_overrides.include?(link_id),
90
+ index:
91
+ )
82
92
  end
83
93
  end
84
94
  end
85
95
 
86
- def check_origin_source(origin_source, link_id, is_cql_expression, override: false)
96
+ def check_origin_source(origin_source, link_id, is_cql_expression, override: false, index: nil) # rubocop:disable Metrics/CyclomaticComplexity
97
+ prefix = index ? "Request #{index}: " : ''
87
98
  if override
88
- origin_source_error(link_id, ['override'], origin_source) unless origin_source == 'override'
99
+ origin_source_error(link_id, ['override'], origin_source, prefix) unless origin_source == 'override'
89
100
  elsif is_cql_expression && !['auto', 'override'].include?(origin_source)
90
- origin_source_error(link_id, 'auto or override', origin_source)
101
+ origin_source_error(link_id, 'auto or override', origin_source, prefix)
91
102
  elsif !is_cql_expression && origin_source != 'manual'
92
- origin_source_error(link_id, 'manual', origin_source)
103
+ origin_source_error(link_id, 'manual', origin_source, prefix)
93
104
  end
94
105
  end
95
106
 
@@ -104,11 +115,13 @@ module DaVinciDTRTestKit
104
115
 
105
116
  # Ensures that all required questions have been answered.
106
117
  # If required_link_ids not provided, all questions are treated as optional.
107
- def check_answer_presence(response_items, required_link_ids = [])
118
+ def check_answer_presence(response_items, required_link_ids = [], index = nil)
119
+ prefix = index ? "Request #{index}: " : ''
120
+
108
121
  required_link_ids.each do |link_id|
109
122
  item = find_item_by_link_id(response_items, link_id)
110
123
  unless item&.answer&.any? { |answer| answer.value.present? }
111
- add_message('error', "No answer for item #{link_id}")
124
+ add_message('error', "#{prefix}No answer for item #{link_id}")
112
125
  end
113
126
  end
114
127
  end
@@ -176,7 +189,6 @@ module DaVinciDTRTestKit
176
189
  questionnaire_cql_expression_link_ids,
177
190
  expected_prepopulated = {},
178
191
  expected_overrides = {})
179
-
180
192
  questionnaire_cql_expression_link_ids.each do |target_link_id|
181
193
  target_item = find_item_by_link_id(template_questionnaire_response.item, target_link_id)
182
194
  raise "Template QuestionnaireResponse missing item with link id `#{target_link_id}`" unless target_item.present?
@@ -235,8 +247,8 @@ module DaVinciDTRTestKit
235
247
  end
236
248
  end
237
249
 
238
- def origin_source_error(link_id, expected, actual)
239
- add_message('error', %(`origin.source` extension on item `#{link_id}` contains unexpected value.
250
+ def origin_source_error(link_id, expected, actual, msg_prefix = nil)
251
+ add_message('error', %(#{msg_prefix}`origin.source` extension on item `#{link_id}` contains unexpected value.
240
252
  Expected: #{expected}. Found: #{actual}))
241
253
  end
242
254
 
@@ -1,8 +1,10 @@
1
1
  require_relative 'auth_groups/oauth2_authentication_group'
2
2
  require_relative 'client_groups/respiratory/dtr_smart_app_respiratory_workflow_group'
3
3
  require_relative 'client_groups/dinner_static/dtr_smart_app_static_dinner_workflow_group'
4
- require_relative 'client_groups/dinner_adaptive/dtr_smart_app_adaptive_dinner_workflow_group'
4
+ require_relative 'client_groups/adaptive_questionnaire/dinner_order/dtr_smart_app_adaptive_dinner_workflow_group'
5
5
  require_relative 'client_groups/custom_static/dtr_smart_app_custom_static_workflow_group'
6
+ require_relative 'client_groups/adaptive_questionnaire/custom/dtr_smart_app_custom_adaptive_workflow_group'
7
+ require_relative 'client_groups/must_support/dtr_smart_app_questionnaire_must_support_group'
6
8
  require_relative 'endpoints/cors'
7
9
  require_relative 'endpoints/mock_authorization'
8
10
  require_relative 'endpoints/mock_authorization/authorize_endpoint'
@@ -43,7 +45,7 @@ module DaVinciDTRTestKit
43
45
 
44
46
  # Hl7 Validator Wrapper:
45
47
  fhir_resource_validator do
46
- igs 'hl7.fhir.us.davinci-dtr#2.0.1'
48
+ igs 'igs/davinci_dtr_2.0.1.tgz'
47
49
 
48
50
  exclude_message do |message|
49
51
  message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
@@ -90,10 +92,9 @@ module DaVinciDTRTestKit
90
92
  )
91
93
 
92
94
  group from: :dtr_smart_app_custom_static_workflow
93
- group from: :dtr_smart_app_adaptive_dinner_workflow
95
+ group from: :dtr_smart_app_custom_adaptive_workflow
94
96
  end
95
97
  group do
96
- id :dtr_smart_app_questionnaire_functionality
97
98
  title 'Questionnaire Functionality Coverage'
98
99
  description %(
99
100
  Tests in this group validate that the client can complete additional DTR workflows
@@ -101,6 +102,8 @@ module DaVinciDTRTestKit
101
102
  )
102
103
  group from: :dtr_smart_app_resp_workflow
103
104
  group from: :dtr_smart_app_static_dinner_workflow
105
+ group from: :dtr_smart_app_adaptive_dinner_workflow
104
106
  end
107
+ group from: :dtr_smart_app_questionnaire_ms
105
108
  end
106
109
  end
@@ -21,7 +21,7 @@ module DaVinciDTRTestKit
21
21
  # Overwrite the OAuth URIs returned by the reference server to point to the suite endpoints instead
22
22
  oauth_uris_url = 'http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris'
23
23
  base_url = MockAuthorization.env_base_url(env, METADATA_PATH)
24
- sec_ext = cs.rest.first&.security&.extension&.delete_if { |e| e.url == oauth_uris_url }
24
+ sec_ext = cs.rest.first&.security&.extension&.delete_if { |e| e.url == oauth_uris_url } # rubocop:disable Style/SafeNavigationChainLength
25
25
  sec_ext&.push(
26
26
  FHIR::Extension.new(
27
27
  url: oauth_uris_url,
@@ -4,7 +4,26 @@ module DaVinciDTRTestKit
4
4
  module MockPayer
5
5
  class FullEHRNextQuestionEndpoint < NextQuestionEndpoint
6
6
  def test_run_identifier
7
- request.headers['authorization']&.delete_prefix('Bearer ')
7
+ return request.params[:session_path] if request.params[:session_path].present?
8
+
9
+ UDAPSecurityTestKit::MockUDAPServer.issued_token_to_client_id(
10
+ request.headers['authorization']&.delete_prefix('Bearer ')
11
+ )
12
+ end
13
+
14
+ def make_response
15
+ return if response.status == 401 # set in update_result (expired token handling there)
16
+
17
+ super
18
+ end
19
+
20
+ def update_result
21
+ if UDAPSecurityTestKit::MockUDAPServer.request_has_expired_token?(request)
22
+ UDAPSecurityTestKit::MockUDAPServer.update_response_for_expired_token(response, 'Bearer token')
23
+ return
24
+ end
25
+
26
+ super
8
27
  end
9
28
  end
10
29
  end
@@ -1,10 +1,30 @@
1
+ require 'udap_security_test_kit'
1
2
  require_relative 'questionnaire_package_endpoint'
2
3
 
3
4
  module DaVinciDTRTestKit
4
5
  module MockPayer
5
6
  class FullEHRQuestionnairePackageEndpoint < QuestionnairePackageEndpoint
6
7
  def test_run_identifier
7
- request.headers['authorization']&.delete_prefix('Bearer ')
8
+ return request.params[:session_path] if request.params[:session_path].present?
9
+
10
+ UDAPSecurityTestKit::MockUDAPServer.issued_token_to_client_id(
11
+ request.headers['authorization']&.delete_prefix('Bearer ')
12
+ )
13
+ end
14
+
15
+ def make_response
16
+ return if response.status == 401 # set in update_result (expired token handling there)
17
+
18
+ super
19
+ end
20
+
21
+ def update_result
22
+ if UDAPSecurityTestKit::MockUDAPServer.request_has_expired_token?(request)
23
+ UDAPSecurityTestKit::MockUDAPServer.update_response_for_expired_token(response, 'Bearer token')
24
+ return
25
+ end
26
+
27
+ super
8
28
  end
9
29
  end
10
30
  end
@@ -22,6 +22,29 @@ module DaVinciDTRTestKit
22
22
  response.body = build_questionnaire_next_response.to_json
23
23
  end
24
24
 
25
+ def custom_next_question_requests
26
+ requests_repo.tagged_requests(test_run.test_session_id, ["custom_#{DaVinciDTRTestKit::CLIENT_NEXT_TAG}"])
27
+ end
28
+
29
+ def custom_questionnaires
30
+ return @custom_questionnaires unless @custom_questionnaires.nil?
31
+
32
+ questionnaires = JSON.parse(result.input_json)
33
+ .find { |input| input['name'].include?('custom_next_question_questionnaires') }
34
+ &.dig('value')
35
+ return unless questionnaires
36
+
37
+ @custom_questionnaires = [JSON.parse(questionnaires)].flatten.compact
38
+ end
39
+
40
+ def next_custom_questionnaire
41
+ custom_questionnaires[custom_next_question_requests.length]
42
+ end
43
+
44
+ def complete_questionnaire?
45
+ custom_questionnaires && next_custom_questionnaire.blank?
46
+ end
47
+
25
48
  def update_result
26
49
  results_repo.update_result(result.id, 'pass') unless test.config.options[:accepts_multiple_requests]
27
50
  end
@@ -41,9 +64,9 @@ module DaVinciDTRTestKit
41
64
 
42
65
  questionnaire_response_param = FHIR::Parameters::Parameter.new(name: 'return', resource: questionnaire_response)
43
66
 
44
- if questionnaire_last_dinner_order_question_present?(questionnaire_response)
67
+ if complete_questionnaire? || questionnaire_last_dinner_order_question_present?(questionnaire_response)
45
68
  # change the questionnaire response status to completed and return the parameters
46
- return handle_last_dinner_order(questionnaire_response)
69
+ return complete_questionnaire(questionnaire_response)
47
70
  end
48
71
 
49
72
  next_questionnaire = determine_next_questionnaire(questionnaire_response, test.id)
@@ -103,15 +126,17 @@ module DaVinciDTRTestKit
103
126
  end
104
127
 
105
128
  def determine_next_questionnaire(questionnaire_response, test_id)
129
+ if custom_questionnaires
130
+ FHIR.from_contents(next_custom_questionnaire.to_json)
106
131
  # Retrieve the selected option from the response and determine the next set of questions
107
- if questionnaire_dinner_order_selection_present?(questionnaire_response)
132
+ elsif questionnaire_dinner_order_selection_present?(questionnaire_response)
108
133
  dinner_question_from_selection(questionnaire_response, test_id)
109
134
  else
110
135
  Fixtures.next_question_for_test(test_id)
111
136
  end
112
137
  end
113
138
 
114
- def handle_last_dinner_order(questionnaire_response)
139
+ def complete_questionnaire(questionnaire_response)
115
140
  update_questionnaire_response(questionnaire_response)
116
141
  questionnaire_response
117
142
  end
@@ -13,8 +13,12 @@ module DaVinciDTRTestKit
13
13
  session_id = result.test_session_id
14
14
  session_data = Inferno::Repositories::SessionData.new
15
15
  url_input = session_data.load(test_session_id: session_id, name: 'url')
16
- credentials_input = session_data.load(test_session_id: session_id, name: 'credentials',
17
- type: 'oauth_credentials')
16
+ credentials_input =
17
+ session_data.load(
18
+ test_session_id: session_id,
19
+ name: 'smart_auth_info',
20
+ type: 'auth_info'
21
+ )
18
22
 
19
23
  client = FHIR::Client.new(url_input)
20
24
  client.default_json
@@ -12,7 +12,7 @@ module DaVinciDTRTestKit
12
12
  end
13
13
 
14
14
  def tags
15
- [QUESTIONNAIRE_PACKAGE_TAG]
15
+ [test.config.options[:questionnaire_package_tag].presence || QUESTIONNAIRE_PACKAGE_TAG]
16
16
  end
17
17
 
18
18
  def make_response
@@ -14,8 +14,12 @@ module DaVinciDTRTestKit
14
14
  session_data = Inferno::Repositories::SessionData.new
15
15
  endpoint_input = session_data.load(test_session_id: session_id, name: 'custom_endpoint')
16
16
  url_input = session_data.load(test_session_id: session_id, name: 'url')
17
- credentials_input = session_data.load(test_session_id: session_id, name: 'credentials',
18
- type: 'oauth_credentials')
17
+ credentials_input =
18
+ session_data.load(
19
+ test_session_id: session_id,
20
+ name: 'smart_auth_info',
21
+ type: 'auth_info'
22
+ )
19
23
 
20
24
  client = FHIR::Client.new(url_input)
21
25
  client.default_json
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'udap_security_test_kit'
4
+ require 'smart_app_launch_test_kit'
5
+ require_relative '../../urls'
6
+ require_relative '../../tags'
7
+
8
+ module DaVinciDTRTestKit
9
+ module MockUdapSmartServer
10
+ class TokenEndpoint < Inferno::DSL::SuiteEndpoint
11
+ include SMARTAppLaunch::MockSMARTServer::SMARTTokenResponseCreation
12
+ include UDAPSecurityTestKit::MockUDAPServer::UDAPTokenResponseCreation
13
+
14
+ def test_run_identifier
15
+ UDAPSecurityTestKit::MockUDAPServer.client_id_from_client_assertion(request.params[:client_assertion])
16
+ end
17
+
18
+ def make_response
19
+ if request.params[:udap].present?
20
+ make_udap_client_credential_token_response
21
+ else
22
+ make_smart_client_credential_token_response
23
+ end
24
+ end
25
+
26
+ def update_result
27
+ nil # never update for now
28
+ end
29
+
30
+ def tags
31
+ type_tag = request.params[:udap].present? ? UDAPSecurityTestKit::UDAP_TAG : SMARTAppLaunch::SMART_TAG
32
+ [UDAPSecurityTestKit::TOKEN_TAG, UDAPSecurityTestKit::CLIENT_CREDENTIALS_TAG, type_tag]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -59,7 +59,7 @@ module DaVinciDTRTestKit
59
59
  :access_token,
60
60
  :initial_adaptive_questionnaire_request,
61
61
  :next_question_requests,
62
- :credentials
62
+ :smart_auth_info
63
63
 
64
64
  # receive client request
65
65
  test from: :payer_server_questionnaire_request,
@@ -1,11 +1,12 @@
1
1
  Req Set,ID,Reason,Details
2
2
  hl7.fhir.us.davinci-dtr_2.0.1,10,Not Tested,"QuestionnaireResponse.source is not must support or required, so these systems are not required to support it."
3
3
  hl7.fhir.us.davinci-dtr_2.0.1,129,Not Tested,Cdex workflows out of initial scope
4
- hl7.fhir.us.davinci-dtr_2.0.1,231,Not Tested,check as a part of PAS initially
5
- hl7.fhir.us.davinci-dtr_2.0.1,232,Not Tested,check as a part of PAS initially
6
- hl7.fhir.us.davinci-dtr_2.0.1,233,Not Tested,check as a part of PAS initially
7
- hl7.fhir.us.davinci-dtr_2.0.1,234,Not Tested,check as a part of PAS initially
8
- hl7.fhir.us.davinci-dtr_2.0.1,236,Not Tested,check as a part of PAS initially
4
+ hl7.fhir.us.davinci-dtr_2.0.1,231,Not Tested,Not compatible with the PAS approach to prior authorization submission
5
+ hl7.fhir.us.davinci-dtr_2.0.1,232,Not Tested,Not compatible with the PAS approach to prior authorization submission
6
+ hl7.fhir.us.davinci-dtr_2.0.1,233,Not Tested,Not compatible with the PAS approach to prior authorization submission
7
+ hl7.fhir.us.davinci-dtr_2.0.1,234,Not Tested,Not compatible with the PAS approach to prior authorization submission
8
+ hl7.fhir.us.davinci-dtr_2.0.1,235,Not Tested,Not compatible with the PAS approach to prior authorization submission
9
+ hl7.fhir.us.davinci-dtr_2.0.1,236,Not Tested,Not compatible with the PAS approach to prior authorization submission
9
10
  hl7.fhir.us.davinci-dtr_2.0.1,237,Not Tested,"automatic or manual allowed, no clear impact on downstream functionality"
10
11
  hl7.fhir.us.davinci-dtr_2.0.1,238,Not Tested,"automatic or manual allowed, no clear impact on downstream functionality"
11
12
  hl7.fhir.us.davinci-dtr_2.0.1,328,Not Tested,metrics are optional at this time