davinci_crd_test_kit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/davinci_crd_test_kit/card_responses/companions_prerequisites.json +58 -0
  4. data/lib/davinci_crd_test_kit/card_responses/create_update_coverage_information.json +20 -0
  5. data/lib/davinci_crd_test_kit/card_responses/external_reference.json +21 -0
  6. data/lib/davinci_crd_test_kit/card_responses/instructions.json +14 -0
  7. data/lib/davinci_crd_test_kit/card_responses/launch_smart_app.json +21 -0
  8. data/lib/davinci_crd_test_kit/card_responses/propose_alternate_request.json +71 -0
  9. data/lib/davinci_crd_test_kit/card_responses/request_form_completion.json +227 -0
  10. data/lib/davinci_crd_test_kit/cards_validation.rb +234 -0
  11. data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +762 -0
  12. data/lib/davinci_crd_test_kit/client_hook_request_validation.rb +15 -0
  13. data/lib/davinci_crd_test_kit/client_hooks_group.rb +706 -0
  14. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +71 -0
  15. data/lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb +48 -0
  16. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +40 -0
  17. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_read_test.rb +39 -0
  18. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +232 -0
  19. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +40 -0
  20. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +60 -0
  21. data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +40 -0
  22. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +68 -0
  23. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +68 -0
  24. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +41 -0
  25. data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +40 -0
  26. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +63 -0
  27. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +151 -0
  28. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +79 -0
  29. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +76 -0
  30. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +79 -0
  31. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +65 -0
  32. data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +34 -0
  33. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +61 -0
  34. data/lib/davinci_crd_test_kit/crd_client_suite.rb +156 -0
  35. data/lib/davinci_crd_test_kit/crd_jwks.json +59 -0
  36. data/lib/davinci_crd_test_kit/crd_options.rb +9 -0
  37. data/lib/davinci_crd_test_kit/crd_server_suite.rb +115 -0
  38. data/lib/davinci_crd_test_kit/ext/inferno_core/runnable.rb +22 -0
  39. data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +410 -0
  40. data/lib/davinci_crd_test_kit/jwks.rb +25 -0
  41. data/lib/davinci_crd_test_kit/jwt_helper.rb +74 -0
  42. data/lib/davinci_crd_test_kit/mock_service_response.rb +421 -0
  43. data/lib/davinci_crd_test_kit/routes/cds-services.json +74 -0
  44. data/lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb +18 -0
  45. data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +93 -0
  46. data/lib/davinci_crd_test_kit/routes/jwk_set_endpoint_handler.rb +15 -0
  47. data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +173 -0
  48. data/lib/davinci_crd_test_kit/server_discovery_group.rb +59 -0
  49. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +144 -0
  50. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +144 -0
  51. data/lib/davinci_crd_test_kit/server_hook_request_validation.rb +15 -0
  52. data/lib/davinci_crd_test_kit/server_hooks_group.rb +69 -0
  53. data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +173 -0
  54. data/lib/davinci_crd_test_kit/server_order_select_group.rb +169 -0
  55. data/lib/davinci_crd_test_kit/server_order_sign_group.rb +198 -0
  56. data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +23 -0
  57. data/lib/davinci_crd_test_kit/server_tests/additional_orders_validation_test.rb +70 -0
  58. data/lib/davinci_crd_test_kit/server_tests/card_optional_fields_validation_test.rb +47 -0
  59. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_across_hooks_validation_test.rb +32 -0
  60. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +58 -0
  61. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +121 -0
  62. data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +72 -0
  63. data/lib/davinci_crd_test_kit/server_tests/discovery_endpoint_test.rb +88 -0
  64. data/lib/davinci_crd_test_kit/server_tests/discovery_services_validation_test.rb +65 -0
  65. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_across_hooks_validation_test.rb +28 -0
  66. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +36 -0
  67. data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +79 -0
  68. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_across_hooks_test.rb +25 -0
  69. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_test.rb +28 -0
  70. data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +38 -0
  71. data/lib/davinci_crd_test_kit/server_tests/propose_alternate_request_card_validation_test.rb +65 -0
  72. data/lib/davinci_crd_test_kit/server_tests/service_call_test.rb +86 -0
  73. data/lib/davinci_crd_test_kit/server_tests/service_request_context_validation_test.rb +30 -0
  74. data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +41 -0
  75. data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +43 -0
  76. data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +82 -0
  77. data/lib/davinci_crd_test_kit/suggestion_actions_validation.rb +123 -0
  78. data/lib/davinci_crd_test_kit/tags.rb +8 -0
  79. data/lib/davinci_crd_test_kit/test_helper.rb +23 -0
  80. data/lib/davinci_crd_test_kit/urls.rb +52 -0
  81. data/lib/davinci_crd_test_kit/version.rb +3 -0
  82. data/lib/davinci_crd_test_kit.rb +2 -0
  83. metadata +170 -0
