davinci_crd_test_kit 0.14.0 → 0.14.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/inferno_crd_client_suite_v221.json.erb +64 -0
  3. data/config/presets/inferno_crd_server_suite_v221.json.erb +2 -2
  4. data/lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb +12 -6
  5. data/lib/davinci_crd_test_kit/client/endpoints/mocked_card_responses/external_reference.json +1 -1
  6. data/lib/davinci_crd_test_kit/client/endpoints/mocked_card_responses/launch_smart_app.json +0 -7
  7. data/lib/davinci_crd_test_kit/client/v2.2.1/client_appointment_book_group.rb +8 -1
  8. data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_discharge_group.rb +7 -1
  9. data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_start_group.rb +7 -1
  10. data/lib/davinci_crd_test_kit/client/v2.2.1/client_hooks_group.rb +20 -0
  11. data/lib/davinci_crd_test_kit/client/v2.2.1/client_long_running_hook_group.rb +1 -1
  12. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_dispatch_group.rb +7 -1
  13. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_select_group.rb +7 -1
  14. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_sign_group.rb +7 -1
  15. data/lib/davinci_crd_test_kit/client/v2.2.1/client_registration_group.rb +6 -0
  16. data/lib/davinci_crd_test_kit/client/v2.2.1/crd_client_suite.rb +5 -0
  17. data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_skip_long_running_attestation_test.rb +10 -5
  18. data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_service_registration_attestation_test.rb +2 -1
  19. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_complete_test.rb +6 -0
  20. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/client_display_cards_attest.rb +2 -1
  21. data/lib/davinci_crd_test_kit/cross_suite/cards_identification.rb +9 -0
  22. data/lib/davinci_crd_test_kit/cross_suite/prefetch_completeness_checker.rb +25 -11
  23. data/lib/davinci_crd_test_kit/cross_suite/requests_logical_model_validation.rb +2 -1
  24. data/lib/davinci_crd_test_kit/cross_suite/response_logical_model_validation.rb +164 -3
  25. data/lib/davinci_crd_test_kit/cross_suite/tags.rb +4 -0
  26. data/lib/davinci_crd_test_kit/requirements/generated/crd_server_v221_requirements_coverage.csv +38 -38
  27. data/lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb +3 -3
  28. data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_no_special_requests.rb +17 -0
  29. data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_with_bad_auth.rb +14 -0
  30. data/lib/davinci_crd_test_kit/server/server_abstract_invoke_hook_test.rb +24 -6
  31. data/lib/davinci_crd_test_kit/server/server_hook_helper.rb +2 -2
  32. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_server_suite.rb +3 -0
  33. data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_standard_prefetch_expressions_test.rb +166 -0
  34. data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_single_request_test.rb +22 -0
  35. data/lib/davinci_crd_test_kit/server/v2.2.1/server_appointment_book_group.rb +10 -102
  36. data/lib/davinci_crd_test_kit/server/v2.2.1/server_demonstrate_hook_response_group.rb +1 -0
  37. data/lib/davinci_crd_test_kit/server/v2.2.1/server_discovery_group.rb +2 -0
  38. data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_discharge_group.rb +10 -97
  39. data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_start_group.rb +10 -97
  40. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_dispatch_group.rb +10 -103
  41. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_select_group.rb +12 -116
  42. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_sign_group.rb +12 -121
  43. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_response_group.rb +23 -0
  44. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_info_reason_test.rb +108 -0
  45. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_not_found_group.rb +116 -0
  46. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_active_coverage_group.rb +115 -0
  47. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_member_found_group.rb +117 -0
  48. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_group.rb +121 -0
  49. data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_invoke_test.rb +22 -0
  50. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_context_validation_test.rb +1 -0
  51. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_optional_fields_validation_test.rb +1 -0
  52. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_required_fields_validation_test.rb +1 -0
  53. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/additional_orders_validation_test.rb +78 -35
  54. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_card_absence_test.rb +76 -0
  55. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_validation_test.rb +70 -124
  56. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +83 -39
  57. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/external_reference_card_validation_test.rb +59 -17
  58. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/form_completion_response_validation_test.rb +80 -45
  59. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/hook_request_resource_resolution.rb +37 -0
  60. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/instructions_card_received_test.rb +57 -6
  61. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/launch_smart_app_card_validation_test.rb +56 -17
  62. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/propose_alternate_request_card_validation_test.rb +52 -25
  63. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/service_response_validation_test.rb +17 -51
  64. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_cds_hooks_elements_test.rb +1 -1
  65. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_configuration_test.rb +1 -1
  66. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_context_test.rb +1 -1
  67. data/lib/davinci_crd_test_kit/version.rb +2 -2
  68. metadata +18 -8
  69. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/card_optional_fields_validation_test.rb +0 -57
  70. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_received_test.rb +0 -65
