davinci_crd_test_kit 0.12.0 → 0.12.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/inferno_crd_server_suite.json.erb +6 -1
  3. data/config/presets/ri_crd_request_generator.json.erb +1 -1
  4. data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +13 -2
  5. data/lib/davinci_crd_test_kit/client_hooks_group.rb +11 -4
  6. data/lib/davinci_crd_test_kit/client_registration_group.rb +26 -0
  7. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +14 -3
  8. data/lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb +6 -1
  9. data/lib/davinci_crd_test_kit/client_tests/client_encounter_discharge_group.rb +2 -0
  10. data/lib/davinci_crd_test_kit/client_tests/client_encounter_start_group.rb +3 -0
  11. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +1 -0
  12. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +2 -2
  13. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +1 -0
  14. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +1 -1
  15. data/lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb +10 -1
  16. data/lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb +11 -1
  17. data/lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb +11 -1
  18. data/lib/davinci_crd_test_kit/client_tests/client_registration_verification_test.rb +88 -0
  19. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +12 -3
  20. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +13 -3
  21. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +1 -1
  22. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +1 -0
  23. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +1 -0
  24. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +14 -3
  25. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +13 -3
  26. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +13 -3
  27. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +11 -8
  28. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +3 -3
  29. data/lib/davinci_crd_test_kit/crd_client_suite.rb +30 -2
  30. data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +8 -0
  31. data/lib/davinci_crd_test_kit/requirements/davinci-crd-test-kit_out_of_scope_requirements.csv +1 -0
  32. data/lib/davinci_crd_test_kit/requirements/davinci-crd-test-kit_requirements.csv +368 -0
  33. data/lib/davinci_crd_test_kit/requirements/generated/davinci-crd-test-kit_requirements_coverage.csv +366 -0
  34. data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +2 -0
  35. data/lib/davinci_crd_test_kit/server_discovery_group.rb +2 -1
  36. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +1 -0
  37. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +1 -0
  38. data/lib/davinci_crd_test_kit/server_hooks_group.rb +2 -0
  39. data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +2 -0
  40. data/lib/davinci_crd_test_kit/server_order_select_group.rb +1 -0
  41. data/lib/davinci_crd_test_kit/server_order_sign_group.rb +2 -0
  42. data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +3 -0
  43. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +3 -0
  44. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +1 -0
  45. data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +1 -0
  46. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +1 -0
  47. data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +1 -0
  48. data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +1 -0
  49. data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +3 -3
  50. data/lib/davinci_crd_test_kit/suggestion_actions_validation.rb +1 -1
  51. data/lib/davinci_crd_test_kit/version.rb +2 -2
  52. data/lib/davinci_crd_test_kit.rb +1 -0
  53. data/lib/inferno_requirements_tools/ext/inferno_core/runnable.rb +22 -0
  54. data/lib/inferno_requirements_tools/tasks/requirements_coverage.rb +284 -0
  55. data/lib/requirements_config.yaml +18 -0
  56. metadata +10 -2
@@ -41,6 +41,8 @@ module DaVinciCRDTestKit
41
41
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
42
42
  optional
43
43
  )
44
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@164', 'hl7.fhir.us.davinci-crd_2.0.1@168',
45
+ 'hl7.fhir.us.davinci-crd_2.0.1@170', 'hl7.fhir.us.davinci-crd_2.0.1@184'
44
46
 
45
47
  config options: { hook_name: APPOINTMENT_BOOK_TAG }
46
48
  run_as_group
@@ -18,7 +18,7 @@ module DaVinciCRDTestKit
18
18
 
19
19
  # Test Methodology
20
20
 
21
- This test sequence accesses the CRD server Dicovery endpoint at /cds-services using a GET request.
21
+ This test sequence accesses the CRD server Discovery endpoint at /cds-services using a GET request.
22
22
  It parses the response and verifies that:
23
23
  - The Discovery endpoint is TLS secured.
24
24
  - The Discovery endpoint is available at `{baseURL}/cds-services`.
@@ -45,6 +45,7 @@ module DaVinciCRDTestKit
45
45
 
46
46
  This test verifies that the CRD server is using TLS 1.2 or higher.
47
47
  DESCRIPTION
48
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@89'
48
49
  id :crd_server_tls_version_stu2