@@ -0,0 +1,234 @@
1
+ require_relative 'server_hook_request_validation'
2
+ require_relative 'suggestion_actions_validation'
3
+
4
+ module DaVinciCRDTestKit
5
+ module CardsValidation
6
+ include DaVinciCRDTestKit::ServerHookRequestValidation
7
+ include DaVinciCRDTestKit::SuggestionActionsValidation
8
+
9
+ HOOKS = [
10
+ 'appointment-book', 'encounter-discharge', 'encounter-start',
11
+ 'order-dispatch', 'order-select', 'order-sign'
12
+ ].freeze
13
+
14
+ def card_required_fields
15
+ { 'summary' => String, 'indicator' => String, 'source' => Hash }
16
+ end
17
+
18
+ def source_required_fields
19
+ { 'label' => String, 'topic' => Hash }
20
+ end
21
+
22
+ def source_topic_required_fields
23
+ { 'code' => String, 'system' => String }
24
+ end
25
+
26
+ def card_optional_fields
27
+ {
28
+ 'uuid' => String,
29
+ 'detail' => String,
30
+ 'suggestions' => Array,
31
+ 'overrideReasons' => Array,
32
+ 'links' => Array
33
+ }
34
+ end
35
+
36
+ def override_reasons_required_fields
37
+ { 'code' => String, 'system' => String, 'display' => String }
38
+ end
39
+
40
+ def link_required_fields
41
+ { 'label' => String, 'type' => String, 'url' => 'URL' }
42
+ end
43
+
44
+ def valid_card_with_optionals?(card)
45
+ current_error_count = messages.count { |msg| msg[:type] == 'error' }
46
+ card_optional_fields.each do |field, type|
47
+ next unless card[field]
48
+
49
+ validate_presence_and_type(card, field, type, 'Card')
50
+ end
51
+
52
+ card_selection_behavior_check(card)
53
+ card_override_reasons_check(card)
54
+ card_links_check(card)
55
+ card_suggestions_check(card)
56
+
57
+ current_error_count == messages.count { |msg| msg[:type] == 'error' }
58
+ end
59
+
60
+ def card_selection_behavior_check(card)
61
+ return unless card['suggestions'].present?
62
+
63
+ selection_behavior = card['selectionBehavior']
64
+ unless selection_behavior
65
+ add_message('error', "`Card.selectionBehavior` must be provided if suggestions are present. In Card `#{card}`")
66
+ return
67
+ end
68
+
69
+ allowed_values = ['at-most-one', 'any']
70
+ return if allowed_values.include?(selection_behavior)
71
+
72
+ error_msg = "`selectionBehavior` #{selection_behavior} not allowed. " \
73
+ "Allowed values: #{allowed_values.to_sentence}. In Card `#{card}`"
74
+ add_message('error', error_msg)
75
+ end
76
+
77
+ def card_override_reasons_check(card)
78
+ return unless card['overrideReasons'].is_a?(Array)
79
+
80
+ card['overrideReasons'].each do |reason|
81
+ override_reasons_required_fields.each do |field, type|
82
+ validate_presence_and_type(reason, field, type, 'OverrideReason Coding')
83
+ end
84
+ end
85
+ end
86
+
87
+ def card_links_check(card)
88
+ return unless card['links'].is_a?(Array) && card['links'].present?
89
+
90
+ card['links'].each do |link|
91
+ link_required_fields.each do |field, type|
92
+ validate_presence_and_type(link, field, type, 'Link')
93
+ end
94
+
95
+ card_link_type_check(card, link)
96
+ end
97
+ end
98
+
99
+ def card_link_type_check(card, link)
100
+ return unless link['type']
101
+
102
+ unless ['absolute', 'smart'].include?(link['type'])
103
+ add_message('error',
104
+ "`Link.type` must be `absolute` or `smart`. Got `#{link['type']}`: `#{link}`. In Card `#{card}`")
105
+ return
106
+ end
107
+
108
+ return unless link['type'] == 'absolute' && link['appContext'].present?
109
+
110
+ msg = '`appContext` field should only be valued if the link type is smart and is not valid for absolute links: ' \
111
+ "`#{link}`. In Card `#{card}`"
112
+ add_message('error', msg)
113
+ end
114
+
115
+ def card_suggestions_check(card)
116
+ return unless card['suggestions'].is_a?(Array) && card['suggestions'].present?
117
+
118
+ card['suggestions'].each do |suggestion|
119
+ process_suggestion(card, suggestion)
120
+ end
121
+ end
122
+
123
+ def process_suggestion(card, suggestion)
124
+ validate_presence_and_type(suggestion, 'label', String, 'Suggestion')
125
+ return unless suggestion['actions']
126
+
127
+ validate_and_process_actions(card, suggestion)
128
+ end
129
+
130
+ def validate_and_process_actions(card, suggestion)
131
+ actions = suggestion['actions']
132
+ if !actions.is_a?(Array)
133
+ add_message('error', "Suggestion `actions` field is not of type Array: `#{suggestion}`. In Card `#{card}`")
134
+ return
135
+ elsif actions.empty?
136
+ add_message('error',
137
+ "Suggestion `actions` field should not be an empty Array: `#{suggestion}`. In Card `#{card}`")
138
+ return
139
+ end
140
+
141
+ actions.each do |action|
142
+ action_fields_validation(action)
143
+ end
144
+ end
145
+
146
+ def card_source_check(card)
147
+ source = card['source']
148
+ return unless source.is_a?(Hash)
149
+
150
+ source_required_fields.each do |field, type|
151
+ validate_presence_and_type(source, field, type, 'Source')
152
+ end
153
+
154
+ card_source_topic_check(source['topic'])
155
+ # TODO: How to validate topic binding to the ValueSet CRD Card Types?
156
+ end
157
+
158
+ def card_source_topic_check(topic)
159
+ return unless topic.is_a?(Hash)
160
+
161
+ source_topic_required_fields.each do |field, type|
162
+ validate_presence_and_type(topic, field, type, 'Source topic')
163
+ end
164
+ end
165
+
166
+ def card_summary_check(card)
167
+ return if !card['summary'].is_a?(String) || card['summary'].length < 140
168
+
169
+ add_message('error', "`summary` is over the 140-character limit: `#{card}`")
170
+ end
171
+
172
+ def card_indicator_check(card)
173
+ return if !card['indicator'].is_a?(String) || ['info', 'warning', 'critical'].include?(card['indicator'])
174
+
175
+ msg = "`indicator` is `#{card['indicator']}`. Allowed values are `info`, `warning`, `critical`: `#{card}`"
176
+ add_message('error', msg)
177
+ end
178
+
179
+ def cards_check(cards)
180
+ cards.each do |card|
181
+ current_error_count = messages.count { |msg| msg[:type] == 'error' }
182
+ card_required_fields.each do |field, type|
183
+ validate_presence_and_type(card, field, type, 'Card')
184
+ end
185
+
186
+ card_summary_check(card)
187
+ card_indicator_check(card)
188
+ card_source_check(card)
189
+
190
+ valid_cards << card if current_error_count == messages.count { |msg| msg[:type] == 'error' }
191
+ end
192
+ end
193
+
194
+ def perform_cards_validation(cards, response_index = 0)
195
+ unless cards
196
+ add_message('error', "Server response #{response_index + 1} did not have the `cards` field.")
197
+ return
198
+ end
199
+ unless cards.is_a?(Array)
200
+ add_message('error', "`cards` field of server response #{response_index + 1} is not an array.")
201
+ return
202
+ end
203
+ warning do
204
+ assert cards.present?, "Server response #{response_index + 1} has no decision support."
205
+ end
206
+ cards_check(cards)
207
+ end
208
+
209
+ def all_requests
210
+ @all_requests ||= HOOKS.each_with_object([]) do |hook, reqs|
211
+ load_tagged_requests(hook)
212
+ reqs.concat(requests)
213
+ end
214
+ end
215
+
216
+ def extract_all_valid_cards_from_hooks_responses
217
+ all_requests.keep_if { |request| request.status == 200 }
218
+ all_requests.each_with_index do |request, index|
219
+ service_response = JSON.parse(request.response_body)
220
+ perform_cards_validation(service_response['cards'], index)
221
+ rescue JSON::ParserError
222
+ add_message('error', "Invalid JSON: server response #{index + 1} is not a valid JSON.")
223
+ end
224
+ end
225
+
226
+ def extract_valid_cards_with_links_from_hooks_responses
227
+ extract_all_valid_cards_from_hooks_responses
228
+
229
+ valid_cards.each do |card|
230
+ valid_cards_with_links << card if valid_card_with_optionals?(card) && (card['links'])
231
+ end
232
+ end
233
+ end
234
+ end