@@ -0,0 +1,76 @@
1
+ require_relative '../../server_hook_helper'
2
+ require_relative '../../../cross_suite/cards_identification'
3
+ require_relative 'hook_request_resource_resolution'
4
+
5
+ module DaVinciCRDTestKit
6
+ module V221
7
+ class CoverageInformationCardAbsenceTest < Inferno::Test
8
+ include DaVinciCRDTestKit::ServerHookHelper
9
+ include DaVinciCRDTestKit::CardsIdentification
10
+ include HookRequestResourceResolution
11
+
12
+ title 'Hook responses do not include Coverage Information cards'
13
+ id :crd_v221_coverage_information_card_absence
14
+ description %(
15
+ This test verifies that the server does not return the
16
+ [Coverage Information](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#coverage-information-response-type)
17
+ response type as a CDS Hooks card. Coverage Information responses must be returned as `systemActions`
18
+ that update resources with the `coverage-information` extension (inclusion of systemActions is verified in a
19
+ previous test).
20
+ )
21
+
22
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-25'
23
+
24
+ input :mock_ehr_bundle, optional: true
25
+
26
+ def coverage_information_card_message(response_index, card)
27
+ identifier = card['uuid'].present? ? "uuid `#{card['uuid']}`" : 'no uuid'
28
+
29
+ "Server response #{response_index + 1} included a card with #{identifier} and a suggestion action that only " \
30
+ 'adds or modifies the coverage-information extension. Coverage Information must be returned as a ' \
31
+ 'systemAction, not as a card.'
32
+ end
33
+
34
+ def coverage_information_card_response_type?(card, request)
35
+ Array(card['suggestions']).any? do |suggestion|
36
+ Array(suggestion['actions']).any? do |action|
37
+ coverage_information_card_action?(action, request)
38
+ end
39
+ end
40
+ end
41
+
42
+ def coverage_information_card_action?(action, request)
43
+ return false unless coverage_information_response_type?(action)
44
+ return false unless action['type'] == 'update'
45
+
46
+ source_resource = find_action_source_resource(action, request)
47
+ return coverage_information_extension_only_payload?(action['resource']) unless source_resource
48
+
49
+ only_coverage_information_changed?(source_resource.to_hash, action['resource'])
50
+ end
51
+
52
+ run do
53
+ load_tagged_requests(tested_hook_name)
54
+ skip_if requests.blank?, "No #{tested_hook_name} request was made in a previous test as expected."
55
+
56
+ successful_requests = requests.select { |request| request.status == 200 }
57
+ skip_if successful_requests.empty?, 'All service requests were unsuccessful.'
58
+
59
+ successful_requests.each_with_index do |request, index|
60
+ response_body = JSON.parse(request.response_body)
61
+ cards = response_body['cards'].is_a?(Array) ? response_body['cards'] : []
62
+
63
+ cards.select { |card| coverage_information_card_response_type?(card, request) }.each do |card|
64
+ add_message('error', coverage_information_card_message(index, card))
65
+ end
66
+ rescue JSON::ParserError
67
+ add_message('error', "Invalid JSON: server response #{index + 1} is not valid JSON.")
68
+ end
69
+
70
+ assert_no_error_messages(
71
+ 'Service response(s) included Coverage Information cards. Check messages for details.'
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,6 +1,8 @@
1
1
  require_relative '../../server_hook_request_validation'
2
2
  require_relative '../../server_test_helper'
3
3
  require_relative '../../server_hook_helper'
4
+ require_relative '../../../cross_suite/cards_identification'
5
+ require_relative '../../../cross_suite/response_logical_model_validation'
4
6
  require_relative 'hook_request_resource_resolution'
5
7
 
6
8
  module DaVinciCRDTestKit
@@ -10,123 +12,42 @@ module DaVinciCRDTestKit
10
12
  include DaVinciCRDTestKit::ServerTestHelper
11
13
  include DaVinciCRDTestKit::ServerHookHelper
12
14
  include HookRequestResourceResolution
15
+ include DaVinciCRDTestKit::CardsIdentification
16
+ include DaVinciCRDTestKit::ResponseLogicalModelValidation
13
17
 
14
18
  COVERAGE_INFO_EXT_URL = 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/ext-coverage-information'.freeze
15
19
 
16
20
  title 'Coverage Information system actions are valid'
17
21
  id :crd_v221_coverage_info_system_action_validation
18
22
  description %(
19
- This test validates all [Coverage Information](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#coverage-information)
23
+ This test validates all [Coverage
24
+ Information](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#coverage-information-response-type)
20
25
  system actions received. It verifies the following for each action:
21
26
  - The action type is `update`.
22
27
  - The resource within the action conforms its respective FHIR profile.
23
- - The resource does not change any data elements other than adding or modifying
24
- the `coverage-information` extension.
25
-
26
- Additionally, the test examines the `coverage-info` extensions within the resource to ensure that:
27
- - Entries referencing differing coverage have distinct `coverage-assertion-ids` and `satisfied-pa-ids`
28
- (if present).
29
- - Entries referencing the same coverage have the same `coverage-assertion-ids` and `satisfied-pa-ids`
30
- (if present).
28
+ - The resource does not change any data elements other than adding or
29
+ modifying the `coverage-information` extension.
30
+
31
+ Additionally, the test examines the `coverage-info` extensions within
32
+ the resource to ensure that:
33
+ - Entries referencing differing coverage have distinct
34
+ `coverage-assertion-ids` and `satisfied-pa-ids` (if present).
35
+ - Entries referencing the same coverage have the same
36
+ `coverage-assertion-ids` and `satisfied-pa-ids` (if present).
31
37
  )
32
38
 
33
- verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-32',
39
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-26',
40
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-32',
34
41
  'hl7.fhir.us.davinci-crd_2.2.1@resp-37',
35
42
  'hl7.fhir.us.davinci-crd_2.2.1@resp-47',
36
43
  'hl7.fhir.us.davinci-crd_2.2.1@resp-48',
37
44
  'hl7.fhir.us.davinci-crd_2.2.1@resp-52'
38
45
 
39
- input :coverage_info
46
+ input :invoked_hook
40
47
  input :mock_ehr_bundle, optional: true
48
+ output :coverage_info
41
49
 
42
- def find_extension_value(extension, url, *properties)
43
- found_extension = extension.extension.find { |ext| ext.url == url }
44
- return nil unless found_extension
45
-
46
- properties.reduce(found_extension) do |current, prop|
47
- return current unless current.respond_to?(prop)
48
-
49
- current.send(prop)
50
- end
51
- end
52
-
53
- def extract_and_group_coverage_info(resource)
54
- resource.extension.each_with_object({}) do |extension, grouped_extensions|
55
- next unless extension.url == COVERAGE_INFO_EXT_URL
56
-
57
- coverage_key = find_extension_value(extension, 'coverage', 'valueReference', 'reference')
58
- grouped_extensions[coverage_key] ||= []
59
- grouped_extensions[coverage_key] << extension
60
- end
61
- end
62
-
63
- # For the same coverage, ensure coverage-assertion-ids and satisfied-pa-ids are the same.
64
- # For different coverages, ensure coverage-assertion-ids and satisfied-pa-ids are distinct.
65
- def multiple_extensions_conformance_check(grouped_coverage_info, resource)
66
- resource_ref = "#{resource.resourceType}/#{resource.id}"
67
- assertion_ids_across_coverages = Set.new
68
- pa_ids_across_coverages = Set.new
69
-
70
- grouped_coverage_info.each do |coverage, extensions|
71
- coverage_assertion_ids = collect_extensions_id(extensions, 'coverage-assertion-id', 'valueString').uniq
72
- satisfied_pa_ids = collect_extensions_id(extensions, 'satisfied-pa-id', 'valueString').uniq.compact
73
- assert coverage_assertion_ids.length == 1,
74
- same_coverage_conformance_error_msg(resource_ref, coverage, 'coverage-assertion-ids')
75
-
76
- assert satisfied_pa_ids.length <= 1,
77
- same_coverage_conformance_error_msg(resource_ref, coverage, 'satisfied-pa-ids')
78
-
79
- assertion_id = coverage_assertion_ids.first
80
- assert !assertion_ids_across_coverages.include?(assertion_id),
81
- different_coverage_conformance_error_msg(resource_ref, 'coverage-assertion-ids')
82
- assertion_ids_across_coverages.add(assertion_id)
83
- pa_id = satisfied_pa_ids.first
84
- next unless pa_id
85
-
86
- assert !pa_ids_across_coverages.include?(pa_id),
87
- different_coverage_conformance_error_msg(resource_ref, 'satisfied-pa-ids')
88
- pa_ids_across_coverages.add(pa_id)
89
- end
90
- end
91
-
92
- def collect_extensions_id(extensions, url, *properties)
93
- extensions.map do |extension|
94
- find_extension_value(extension, url, *properties)
95
- end
96
- end
97
-
98
- def same_coverage_conformance_error_msg(resource_ref, coverage, id_name)
99
- "#{resource_ref}: extension has multiple repetitions of coverage `#{coverage}` with different #{id_name}."
100
- end
101
-
102
- def different_coverage_conformance_error_msg(resource_ref, id_name)
103
- "#{resource_ref}: extensions referencing differing coverage SHALL have distinct #{id_name}."
104
- end
105
-
106
- def normalize_value(value)
107
- case value
108
- when Hash
109
- value.transform_values { |child| normalize_value(child) }
110
- when Array
111
- value.map { |child| normalize_value(child) }.sort_by(&:to_json)
112
- else
113
- value
114
- end
115
- end
116
-
117
- def strip_coverage_info_extensions(resource_hash)
118
- normalized_hash = resource_hash.deep_dup
119
- return normalized_hash unless normalized_hash['extension'].is_a?(Array)
120
-
121
- normalized_hash['extension'] = normalized_hash['extension'].reject do |extension|
122
- extension['url'] == COVERAGE_INFO_EXT_URL
123
- end
124
- normalized_hash.delete('extension') if normalized_hash['extension'].empty?
125
- normalized_hash
126
- end
127
-
128
- def verify_only_coverage_info_changed(action)
129
- request = matching_request_for_action(action)
50
+ def verify_only_coverage_info_changed(action, request)
130
51
  source_resource = find_action_source_resource(action, request)
131
52
  updated_resource_hash = action['resource']
132
53
  resource_ref = "#{updated_resource_hash['resourceType']}/#{updated_resource_hash['id']}"
@@ -140,44 +61,69 @@ module DaVinciCRDTestKit
140
61
  return
141
62
  end
142
63
 
143
- source_resource_hash = source_resource.to_hash
64
+ return if only_coverage_information_changed?(source_resource.to_hash, updated_resource_hash)
144
65
 
145
- source_without_coverage_info = normalize_value(strip_coverage_info_extensions(source_resource_hash))
146
- updated_without_coverage_info = normalize_value(strip_coverage_info_extensions(updated_resource_hash))
66
+ add_message(
67
+ 'error',
68
+ "#{resource_ref}: resource content changed outside the coverage-information extension."
69
+ )
70
+ end
147
71
 
148
- assert source_without_coverage_info == updated_without_coverage_info,
149
- "#{resource_ref}: resource content changed outside the coverage-information extension."
72
+ def body_has_no_system_actions?(body)
73
+ !body.is_a?(Hash) ||
74
+ body['systemActions'].blank? ||
75
+ !body['systemActions'].is_a?(Array) ||
76
+ !body['systemActions'].all?(Hash)
150
77
  end
151
78
 
152
- def coverage_info_system_action_check(coverage_info_system_action)
153
- type = coverage_info_system_action['type']
154
- assert type, '`type` field is missing.'
155
- assert type == 'update', "`type` must be `update`, but was `#{type}`"
79
+ def coverage_info_actions(request)
80
+ return [] if request.status != 200
81
+
82
+ response_body = JSON.parse(request.response_body)
156
83
 
157
- resource = FHIR.from_contents(coverage_info_system_action['resource'].to_json)
158
- profile_url = structure_definition_map('v221')[resource.resourceType]
159
- resource_is_valid?(resource:, profile_url:)
84
+ return [] if body_has_no_system_actions?(response_body)
160
85
 
161
- grouped_coverage_info = extract_and_group_coverage_info(resource)
162
- multiple_extensions_conformance_check(grouped_coverage_info, resource)
163
- verify_only_coverage_info_changed(coverage_info_system_action)
86
+ response_body['systemActions'].select { |action| coverage_information_response_type? action }
87
+ rescue JSON::ParserError
88
+ []
164
89
  end
165
90
 
166
91
  run do
167
92
  load_tagged_requests(tested_hook_name)
168
- parsed_coverage_info = parse_json(coverage_info)
169
- error_messages = []
170
- parsed_coverage_info.each do |action|
171
- coverage_info_system_action_check(action)
172
- rescue Inferno::Exceptions::AssertionException => e
173
- error_messages << "Coverage Info system action `#{action}`: #{e.message}"
174
- end
175
93
 
176
- error_messages.each do |msg|
177
- messages << { type: 'error', message: msg }
94
+ all_coverage_info_actions = []
95
+ coverage_info_count = 0
96
+
97
+ successful_requests = requests.select { |request| request.status == 200 }
98
+
99
+ skip_if successful_requests.blank?,
100
+ 'No successful hook responses were received'
101
+
102
+ requests.each_with_index do |request, index|
103
+ actions = coverage_info_actions(request)
104
+
105
+ coverage_info_count += actions.length
106
+
107
+ perform_response_logical_model_validation(
108
+ nil,
109
+ actions,
110
+ request.request_body,
111
+ index,
112
+ '2.2.1'
113
+ )
114
+ actions.each { |action| verify_only_coverage_info_changed(action, request) }
115
+
116
+ all_coverage_info_actions.concat(actions)
178
117
  end
179
118
 
180
- assert_no_error_messages 'Some Coverage Info system actions are not valid.'
119
+ skip_if coverage_info_count.zero?,
120
+ "#{tested_hook_name} hook responses do not contain any Coverage Information system actions."
121
+
122
+ output coverage_info: all_coverage_info_actions.to_json
123
+
124
+ no_error_validation(
125
+ 'Not all Coverage Information system actions were valid. See messages for more information.'
126
+ )
181
127
  end
182
128
  end
183
129
  end
@@ -1,74 +1,118 @@
1
1
  require_relative '../../server_test_helper'
2
- require_relative '../../../cross_suite/suggestion_actions_validation'
3
2
  require_relative '../../server_hook_helper'
4
3
  require_relative '../../../cross_suite/cards_identification'
5
- require_relative '../../../cross_suite/cards_validation'
4
+ require_relative '../../../cross_suite/response_logical_model_validation'
6
5
 
7
6
  module DaVinciCRDTestKit
8
7
  module V221
9
8
  class CreateOrUpdateCoverageInfoResponseValidationTest < Inferno::Test
10
9
  include DaVinciCRDTestKit::ServerTestHelper
11
- include DaVinciCRDTestKit::SuggestionActionsValidation
12
10
  include DaVinciCRDTestKit::ServerHookHelper
13
11
  include DaVinciCRDTestKit::CardsIdentification
14
- include DaVinciCRDTestKit::CardsValidation
12
+ include DaVinciCRDTestKit::ResponseLogicalModelValidation
15
13
 
16
14
  title 'Create or Update Coverage Information cards and system actions are valid'
17
15
  id :crd_v221_create_or_update_coverage_info_response_validation
18
16
  description %(
19
- This test validates the Create or Update Coverage Information cards or system actions received from the
20
- CRD service, as per the specifications outlined in the [Da Vinci CRD Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#create-or-update-coverage-information).
17
+ This test validates the Create or Update Coverage Information cards or
18
+ system actions received from the CRD service, as per the specifications
19
+ outlined in the [Da Vinci CRD Implementation
20
+ Guide](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#update-coverage-records-response-type).
21
21
 
22
22
  - **Checking for Presence:**
23
- The test first checks if any Create or Update Coverage Information cards or system actions are present in
24
- the returned valid cards or valid system actions.
25
- - **For cards**: it ensures there are cards with a `suggestions` array containing a single suggestion,
26
- and the `actions` array of that suggestion has one `create` or `update` action for the `Coverage` resource.
27
- - **For system actions**: it checks for the presence of `create` or `update` actions for the `Coverage`
28
- resource.
23
+ The test first checks if any Create or Update Coverage Information
24
+ cards or system actions are present in the returned valid cards or
25
+ valid system actions.
26
+ - **For cards**: it ensures there are cards with a `suggestions` array
27
+ containing a single suggestion, and the `actions` array of that
28
+ suggestion has one `create` or `update` action for the `Coverage`
29
+ resource.
30
+ - **For system actions**: it checks for the presence of `create` or
31
+ `update` actions for the `Coverage` resource.
29
32
 
30
33
  - **Validating:**
31
- If any Create or Update Coverage Information cards or system actions are found, each `Coverage` resource is
32
- validated against the base FHIR Coverage resource.
34
+ If any Create or Update Coverage Information cards or system actions
35
+ are found, each `Coverage` resource is validated against the base FHIR
36
+ Coverage resource.
33
37
 
34
- If no Create or Update Coverage Information cards or system actions are received, the test is skipped.
38
+ If no Create or Update Coverage Information cards or system actions
39
+ are received, the test is skipped.
35
40
  )
36
41
 
37
- verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-71'
42
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-3',
43
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-71'
44
+
45
+ input :invoked_hook
38
46
 
39
47
  optional
40
- input :valid_cards_with_suggestions, :valid_system_actions
48
+
49
+ def body_has_no_cards?(body)
50
+ !body.is_a?(Hash) ||
51
+ body['cards'].blank? ||
52
+ !body['cards'].is_a?(Array) ||
53
+ !body['cards'].all?(Hash)
54
+ end
55
+
56
+ def update_coverage_cards(request)
57
+ return [] if request.status != 200
58
+
59
+ response_body = JSON.parse(request.response_body)
60
+
61
+ return [] if body_has_no_cards?(response_body)
62
+
63
+ response_body['cards'].select { |card| create_or_update_coverage_card_response_type? card }
64
+ rescue JSON::ParserError
65
+ []
66
+ end
67
+
68
+ def body_has_no_system_actions?(body)
69
+ !body.is_a?(Hash) ||
70
+ body['systemActions'].blank? ||
71
+ !body['systemActions'].is_a?(Array) ||
72
+ !body['systemActions'].all?(Hash)
73
+ end
74
+
75
+ def update_coverage_actions(request)
76
+ return [] if request.status != 200
77
+
78
+ response_body = JSON.parse(request.response_body)
79
+
80
+ return [] if body_has_no_system_actions?(response_body)
81
+
82
+ response_body['systemActions'].select { |action| create_or_update_coverage_action_response_type? action }
83
+ rescue JSON::ParserError
84
+ []
85
+ end
41
86
 
42
87
  run do
43
- parsed_cards = parse_json(valid_cards_with_suggestions)
44
- parsed_actions = parse_json(valid_system_actions)
88
+ load_tagged_requests(tested_hook_name)
45
89
 
46
- create_or_update_coverage_info_cards = parsed_cards.select do |card|
47
- create_or_update_coverage_card_response_type?(card)
48
- end
49
- create_or_update_coverage_info_actions = parsed_actions.select do |action|
50
- create_or_update_coverage_action_response_type?(action)
51
- end
90
+ update_coverage_count = 0
52
91
 
53
- skip_msg = "#{tested_hook_name} hook response does not contain any Create or Update Coverage Information " \
54
- 'cards or system actions.'
55
- skip_if create_or_update_coverage_info_cards.blank? && create_or_update_coverage_info_actions.blank?, skip_msg
92
+ successful_requests = requests.select { |request| request.status == 200 }
56
93
 
57
- if create_or_update_coverage_info_actions.present?
58
- actions_check(create_or_update_coverage_info_actions, ig_version: 'v221')
59
- end
94
+ skip_if successful_requests.blank?,
95
+ 'No successful hook responses were received'
60
96
 
61
- if create_or_update_coverage_info_cards.present?
62
- create_or_update_coverage_info_cards.each do |card|
63
- actions_check(card['suggestions'].first['actions'].select do |action|
64
- create_or_update_coverage_action_response_type?(action)
65
- end, ig_version: 'v221')
97
+ requests.each_with_index do |request, index|
98
+ cards = update_coverage_cards(request)
99
+ actions = update_coverage_actions(request)
66
100
 
67
- create_or_update_coverage_check(card)
68
- end
101
+ update_coverage_count += cards.length + actions.length
102
+
103
+ perform_response_logical_model_validation(
104
+ cards,
105
+ actions,
106
+ request.request_body,
107
+ index,
108
+ '2.2.1'
109
+ )
69
110
  end
70
111
 
71
- no_error_validation('Some Create or Update Coverage Information received are not valid.')
112
+ skip_if update_coverage_count.zero?,
113
+ "#{tested_hook_name} hook responses do not contain any Update Coverage Records cards or system actions."
114
+
115
+ no_error_validation('Not all Update Coverage Records responses were valid. See messages for more information.')
72
116
  end
73
117
  end
74
118
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../../server_test_helper'
2
2
  require_relative '../../server_hook_helper'
3
3
  require_relative '../../../cross_suite/cards_identification'
4
- require_relative '../../../cross_suite/cards_validation'
4
+ require_relative '../../../cross_suite/response_logical_model_validation'
5
5
 
6
6
  module DaVinciCRDTestKit
7
7
  module V221
@@ -9,38 +9,80 @@ module DaVinciCRDTestKit
9
9
  include DaVinciCRDTestKit::ServerTestHelper
10
10
  include DaVinciCRDTestKit::ServerHookHelper
11
11
  include DaVinciCRDTestKit::CardsIdentification
12
- include DaVinciCRDTestKit::CardsValidation
12
+ include DaVinciCRDTestKit::ResponseLogicalModelValidation
13
13
 
14
14
  title 'External Reference cards are valid'
15
15
  id :crd_v221_external_reference_card_validation
16
16
  description %(
17
- This test verifies the presence of valid External Reference cards within the list of valid cards
18
- returned by the CRD service.
19
- As per the [Da Vinci CRD Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#external-reference),
20
- External Reference cards must contain links with the type set to `absolute`.
21
- This test checks for the presence of any External Reference cards by verifying:
17
+ This test verifies the presence of valid External Reference cards within
18
+ the CRD service responses.
19
+
20
+ As per the [Da Vinci CRD Implementation
21
+ Guide](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#external-reference-response-type),
22
+ External Reference cards must contain links with the type set to
23
+ `absolute`. This test checks for the presence of any External Reference
24
+ cards by verifying:
22
25
  - The presence of a `links` array within each card.
23
26
  - That every link in the `links` array of a card is of type `absolute`.
27
+
28
+ The test will be skipped if no External Reference cards are found within
29
+ the returned valid cards.
24
30
  )
25
31
 
26
- verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-22'
32
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-19',
33
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-22'
34
+
35
+ input :invoked_hook
27
36
 
28
- input :valid_cards_with_links
29
37
  optional
30
38
 
39
+ def body_has_no_cards?(body)
40
+ !body.is_a?(Hash) ||
41
+ body['cards'].blank? ||
42
+ !body['cards'].is_a?(Array) ||
43
+ !body['cards'].all?(Hash)
44
+ end
45
+
46
+ def external_reference_cards(request)
47
+ return [] if request.status != 200
48
+
49
+ response_body = JSON.parse(request.response_body)
50
+
51
+ return [] if body_has_no_cards?(response_body)
52
+
53
+ response_body['cards'].select { |card| external_reference_response_type? card }
54
+ rescue JSON::ParserError
55
+ []
56
+ end
57
+
31
58
  run do
32
- parsed_cards = parse_json(valid_cards_with_links)
33
- external_reference_cards = parsed_cards.select { |card| external_reference_response_type?(card) }
59
+ load_tagged_requests(tested_hook_name)
60
+
61
+ external_reference_count = 0
62
+
63
+ successful_requests = requests.select { |request| request.status == 200 }
34
64
 
35
- skip_if external_reference_cards.blank?,
36
- "#{tested_hook_name} hook response does not contain any External Reference cards."
65
+ skip_if successful_requests.blank?,
66
+ 'No successful hook responses were received'
37
67
 
38
- external_reference_cards.each do |card|
39
- external_reference_card_check(card)
68
+ requests.each_with_index do |request, index|
69
+ cards = external_reference_cards(request)
70
+
71
+ external_reference_count += cards.length
72
+
73
+ perform_response_logical_model_validation(
74
+ cards,
75
+ nil,
76
+ request.request_body,
77
+ index,
78
+ '2.2.1'
79
+ )
40
80
  end
41
81
 
42
- assert messages.blank?,
43
- 'Not all External Reference cards were valid. See messages for more information.'
82
+ skip_if external_reference_count.zero?,
83
+ "#{tested_hook_name} hook responses do not contain any External Reference cards."
84
+
85
+ no_error_validation('Not all External Reference cards were valid. See messages for more information.')
44
86
  end
45
87
  end
46
88
  end