49
50
 
50
51
  config(
@@ -35,6 +35,7 @@ module DaVinciCRDTestKit
35
35
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
36
36
  optional
37
37
  )
38
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@168', 'hl7.fhir.us.davinci-crd_2.0.1@196'
38
39
 
39
40
  config options: { hook_name: ENCOUNTER_DISCHARGE_TAG }
40
41
  run_as_group
@@ -35,6 +35,7 @@ module DaVinciCRDTestKit
35
35
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
36
36
  optional
37
37
  )
38
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@168', 'hl7.fhir.us.davinci-crd_2.0.1@185'
38
39
 
39
40
  config options: { hook_name: ENCOUNTER_START_TAG }
40
41
  run_as_group
@@ -51,6 +51,8 @@ module DaVinciCRDTestKit
51
51
  - Each [CRD response type](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#potential-crd-response-types)
52
52
  returned is valid - optional for some response types. See the individual test groups for more details.
53
53
  )
54
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@4', 'hl7.fhir.us.davinci-crd_2.0.1@152',
55
+ 'hl7.fhir.us.davinci-crd_2.0.1@153'
54
56
 
55
57
  group from: :crd_server_appointment_book,
56
58
  optional: true
@@ -41,6 +41,8 @@ module DaVinciCRDTestKit
41
41
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
42
42
  optional
43
43
  )
44
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@164', 'hl7.fhir.us.davinci-crd_2.0.1@168',
45
+ 'hl7.fhir.us.davinci-crd_2.0.1@204', 'hl7.fhir.us.davinci-crd_2.0.1@207'
44
46
 
45
47
  config options: { hook_name: ORDER_DISPATCH_TAG }
46
48
  run_as_group
@@ -41,6 +41,7 @@ module DaVinciCRDTestKit
41
41
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
42
42
  optional
43
43
  )
44
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@168', 'hl7.fhir.us.davinci-crd_2.0.1@208'
44
45
 
45
46
  config options: { hook_name: ORDER_SELECT_TAG }
46
47
  run_as_group
@@ -47,6 +47,8 @@ module DaVinciCRDTestKit
47
47
  - [Request form completion](https://hl7.org/fhir/us/davinci-crd/STU2/cards.html#request-form-completion) -
48
48
  optional
49
49
  )
50
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@164', 'hl7.fhir.us.davinci-crd_2.0.1@168',
51
+ 'hl7.fhir.us.davinci-crd_2.0.1@217', 'hl7.fhir.us.davinci-crd_2.0.1@226'
50
52
 
51
53
  config options: { hook_name: ORDER_SIGN_TAG }
52
54
  run_as_group
@@ -13,6 +13,9 @@ module DaVinciCRDTestKit
13
13
  - External Reference Card
14
14
  - Instructions Card
15
15
  )
16
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@247', 'hl7.fhir.us.davinci-crd_2.0.1@248',
17
+ 'hl7.fhir.us.davinci-crd_2.0.1@249'
18
+
16
19
  id :crd_server_required_card_response_validation
17
20
  run_as_group
18
21
 
@@ -17,6 +17,9 @@ module DaVinciCRDTestKit
17
17
  ServiceRequest, or VisionPrescription
18
18
  - Then, among the target actions, checking if their resource has the [coverage-information extension](http://hl7.org/fhir/us/davinci-crd/StructureDefinition/ext-coverage-information).
19
19
  )
20
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@272', 'hl7.fhir.us.davinci-crd_2.0.1@273',
21
+ 'hl7.fhir.us.davinci-crd_2.0.1@274', 'hl7.fhir.us.davinci-crd_2.0.1@275',
22
+ 'hl7.fhir.us.davinci-crd_2.0.1@276', 'hl7.fhir.us.davinci-crd_2.0.1@277'
20
23
 
21
24
  input :valid_system_actions, :invoked_hook
22
25
  output :coverage_info
@@ -21,6 +21,7 @@ module DaVinciCRDTestKit
21
21
  (if present).
22
22
  )
23
23
  input :coverage_info
24
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@264', 'hl7.fhir.us.davinci-crd_2.0.1@265'
24
25
 
25
26
  def find_extension_value(extension, url, *properties)
