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
@@ -1,38 +1,42 @@
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 FormCompletionResponseValidationTest < 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 'Request Form Completion cards and system actions are valid'
17
15
  id :crd_v221_request_form_completion_response_validation
18
16
  description %(
19
- This test validates the Request Form Completion cards or system actions received from the CRD service,
20
- as per the specifications outlined in the [Da Vinci CRD Implementation Guide](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion).
17
+ This test validates the Request Form Completion cards or system actions
18
+ received from the CRD service, as per the specifications outlined in the
19
+ [Da Vinci CRD Implementation
20
+ Guide](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#request-form-completion-response-type).
21
21
 
22
22
  - **Checking for Presence:**
23
- The test begins by verifying whether any Request Form Completion cards or system actions are present.
24
- - **For cards:** It ensures that there are cards with `suggestions` containing `create` actions
25
- for the `Task` resource, specifically:
23
+ The test begins by verifying whether any Request Form Completion cards
24
+ or system actions are present.
25
+ - **For cards:** It ensures that there are cards with `suggestions`
26
+ containing `create` actions for the `Task` resource, specifically:
26
27
  - The `Task` must have a `code` of `complete-questionnaire`.
27
- - The `Task` should include an input of type `text` (`Task.input.type.text`) labeled as `questionnaire`
28
- and associated with a valid canonical URL (`Task.input.valueCanonical`).
29
- - **For system actions:** It checks for the presence of `create` actions for the `Task` resource with
30
- the characteristics described above.
28
+ - The `Task` should include an input of type `text`
29
+ (`Task.input.type.text`) labeled as `questionnaire` and associated
30
+ with a valid canonical URL (`Task.input.valueCanonical`).
31
+ - **For system actions:** It checks for the presence of `create`
32
+ actions for the `Task` resource with the characteristics described
33
+ above.
31
34
 
32
35
  - **Validating:**
33
- If any Request Form Completion cards or system actions are found, the test proceeds to validate them.
34
- Each `Task` resource is validated against the [CRD Questionnaire Task
35
- profile](http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-taskquestionnaire).
36
+ If any Request Form Completion cards or system actions are found, the
37
+ test proceeds to validate them. Each `Task` resource is validated
38
+ against the [CRD Questionnaire Task
39
+ profile](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/StructureDefinition-profile-taskquestionnaire.html).
36
40
  Additionally, if any actions for the creation of a `Questionnaire` are
37
41
  found, the test verifies that they include the
38
42
  `davinci-crd.if-none-exist` extension.
@@ -41,50 +45,81 @@ module DaVinciCRDTestKit
41
45
  )
42
46
 
43
47
  verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@dev-20',
48
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-3',
44
49
  'hl7.fhir.us.davinci-crd_2.2.1@resp-62',
45
50
  'hl7.fhir.us.davinci-crd_2.2.1@resp-65'
46
51
 
52
+ input :invoked_hook
53
+
47
54
  optional
48
- input :valid_cards_with_suggestions, :valid_system_actions
55
+
56
+ def body_has_no_cards?(body)
57
+ !body.is_a?(Hash) ||
58
+ body['cards'].blank? ||
59
+ !body['cards'].is_a?(Array) ||
60
+ !body['cards'].all?(Hash)
61
+ end
62
+
63
+ def form_completion_cards(request)
64
+ return [] if request.status != 200
65
+
66
+ response_body = JSON.parse(request.response_body)
67
+
68
+ return [] if body_has_no_cards?(response_body)
69
+
70
+ response_body['cards'].select { |card| form_completion_card_response_type? card }
71
+ rescue JSON::ParserError
72
+ []
73
+ end
74
+
75
+ def body_has_no_system_actions?(body)
76
+ !body.is_a?(Hash) ||
77
+ body['systemActions'].blank? ||
78
+ !body['systemActions'].is_a?(Array) ||
79
+ !body['systemActions'].all?(Hash)
80
+ end
81
+
82
+ def form_completion_actions(request)
83
+ return [] if request.status != 200
84
+
85
+ response_body = JSON.parse(request.response_body)
86
+
87
+ return [] if body_has_no_system_actions?(response_body)
88
+
89
+ response_body['systemActions'].select { |action| form_completion_action_response_type? action }
90
+ rescue JSON::ParserError
91
+ []
92
+ end
49
93
 
50
94
  run do
51
- parsed_cards = parse_json(valid_cards_with_suggestions)
52
- parsed_actions = parse_json(valid_system_actions)
95
+ load_tagged_requests(tested_hook_name)
53
96
 
54
- form_completion_cards = parsed_cards.select { |card| form_completion_card_response_type?(card) }
55
- form_completion_actions = parsed_actions.select { |action| form_completion_action_response_type?(action) }
97
+ form_completion_count = 0
56
98
 
57
- skip_if form_completion_cards.blank? && form_completion_actions.blank?,
58
- "#{tested_hook_name} hook response does not contain any Request Form Completion cards " \
59
- 'or system actions.'
99
+ successful_requests = requests.select { |request| request.status == 200 }
60
100
 
61
- actions_check(form_completion_actions, ig_version: 'v221') if form_completion_actions.present?
101
+ skip_if successful_requests.blank?,
102
+ 'No successful hook responses were received'
62
103
 
63
- if form_completion_cards.present?
64
- form_completion_cards.each do |card|
65
- actions =
66
- card['suggestions']
67
- .flat_map { |suggestion| suggestion['actions'] }
68
- .compact
69
- .select { |action| form_completion_action_response_type? action }
104
+ requests.each_with_index do |request, index|
105
+ cards = form_completion_cards(request)
106
+ actions = form_completion_actions(request)
70
107
 
71
- actions_check(actions, ig_version: 'v221')
108
+ form_completion_count += cards.length + actions.length
72
109
 
73
- form_completion_check(card)
74
- end
110
+ perform_response_logical_model_validation(
111
+ cards,
112
+ actions,
113
+ request.request_body,
114
+ index,
115
+ '2.2.1'
116
+ )
75
117
  end
76
118
 
77
- questionnaire_create_actions =
78
- parsed_actions.select { |action| create_questionnaire_action_response_type?(action) } +
79
- form_completion_cards
80
- .flat_map { |card| card['suggestions'] }
81
- .flat_map { |suggestion| suggestion['actions'] }
82
- .compact
83
- .select { |action| create_questionnaire_action_response_type?(action) }
84
-
85
- questionnaire_create_actions.each { |action| questionnaire_creation_check(action) }
119
+ skip_if form_completion_count.zero?,
120
+ "#{tested_hook_name} hook responses do not contain any Request Form Completion cards or system actions."
86
121
 
87
- no_error_validation('Some Request Form Completion received are not valid.')
122
+ no_error_validation('Not all Request Form Completion responses were valid. See messages for more information.')
88
123
  end
89
124
  end
90
125
  end
@@ -34,6 +34,43 @@ module DaVinciCRDTestKit
34
34
  fallback_source_resource(request_body, target_type, target_id)
35
35
  end
36
36
 
37
+ def only_coverage_information_changed?(source_resource_hash, updated_resource_hash)
38
+ return true if coverage_information_extension_only_payload?(updated_resource_hash)
39
+
40
+ source_without_coverage_info = normalize_value(strip_coverage_info_extensions(source_resource_hash))
41
+ updated_without_coverage_info = normalize_value(strip_coverage_info_extensions(updated_resource_hash))
42
+
43
+ source_without_coverage_info == updated_without_coverage_info
44
+ end
45
+
46
+ def coverage_information_extension_only_payload?(resource_hash)
47
+ return false unless resource_hash.is_a?(Hash)
48
+
49
+ strip_coverage_info_extensions(resource_hash).keys.all? { |key| ['resourceType', 'id', 'meta'].include?(key) }
50
+ end
51
+
52
+ def strip_coverage_info_extensions(resource_hash)
53
+ normalized_hash = resource_hash.deep_dup
54
+ return normalized_hash unless normalized_hash['extension'].is_a?(Array)
55
+
56
+ normalized_hash['extension'] = normalized_hash['extension'].reject do |extension|
57
+ extension['url'] == DaVinciCRDTestKit::CardsIdentification::COVERAGE_INFO_EXT_URL
58
+ end
59
+ normalized_hash.delete('extension') if normalized_hash['extension'].empty?
60
+ normalized_hash
61
+ end
62
+
63
+ def normalize_value(value)
64
+ case value
65
+ when Hash
66
+ value.transform_values { |child| normalize_value(child) }
67
+ when Array
68
+ value.map { |child| normalize_value(child) }.sort_by(&:to_json)
69
+ else
70
+ value
71
+ end
72
+ end
73
+
37
74
  def find_appointment_book_resource(request_body, target_type, target_id)
38
75
  appointments_bundle = request_body.dig('context', 'appointments')
39
76
  find_resource_in_bundle(appointments_bundle, target_type, target_id) ||
@@ -1,6 +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/response_logical_model_validation'
4
5
 
5
6
  module DaVinciCRDTestKit
6
7
  module V221
@@ -8,24 +9,74 @@ module DaVinciCRDTestKit
8
9
  include DaVinciCRDTestKit::ServerTestHelper
9
10
  include DaVinciCRDTestKit::ServerHookHelper
10
11
  include DaVinciCRDTestKit::CardsIdentification
12
+ include DaVinciCRDTestKit::ResponseLogicalModelValidation
11
13
 
12
14
  title 'Instructions cards are valid'
13
15
  id :crd_v221_valid_instructions_card_received
14
16
  description %(
15
- This test validates that an [Instructions](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#instructions)
17
+ This test validates that an
18
+ [Instructions](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#instructions-response-type)
16
19
  card was received. It does so by:
17
- - Checking for the presence of a valid card that does not contain the `links` field and the `suggestions` field.
20
+ - Checking for the presence of a valid card that does not contain the
21
+ `links` field and the `suggestions` field.
22
+
23
+ The test will be skipped if no Instructions cards are found within the
24
+ returned valid cards.
18
25
  )
19
26
 
20
27
  verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-24'
21
28
 
22
- input :valid_cards
29
+ input :invoked_hook
30
+
23
31
  optional
24
32
 
33
+ def body_has_no_cards?(body)
34
+ !body.is_a?(Hash) ||
35
+ body['cards'].blank? ||
36
+ !body['cards'].is_a?(Array) ||
37
+ !body['cards'].all?(Hash)
38
+ end
39
+
40
+ def instructions_cards(request)
41
+ return [] if request.status != 200
42
+
43
+ response_body = JSON.parse(request.response_body)
44
+
45
+ return [] if body_has_no_cards?(response_body)
46
+
47
+ response_body['cards'].select { |card| instructions_response_type? card }
48
+ rescue JSON::ParserError
49
+ []
50
+ end
51
+
25
52
  run do
26
- parsed_cards = parse_json(valid_cards)
27
- instructions_card = parsed_cards.find { |card| instructions_response_type?(card) }
28
- assert instructions_card, "#{tested_hook_name} hook response did not contain an Instructions card."
53
+ load_tagged_requests(tested_hook_name)
54
+
55
+ instructions_count = 0
56
+
57
+ successful_requests = requests.select { |request| request.status == 200 }
58
+
59
+ skip_if successful_requests.blank?,
60
+ 'No successful hook responses were received'
61
+
62
+ requests.each_with_index do |request, index|
63
+ cards = instructions_cards(request)
64
+
65
+ instructions_count += cards.length
66
+
67
+ perform_response_logical_model_validation(
68
+ cards,
69
+ nil,
70
+ request.request_body,
71
+ index,
72
+ '2.2.1'
73
+ )
74
+ end
75
+
76
+ skip_if instructions_count.zero?,
77
+ "#{tested_hook_name} hook responses do not contain any Instructions cards."
78
+
79
+ no_error_validation('Not all Instructions cards were valid. See messages for more information.')
29
80
  end
30
81
  end
31
82
  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,40 +9,79 @@ 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 'Launch SMART Application cards are valid'
15
15
  id :crd_v221_launch_smart_app_card_validation
16
16
  description %(
17
- This test verifies the presence of valid Launch SMART Application 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#launch-smart-application),
20
- Launch SMART Application cards must contain links with the type set to `smart`.
21
- This test checks for the presence of any Launch SMART Application cards by verifying:
17
+ This test verifies the presence of valid Launch SMART Application cards
18
+ within 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#launch-smart-application-response-type),
22
+ Launch SMART Application cards must contain links with the type set to
23
+ `smart`. This test checks for the presence of any Launch SMART
24
+ Application cards by verifying:
22
25
  - The existence of a `links` array within each card.
23
26
  - That every link in the `links` array of a card is of type `smart`.
24
27
 
25
- The test will be skipped if no Launch SMART Application cards are found within the returned valid cards.
28
+ The test will be skipped if no Launch SMART Application cards are found
29
+ within the returned valid cards.
26
30
  )
27
31
 
28
32
  verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-72'
29
33
 
34
+ input :invoked_hook
35
+
30
36
  optional
31
- input :valid_cards_with_links
37
+
38
+ def body_has_no_cards?(body)
39
+ !body.is_a?(Hash) ||
40
+ body['cards'].blank? ||
41
+ !body['cards'].is_a?(Array) ||
42
+ !body['cards'].all?(Hash)
43
+ end
44
+
45
+ def launch_smart_app_cards(request)
46
+ return [] if request.status != 200
47
+
48
+ response_body = JSON.parse(request.response_body)
49
+
50
+ return [] if body_has_no_cards?(response_body)
51
+
52
+ response_body['cards'].select { |card| launch_smart_app_response_type? card }
53
+ rescue JSON::ParserError
54
+ []
55
+ end
32
56
 
33
57
  run do
34
- parsed_cards = parse_json(valid_cards_with_links)
35
- launch_smart_app_cards = parsed_cards.select { |card| launch_smart_app_response_type?(card) }
58
+ load_tagged_requests(tested_hook_name)
59
+
60
+ launch_smart_app_count = 0
61
+
62
+ successful_requests = requests.select { |request| request.status == 200 }
36
63
 
37
- skip_if launch_smart_app_cards.blank?,
38
- "#{tested_hook_name} hook response does not contain any Launch SMART App cards."
64
+ skip_if successful_requests.blank?,
65
+ 'No successful hook responses were received'
39
66
 
40
- launch_smart_app_cards.each do |card|
41
- smart_app_card_check(card)
67
+ requests.each_with_index do |request, index|
68
+ cards = launch_smart_app_cards(request)
69
+
70
+ launch_smart_app_count += cards.length
71
+
72
+ perform_response_logical_model_validation(
73
+ cards,
74
+ nil,
75
+ request.request_body,
76
+ index,
77
+ '2.2.1'
78
+ )
42
79
  end
43
80
 
44
- assert messages.blank?,
45
- 'Not all Launch SMART App cards were valid. See messages for more information.'
81
+ skip_if launch_smart_app_count.zero?,
82
+ "#{tested_hook_name} hook responses do not contain any Launch SMART App cards."
83
+
84
+ no_error_validation('Not all Launch SMART App cards were valid. See messages for more information.')
46
85
  end
47
86
  end
48
87
  end
@@ -1,53 +1,80 @@
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 ProposeAlternateRequestCardValidationTest < 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 'Propose Alternate Request cards are valid'
17
15
  id :crd_v221_propose_alternate_request_card_validation
18
- description %(
19
- This test validates that all [Propose Alternate Request](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#propose-alternate-request)
20
- cards received are valid. It checks for the presence of a card's suggestion
21
- with a single action with `Action.type` of `update` or a card with at least
22
- two actions, one with `Action.type` of `delete` and the other with
23
- `Action.type` of `create`.
16
+ description %( This test validates that all [Propose Alternate
17
+ Request](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/cards.html#propose-alternate-request-response-type)
18
+ cards received are valid. It checks for the presence of a card's
19
+ suggestion with a single action with `Action.type` of `update` or a card
20
+ with at least two actions, one with `Action.type` of `delete` and the
21
+ other with `Action.type` of `create`.
24
22
  )
25
23
  optional
26
24
 
27
- verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-55',
25
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-3',
26
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-55',
28
27
  'hl7.fhir.us.davinci-crd_2.2.1@resp-56'
29
28
 
30
- input :valid_cards_with_suggestions, :contexts
29
+ input :invoked_hook
30
+
31
+ def body_has_no_cards?(body)
32
+ !body.is_a?(Hash) ||
33
+ body['cards'].blank? ||
34
+ !body['cards'].is_a?(Array) ||
35
+ !body['cards'].all?(Hash)
36
+ end
37
+
38
+ def alternate_request_cards(request)
39
+ return [] if request.status != 200
40
+
41
+ response_body = JSON.parse(request.response_body)
42
+
43
+ return [] if body_has_no_cards?(response_body)
44
+
45
+ response_body['cards'].select { |card| propose_alternative_request_response_type? card }
46
+ rescue JSON::ParserError
47
+ []
48
+ end
31
49
 
32
50
  run do
33
- parsed_cards = parse_json(valid_cards_with_suggestions)
34
- parsed_contexts = parse_json(contexts)
35
- proposed_alternate_cards = parsed_cards.filter do |card|
36
- propose_alternative_request_response_type?(card)
37
- end
51
+ load_tagged_requests(tested_hook_name)
52
+
53
+ alternate_request_count = 0
38
54
 
39
- skip_if proposed_alternate_cards.blank?,
40
- "#{tested_hook_name} hook response does not contain a Propose Alternate Request card."
55
+ successful_requests = requests.select { |request| request.status == 200 }
41
56
 
42
- proposed_alternate_cards.each do |card|
43
- card['suggestions'].each do |suggestion|
44
- actions_check(suggestion['actions'], parsed_contexts, ig_version: 'v221')
45
- end
57
+ skip_if successful_requests.blank?,
58
+ 'No successful hook responses were received'
46
59
 
47
- propose_alternate_request_check(card)
60
+ requests.each_with_index do |request, index|
61
+ cards = alternate_request_cards(request)
62
+
63
+ alternate_request_count += cards.length
64
+
65
+ perform_response_logical_model_validation(
66
+ cards,
67
+ nil,
68
+ request.request_body,
69
+ index,
70
+ '2.2.1'
71
+ )
48
72
  end
49
73
 
50
- no_error_validation('Some Propose Alternate Request cards are not valid.')
74
+ skip_if alternate_request_count.zero?,
75
+ "#{tested_hook_name} hook responses do not contain any Propose Alternate Request cards."
76
+
77
+ no_error_validation('Not all Propose Alternate Request cards were valid. See messages for more information')
51
78
  end
52
79
  end
53
80
  end
@@ -12,83 +12,49 @@ module DaVinciCRDTestKit
12
12
  title 'Service responses contain valid cards and systemActions'
13
13
  id :crd_v221_service_response_validation
14
14
  description %(
15
- As per the [CDS Hooks spec section on CDS Service
16
- Response](https://cds-hooks.hl7.org/2.0/#cds-service-response), a
17
- successful server's response to a service request must be a JSON object
18
- containing a `cards` array and optionally a `systemActions` array.
15
+ This test validates the responses against the [CRD v2.2.1 logical
16
+ models](https://hl7.org/fhir/us/davinci-crd/2.2.1/en/artifacts.html#4).
19
17
 
20
- Each card must contain the following required fields: `summary`, `indicator`, and `source`.
21
- The required fields must have a valid data structure.
18
+ This test implements [corrections to errors in the logical
19
+ models](https://github.com/inferno-framework/davinci-crd-test-kit/wiki/Logical-Model-Validation-Changes).
22
20
  )
23
21
 
24
22
  verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@resp-4',
25
23
  'hl7.fhir.us.davinci-crd_2.2.1@resp-5',
26
- 'hl7.fhir.us.davinci-crd_2.2.1@resp-13',
27
- 'hl7.fhir.us.davinci-crd_2.2.1@resp-22',
28
- 'hl7.fhir.us.davinci-crd_2.2.1@resp-24',
29
- 'hl7.fhir.us.davinci-crd_2.2.1@resp-56'
24
+ 'hl7.fhir.us.davinci-crd_2.2.1@resp-13'
30
25
 
31
26
  input :invoked_hook
32
- output :valid_cards, :valid_system_actions
33
-
34
- SYSTEM_ACTIONS_HOOK_NAMES = ['appointment-book', 'order-sign', 'order-dispatch'].freeze
35
-
36
- def valid_cards
37
- @valid_cards ||= []
38
- end
39
-
40
- def valid_system_actions
41
- @valid_system_actions ||= []
42
- end
43
-
44
- def system_actions_check(system_actions)
45
- system_actions.each do |action|
46
- current_error_count = messages.count { |msg| msg[:type] == 'error' }
47
- action_fields_validation(action, ig_version: 'v221')
48
- valid_system_actions << action if current_error_count == messages.count { |msg| msg[:type] == 'error' }
49
- end
50
- end
51
-
52
- def perform_system_actions_validation(system_actions, response_index)
53
- if SYSTEM_ACTIONS_HOOK_NAMES.include?(invoked_hook) && system_actions.nil?
54
- msg = "Server response #{response_index + 1} did not have the `systemActions` field, " \
55
- "which must be present for the `#{invoked_hook}` hook."
56
- add_message('error', msg)
57
- end
58
- return if system_actions.nil?
59
-
60
- unless system_actions.is_a?(Array)
61
- add_message('error', "`systemActions` of server response #{response_index + 1} is not an array.")
62
- return
63
- end
64
- system_actions_check(system_actions)
65
- end
66
27
 
67
28
  run do
68
29
  load_tagged_requests(tested_hook_name)
30
+
69
31
  skip_if requests.blank?, "No #{tested_hook_name} request was made in a previous test as expected."
32
+
70
33
  successful_requests = requests.select { |request| request.status == 200 }
34
+
71
35
  skip_if successful_requests.empty?, 'All service requests were unsuccessful.'
72
36
 
73
37
  info do
74
38
  unsuccessful_count = (requests - successful_requests).length
39
+
75
40
  assert unsuccessful_count.zero?, "#{unsuccessful_count} out of #{requests.length} requests were unsuccessful"
76
41
  end
77
42
 
78
43
  successful_requests.each_with_index do |request, index|
79
44
  service_response = JSON.parse(request.response_body)
80
- perform_cards_validation(service_response['cards'], service_response['systemActions'].present?, 'v221', index)
81
45
 
82
- perform_system_actions_validation(service_response['systemActions'], index)
83
-
84
- # perform_response_logical_model_validation(service_response['cards'], service_response['systemActions'],
85
- # index)
46
+ conforms_to_logical_model?(
47
+ service_response,
48
+ 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/CRDHooksResponseBase|2.2.1',
49
+ message_prefix: "Request #{index}: "
50
+ )
86
51
  rescue JSON::ParserError
87
52
  add_message('error', "Invalid JSON: server response #{index + 1} is not valid JSON.")
88
53
  end
89
54
 
90
- output valid_system_actions: valid_system_actions.to_json
91
- output valid_cards: valid_cards.to_json
55
+ messages.reject! do |message|
56
+ message[:type] == 'error' && message[:message].end_with?('Resource requires an id, but none is present')
57
+ end
92
58
 
93
59
  no_error_validation('Some service responses are not valid. Check messages for issues found.')
94
60
  end
@@ -60,7 +60,7 @@ module DaVinciCRDTestKit
60
60
  next
61
61
  end
62
62
 
63
- next if coverage_info_response?(response_body)
63
+ next if coverage_info_system_action_response?(response_body)
64
64
 
65
65
  add_message(
66
66
  'error',
@@ -60,7 +60,7 @@ module DaVinciCRDTestKit
60
60
  next
61
61
  end
62
62
 
63
- next if coverage_info_response?(response_body)
63
+ next if coverage_info_system_action_response?(response_body)
64
64
 
65
65
  add_message(
66
66
  'error',
@@ -60,7 +60,7 @@ module DaVinciCRDTestKit
60
60
  next
61
61
  end
62
62
 
63
- next if coverage_info_response?(response_body)
63
+ next if coverage_info_system_action_response?(response_body)
64
64
 
65
65
  add_message(
66
66
  'error',
@@ -1,4 +1,4 @@
1
1
  module DaVinciCRDTestKit
2
- VERSION = '0.14.0'.freeze
3
- LAST_UPDATED = '2026-06-02'.freeze # TODO: update next release
2
+ VERSION = '0.14.1'.freeze
3
+ LAST_UPDATED = '2026-07-13'.freeze # TODO: update next release
4
4
  end