26
27
  found_extension = extension.extension.find { |ext| ext.url == url }
@@ -28,6 +28,7 @@ module DaVinciCRDTestKit
28
28
 
29
29
  If no Create or Update Coverage Information cards or system actions are received, the test is skipped.
30
30
  )
31
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@306'
31
32
  optional
32
33
  input :valid_cards_with_suggestions, :valid_system_actions
33
34
 
@@ -17,6 +17,7 @@ module DaVinciCRDTestKit
17
17
  - The presence of a `links` array within each card.
18
18
  - That every link in the `links` array of a card is of type `absolute`.
19
19
  )
20
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@258', 'hl7.fhir.us.davinci-crd_2.0.1@259'
20
21
 
21
22
  input :valid_cards_with_links
22
23
  optional
@@ -30,6 +30,7 @@ module DaVinciCRDTestKit
30
30
 
31
31
  If no Request Form Completion cards or system actions are received, the test is skipped.
32
32
  )
33
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@299'
33
34
  optional
34
35
  input :valid_cards_with_suggestions, :valid_system_actions
35
36
 
@@ -19,6 +19,7 @@ module DaVinciCRDTestKit
19
19
 
20
20
  The test will be skipped if no Launch SMART Application cards are found within the returned valid cards.
21
21
  )
22
+ verifies_requirements 'hl7.fhir.us.davinci-crd_2.0.1@258'
22
23
 
23
24
  optional
24
25
  input :valid_cards_with_links
@@ -19,7 +19,7 @@ module DaVinciCRDTestKit
19
19
  input :invoked_hook
20
20
  output :valid_cards, :valid_system_actions
21
21
 
22
- SYSTEM_ACTIONS_HOOK_NAMES = ['appointment-book', 'order-sign'].freeze
22
+ SYSTEM_ACTIONS_HOOK_NAMES = ['appointment-book', 'order-sign', 'order-dispatch'].freeze
23
23
 
24
24
  def valid_cards
25
25
  @valid_cards ||= []
@@ -59,8 +59,8 @@ module DaVinciCRDTestKit
59
59
  skip_if successful_requests.empty?, 'All service requests were unsuccessful.'
60
60
 
61
61
  info do
62
- unsuncessful_count = (requests - successful_requests).length
63
- assert unsuncessful_count.zero?, "#{unsuncessful_count} out of #{requests.length} requests were unsuccessful"
62
+ unsuccessful_count = (requests - successful_requests).length
63
+ assert unsuccessful_count.zero?, "#{unsuccessful_count} out of #{requests.length} requests were unsuccessful"
64
64
  end
65
65
 
66
66
  successful_requests.each_with_index do |request, index|
@@ -99,7 +99,7 @@ module DaVinciCRDTestKit
99
99
  return if draft_orders_bundle_entry_refs(contexts).include?(ref)
100
100
 
101
101
  error_msg = "Resource being updated must be from the `draftOrders` entry. #{ref} is not in the " \
102
- "`context.drafOrders` of the submitted requests. In Action `#{action}`"
102
+ "`context.draftOrders` of the submitted requests. In Action `#{action}`"
103
103
  add_message('error', error_msg)
104
104
  end
105
105
 
@@ -1,4 +1,4 @@
1
1
  module DaVinciCRDTestKit
2
- VERSION = '0.12.0'.freeze
3
- LAST_UPDATED = '2025-03-21'.freeze # TODO: update next release
2
+ VERSION = '0.12.1'.freeze
3
+ LAST_UPDATED = '2025-05-20'.freeze # TODO: update next release
4
4
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'inferno_requirements_tools/ext/inferno_core/runnable'
1
2
  require_relative 'davinci_crd_test_kit/metadata'
2
3
  require_relative 'davinci_crd_test_kit/crd_server_suite'
3
4
  require_relative 'davinci_crd_test_kit/crd_client_suite'
@@ -0,0 +1,22 @@
1
+ module Inferno
2
+ module DSL
3
+ # This module contains the DSL for defining child entities in the test
4
+ # definition framework.
5
+ module Runnable
6
+ # Set/Get the IDs of requirements verifed by this runnable
7
+ # Set with [] to clear the list
8
+ #
9
+ # @param requirements [Array<String>]
10
+ # @return [Array<String>] the requirement IDs
11
+ def verifies_requirements(*requirement_ids)
12
+ if requirement_ids.empty?
13
+ @requirement_ids || []
14
+ elsif requirement_ids == [[]]
15
+ @requirement_ids = []
16
+ else
17
+ @requirement_ids = requirement_ids
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,284 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'yaml'
5
+ require_relative '../ext/inferno_core/runnable'
6
+
7
+ module InfernoRequirementsTools
8
+ module Tasks
9
+ # This class manages the mapping of test kit tests to requirements that they verify
10
+ # and creates a CSV file with the tests that cover each requirement.
11
+ # It expects a CSV file in the repo at `lib/[test kit id]/requirements/[test kit id]_requirements.csv`
12
+ # that serves as the source of the requirement set for the test kit. The requirements in
13
+ # this files are identified by a requirement set and an id and tests, groups, and suites
14
+ # within in the test kit can claim that they verify a requirement by including a reference
15
+ # to that requirementin the form <requirement set>@<id> in their `verifies_requirements` field.
16
+ # Requirements that are out of scope can be listed in a companion file
17
+ # `lib/[test kit id]/requirements/[test kit id]_out_of_scope_requirements.csv`.
18
+ #
19
+ # The `run` method generates a CSV file at
20
+ # `lib/[test kit id]/requirements/generated/[test kit id]_requirements_coverage.csv``.
21
+ # This file will be identical to the input spreadsheet, plus an additional column which holds a comma separated
22
+ # list of inferno test IDs that test each requirement. These test IDs are Inferno short form IDs that represent the
23
+ # position of the test within its group and suite. For example, the fifth test in the second group will have an ID
24
+ # of 2.05. This ID is also shown in the Inferno web UI.
25
+ #
26
+ # The `run_check` method will check whether the previously generated file is up-to-date.
27
+ class RequirementsCoverage
28
+ VERSION = '0.2.0' # update when making meaningful changes to this method for tracking used versions
29
+ CONFIG = YAML.load_file(File.join('lib', 'requirements_config.yaml'))
30
+
31
+ TEST_KIT_ID = CONFIG['test_kit_id']
32
+ TEST_SUITES = CONFIG['suites'].map do |test_suite|
33
+ Object.const_get(test_suite['class_name'])
34
+ end
35
+
36
+ SUITE_ID_TO_ACTOR_MAP = CONFIG['suites'].each_with_object({}) do |test_suite, hash|
37
+ hash[test_suite['id']] = test_suite['suite_actor']
38
+ end
39
+
40
+ # Derivative constants
41
+ TEST_KIT_CODE_FOLDER = TEST_KIT_ID
42
+ DASHERIZED_TEST_KIT_ID = TEST_KIT_ID.gsub('_', '-')
43
+ INPUT_HEADERS = [
44
+ 'Req Set',
45
+ 'ID',
46
+ 'URL',
47
+ 'Requirement',
48
+ 'Conformance',
49
+ 'Actor',
50
+ 'Sub-Requirement(s)',
51
+ 'Conditionality'
52
+ ].freeze
53
+ SHORT_ID_HEADER = 'Short ID(s)'
54
+ FULL_ID_HEADER = 'Full ID(s)'
55
+ INPUT_FILE_NAME = "#{DASHERIZED_TEST_KIT_ID}_requirements.csv".freeze
56
+ INPUT_FILE = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', INPUT_FILE_NAME).freeze
57
+ NOT_TESTED_FILE_NAME = "#{DASHERIZED_TEST_KIT_ID}_out_of_scope_requirements.csv".freeze
58
+ NOT_TESTED_FILE = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', NOT_TESTED_FILE_NAME).freeze
59
+ OUTPUT_HEADERS = INPUT_HEADERS + TEST_SUITES.flat_map do |suite|
60
+ ["#{suite.title} #{SHORT_ID_HEADER}", "#{suite.title} #{FULL_ID_HEADER}"]
61
+ end
62
+ OUTPUT_FILE_NAME = "#{DASHERIZED_TEST_KIT_ID}_requirements_coverage.csv".freeze
63
+ OUTPUT_FILE_DIRECTORY = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', 'generated')
64
+ OUTPUT_FILE = File.join(OUTPUT_FILE_DIRECTORY, OUTPUT_FILE_NAME).freeze
65
+
66
+ def input_rows
67
+ @input_rows ||=
68
+ CSV.parse(File.open(INPUT_FILE, 'r:bom|utf-8'), headers: true).map do |row|
69
+ row.to_h.slice(*INPUT_HEADERS)
70
+ end
71
+ end
72
+
73
+ def not_tested_requirements_map
74
+ @not_tested_requirements_map ||= load_not_tested_requirements
75
+ end
76
+
77
+ def load_not_tested_requirements
78
+ return {} unless File.exist?(NOT_TESTED_FILE)
79
+
80
+ not_tested_requirements = {}
81
+ CSV.parse(File.open(NOT_TESTED_FILE, 'r:bom|utf-8'), headers: true).each do |row|
82
+ row_hash = row.to_h
83
+ not_tested_requirements["#{row_hash['Req Set']}@#{row_hash['ID']}"] = row_hash
84
+ end
85
+
86
+ not_tested_requirements
87
+ end
88
+
89
+ # Of the form:
90
+ # {
91
+ # 'req-id-1': [
92
+ # { short_id: 'short-id-1', full_id: 'long-id-1', suite_id: 'suite-id-1' },
93
+ # { short_id: 'short-id-2', full_id: 'long-id-2', suite_id: 'suite-id-2' }
94
+ # ],
95
+ # 'req-id-2': [{ short_id: 'short-id-3', full_id: 'long-id-3', suite_id: 'suite-id-3' }],
96
+ # ...
97
+ # }
98
+ def inferno_requirements_map
99
+ @inferno_requirements_map ||= TEST_SUITES.each_with_object({}) do |suite, requirements_map|
100
+ serialize_requirements(suite, 'suite', suite.id, requirements_map)
101
+ suite.groups.each do |group|
102
+ map_group_requirements(group, suite.id, requirements_map)
103
+ end
104
+ end
105
+ end
106
+
107
+ def new_csv
108
+ @new_csv ||=
109
+ CSV.generate(+"\xEF\xBB\xBF") do |csv|
110
+ csv << OUTPUT_HEADERS
111
+ input_rows.each do |row| # NOTE: use row order from source file
112
+ next if row['Conformance'] == 'DEPRECATED' # filter out deprecated rows
113
+
114
+ TEST_SUITES.each do |suite|
115
+ suite_actor = SUITE_ID_TO_ACTOR_MAP[suite.id]
116
+ if row['Actor']&.include?(suite_actor)
117
+ add_suite_tests_for_row(row, suite)
118
+ else
119
+ row["#{suite.title} #{SHORT_ID_HEADER}"] = 'NA'
120
+ row["#{suite.title} #{FULL_ID_HEADER}"] = 'NA'
121
+ end
122
+ end
123
+ csv << row.values
124
+ end
125
+ end
126
+ end
127
+
128
+ def add_suite_tests_for_row(row, suite)
129
+ set_and_req_id = "#{row['Req Set']}@#{row['ID']}"
130
+ items = get_items_for_requirement(set_and_req_id, suite)
131
+ short_ids = items[0]
132
+ full_ids = items[1]
133
+ if short_ids.blank? && not_tested_requirements_map.key?(set_and_req_id)
134
+ row["#{suite.title} #{SHORT_ID_HEADER}"] = 'Not Tested'
135
+ row["#{suite.title} #{FULL_ID_HEADER}"] = 'Not Tested'
136
+ else
137
+ row["#{suite.title} #{SHORT_ID_HEADER}"] = short_ids&.join(', ')
138
+ row["#{suite.title} #{FULL_ID_HEADER}"] = full_ids&.join(', ')
139
+ end
140
+ end
141
+
142
+ def get_items_for_requirement(set_and_req_id, suite)
143
+ suite_requirement_items = inferno_requirements_map[set_and_req_id]&.filter do |item|
144
+ item[:suite_id] == suite.id
145
+ end
146
+ [
147
+ suite_requirement_items&.map { |item| item[:short_id] },
148
+ suite_requirement_items&.map { |item| item[:full_id] }
149
+ ]
150
+ end
151
+
152
+ def input_requirement_ids
153
+ @input_requirement_ids ||= input_rows.map { |row| "#{row['Req Set']}@#{row['ID']}" }
154
+ end
155
+
156
+ # The requirements present in Inferno that aren't in the input spreadsheet
157
+ def unmatched_requirements_map
158
+ @unmatched_requirements_map ||= inferno_requirements_map.except(*input_requirement_ids)
159
+ end
160
+
161
+ def old_csv
162
+ @old_csv ||= File.read(OUTPUT_FILE)
163
+ end
164
+
165
+ def run
166
+ unless File.exist?(INPUT_FILE)
167
+ puts "Could not find input file: #{INPUT_FILE}. Aborting requirements coverage generation..."
168
+ exit(1)
169
+ end
170
+
171
+ if unmatched_requirements_map.any?
172
+ puts "WARNING: The following requirements indicated in the test kit are not present in #{INPUT_FILE_NAME}"
173
+ output_requirements_map_table(unmatched_requirements_map)
174
+ end
175
+
176
+ if File.exist?(OUTPUT_FILE)
177
+ if old_csv == new_csv
178
+ puts "'#{OUTPUT_FILE_NAME}' file is up to date."
179
+ return
180
+ else
181
+ puts 'Requirements coverage has changed.'
182
+ end
183
+ else
184
+ puts "No existing #{OUTPUT_FILE_NAME}."
185
+ end
186
+
187
+ puts "Writing to file #{OUTPUT_FILE}..."
188
+ FileUtils.mkdir_p(OUTPUT_FILE_DIRECTORY)
189
+ File.write(OUTPUT_FILE, new_csv)
190
+ puts 'Done.'
191
+ end
192
+
193
+ def run_check
194
+ unless File.exist?(INPUT_FILE)
195
+ puts "Could not find input file: #{INPUT_FILE}. Aborting requirements coverage check..."
196
+ exit(1)
197
+ end
198
+
199
+ if unmatched_requirements_map.any?
200
+ puts "The following requirements indicated in the test kit are not present in #{INPUT_FILE_NAME}"
201
+ output_requirements_map_table(unmatched_requirements_map)
202
+ end
203
+
204
+ if File.exist?(OUTPUT_FILE)
205
+ if old_csv == new_csv
206
+ puts "'#{OUTPUT_FILE_NAME}' file is up to date."
207
+ return unless unmatched_requirements_map.any?
208
+ else
209
+ puts <<~MESSAGE
210
+ #{OUTPUT_FILE_NAME} file is out of date.
211
+ To regenerate the file, run:
212
+
213
+ bundle exec rake requirements:generate_coverage
214
+
215
+ MESSAGE
216
+ end
217
+ else
218
+ puts <<~MESSAGE
219
+ No existing #{OUTPUT_FILE_NAME} file.
220
+ To generate the file, run:
221
+
222
+ bundle exec rake requirements:generate_coverage
223
+
224
+ MESSAGE
225
+ end
226
+
227
+ puts 'Check failed.'
228
+ exit(1)
229
+ end
230
+
231
+ def map_group_requirements(group, suite_id, requirements_map)
232
+ serialize_requirements(group, group.short_id, suite_id, requirements_map)
233
+ group.tests&.each { |test| serialize_requirements(test, test.short_id, suite_id, requirements_map) }
234
+ group.groups&.each { |subgroup| map_group_requirements(subgroup, suite_id, requirements_map) }
235
+ end
236
+
237
+ def serialize_requirements(runnable, short_id, suite_id, requirements_map)
238
+ runnable.verifies_requirements&.each do |requirement_id|
239
+ requirement_id_string = requirement_id.to_s
240
+
241
+ requirements_map[requirement_id_string] ||= []
242
+ requirements_map[requirement_id_string] << { short_id:, full_id: runnable.id, suite_id: }
243
+ end
244
+ end
245
+
246
+ # Output the requirements in the map like so:
247
+ #
248
+ # requirement_id | short_id | full_id
249
+ # ---------------+------------+----------
250
+ # req-id-1 | short-id-1 | full-id-1
251
+ # req-id-2 | short-id-2 | full-id-2
252
+ #
253
+ def output_requirements_map_table(requirements_map)
254
+ headers = %w[requirement_id short_id full_id]
255
+ col_widths = headers.map(&:length)
256
+ col_widths[0] = [col_widths[0], requirements_map.keys.map(&:length).max].max
257
+ col_widths[1] = ([col_widths[1]] + requirements_map.values.flatten.map { |item| item[:short_id].length }).max
258
+ col_widths[2] = ([col_widths[2]] + requirements_map.values.flatten.map { |item| item[:full_id].length }).max
259
+ col_widths.map { |width| width + 3 }
260
+
261
+ puts [
262
+ headers[0].ljust(col_widths[0]),
263
+ headers[1].ljust(col_widths[1]),
264
+ headers[2].ljust(col_widths[2])
265
+ ].join(' | ')
266
+ puts col_widths.map { |width| '-' * width }.join('-+-')
267
+ output_requirements_map_table_contents(requirements_map, col_widths)
268
+ puts
269
+ end
270
+
271
+ def output_requirements_map_table_contents(requirements_map, col_widths)
272
+ requirements_map.each do |requirement_id, runnables|
273
+ runnables.each do |runnable|
274
+ puts [
275
+ requirement_id.ljust(col_widths[0]),
276
+ runnable[:short_id].ljust(col_widths[1]),
277
+ runnable[:full_id].ljust(col_widths[2])
278
+ ].join(' | ')
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,18 @@
1
+ test_kit_id: davinci_crd_test_kit
2
+
3
+ suites:
4
+ - id: crd_server
5
+ class_name: DaVinciCRDTestKit::CRDServerSuite
6
+ suite_actor: Server
7
+ - id: crd_client
8
+ class_name: DaVinciCRDTestKit::CRDClientSuite
9
+ suite_actor: Client
10
+
11
+ requirement_sets:
12
+ - id: hl7.fhir.us.davinci-crd_2.0.1
13
+ folder: Coverage Requirements Discovery (CRD)
14
+ requirements: All
15
+ actor_map:
16
+ - {spec: Client, test_kit: Client}
17
+ - {spec: Server, test_kit: Server}
18
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: davinci_crd_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2025-03-21 00:00:00.000000000 Z
13
+ date: 2025-05-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: inferno_core
@@ -78,6 +78,7 @@ files:
78
78
  - lib/davinci_crd_test_kit/client_fhir_api_group.rb
79
79
  - lib/davinci_crd_test_kit/client_hook_request_validation.rb
80
80
  - lib/davinci_crd_test_kit/client_hooks_group.rb
81
+ - lib/davinci_crd_test_kit/client_registration_group.rb
81
82
  - lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb
82
83
  - lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb
83
84
  - lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb
@@ -91,6 +92,7 @@ files:
91
92
  - lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb
92
93
  - lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb
93
94
  - lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb
95
+ - lib/davinci_crd_test_kit/client_tests/client_registration_verification_test.rb
94
96
  - lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb
95
97
  - lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb
96
98
  - lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb
@@ -117,6 +119,9 @@ files:
117
119
  - lib/davinci_crd_test_kit/jwt_helper.rb
118
120
  - lib/davinci_crd_test_kit/metadata.rb
119
121
  - lib/davinci_crd_test_kit/mock_service_response.rb
122
+ - lib/davinci_crd_test_kit/requirements/davinci-crd-test-kit_out_of_scope_requirements.csv
123
+ - lib/davinci_crd_test_kit/requirements/davinci-crd-test-kit_requirements.csv
124
+ - lib/davinci_crd_test_kit/requirements/generated/davinci-crd-test-kit_requirements_coverage.csv
120
125
  - lib/davinci_crd_test_kit/routes/cds-services.json
121
126
  - lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb
122
127
  - lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb
@@ -158,6 +163,9 @@ files:
158
163
  - lib/davinci_crd_test_kit/test_helper.rb
159
164
  - lib/davinci_crd_test_kit/urls.rb
160
165
  - lib/davinci_crd_test_kit/version.rb
166
+ - lib/inferno_requirements_tools/ext/inferno_core/runnable.rb
167
+ - lib/inferno_requirements_tools/tasks/requirements_coverage.rb
168
+ - lib/requirements_config.yaml
161
169
  homepage: https://github.com/inferno-framework/davinci-crd-test-kit
162
170
  licenses:
163
171
  - Apache-2.0