subscriptions_test_kit 0.9.2 → 0.9.3

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/inferno_requirements_tools/tasks/collect_requirements.rb +51 -50
  3. data/lib/inferno_requirements_tools/tasks/requirements_coverage.rb +22 -19
  4. data/lib/subscriptions_test_kit/common/notification_conformance_verification.rb +4 -2
  5. data/lib/subscriptions_test_kit/endpoints/subscription_create_endpoint.rb +7 -3
  6. data/lib/subscriptions_test_kit/endpoints/subscription_status_endpoint.rb +16 -11
  7. data/lib/subscriptions_test_kit/requirements/generated/subscriptions-test-kit_requirements_coverage.csv +50 -50
  8. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/common/subscription_simulation_utils.rb +9 -6
  9. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_payload_verification_test.rb +2 -2
  10. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/notification_input_verification_test.rb +1 -1
  11. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/processing_attestation_test.rb +1 -1
  12. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification/subscription_verification_test.rb +2 -2
  13. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/conformance_verification_group.rb +1 -1
  14. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_test.rb +14 -11
  15. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/event_notification_verification_test.rb +6 -5
  16. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification/handshake_notification_verification_test.rb +5 -5
  17. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client/workflow/interaction_verification_group.rb +1 -1
  18. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_client_suite.rb +5 -3
  19. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/capability_statement/cs_conformance_test.rb +1 -1
  20. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/capability_statement/topic_discovery_test.rb +3 -3
  21. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/capability_statement_group.rb +3 -3
  22. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/creation_response_conformance_test.rb +1 -1
  23. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction/subscription_conformance_test.rb +10 -9
  24. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/interaction_group.rb +3 -3
  25. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/common/subscription_status_operation.rb +3 -2
  26. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification/empty_content_group.rb +3 -2
  27. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification/full_resource_content/full_resource_conformance_test.rb +2 -2
  28. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification/full_resource_content_group.rb +3 -2
  29. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification/id_only_content/id_only_conformance_test.rb +2 -2
  30. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification/id_only_content_group.rb +3 -2
  31. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/event_notification_group.rb +2 -2
  32. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/handshake_heartbeat/handshake_conformance_test.rb +7 -6
  33. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/handshake_heartbeat/heartbeat_conformance_test.rb +4 -2
  34. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/status_operation/status_invocation_test.rb +2 -2
  35. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/status_operation_group.rb +1 -1
  36. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server/subscription_rejection/reject_subscriptions_test.rb +29 -25
  37. data/lib/subscriptions_test_kit/suites/subscriptions_r5_backport_r4_server_suite.rb +3 -2
  38. data/lib/subscriptions_test_kit/version.rb +1 -3
  39. metadata +4 -74
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4aa07a6d63da9348618f7c9d28443c743ff8669b25e5304c2d572370d7eb457c
4
- data.tar.gz: 28ca4154cb6b064fd6c7d5cb7dd47f9b6c68a5eef051214e091152517971979e
3
+ metadata.gz: ad5fae1148d468463f60e61a0e7c273b90eac5294b1c8d19b588288d3d53ca77
4
+ data.tar.gz: 62507f2c4a5cc9e39c1906bbb10032d1baa00b8ec33055762016c5ce2d22722c
5
5
  SHA512:
6
- metadata.gz: 756cebdf92654e1115f752c127e68c41052259087c781a665f9f286f4eaecb48bd10996cba20d6e0cfce1c287e3779be0a401e6a8f297b94bc40952e9686c013
7
- data.tar.gz: d8146e70aae224a33f534f05b54282d96bdc62742d5710d3f6e30260d480ec52efd9396a98090800756611640cf66bbe695b7322c1f650eb47be3fb5e729afb3
6
+ metadata.gz: bad58bc3cdcb1c451bcb28f5dfbe8a5492e5cc8fff49b00f99d35d93588cec7936feeedee3c24984d6c1090ade8f92b35da4c89d83fd8036586f539cd0296ad7
7
+ data.tar.gz: f26d5875e051b95b24dabc7f45cdba4b9bda8631f76f022aa1e753569e89be8fd264b052ac9662855023c2f685ad9b4ac5473732b8d7538b32d7a2db819e2e6b
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'CSV'
3
+ require 'csv'
4
4
  require 'roo'
5
5
 
6
6
  module InfernoRequirementsTools
@@ -52,22 +52,26 @@ module InfernoRequirementsTools
52
52
  PLANNED_NOT_TESTED_OUTPUT_FILE =
53
53
  File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', PLANNED_NOT_TESTED_OUTPUT_FILE_NAME).freeze
54
54
 
55
- def input_file_map
56
- @input_file_map ||= {}
55
+ def available_input_worksheets
56
+ @available_input_worksheets ||= Dir.glob(File.join(@input_directory, '*.xlsx')).reject { |f| f.include?('~$') }
57
57
  end
58
58
 
59
- def input_rows
60
- @input_rows ||= {}
61
- end
62
-
63
- def input_rows_for_set(req_set_id)
64
- input_rows[req_set_id] = extract_input_rows_for_set(req_set_id) unless input_rows.has_key?(req_set_id)
65
- input_rows[req_set_id]
66
- end
67
-
68
- def extract_input_rows_for_set(req_set_id)
69
- CSV.parse(Roo::Spreadsheet.open(input_file_map[req_set_id]).sheet('Requirements').to_csv, headers: true).map do |row|
70
- row.to_h.slice(*INPUT_HEADERS)
59
+ # Of the form:
60
+ # {
61
+ # req_set_id_1: [row1, row2, row 3, ...],
62
+ # req_set_id_2: [row1, row2, row 3, ...]
63
+ # }
64
+ def input_requirement_sets
65
+ @input_requirement_sets ||= INPUT_SETS.each_with_object({}) do |req_set_id, hash|
66
+ req_set_file = available_input_worksheets.find { |worksheet_file| worksheet_file.include?(req_set_id) }
67
+
68
+ hash[req_set_id] =
69
+ unless req_set_file.nil?
70
+ CSV.parse(Roo::Spreadsheet.open(req_set_file).sheet('Requirements').to_csv,
71
+ headers: true).map do |row|
72
+ row.to_h.slice(*INPUT_HEADERS)
73
+ end
74
+ end
71
75
  end
72
76
  end
73
77
 
@@ -76,12 +80,11 @@ module InfernoRequirementsTools
76
80
  CSV.generate(+"\xEF\xBB\xBF") do |csv| # start with an unnecessary BOM to make viewing in excel easier
77
81
  csv << REQUIREMENTS_OUTPUT_HEADERS
78
82
 
79
- INPUT_SETS.each do |req_set_id|
80
- input_rows = input_rows_for_set(req_set_id)
81
- input_rows.each do |row| # NOTE: use row order from source file
82
- row['Req Set'] = req_set_id
83
-
84
- csv << REQUIREMENTS_OUTPUT_HEADERS.map { |header| row.key?(header) ? row[header] : row["#{header}*"]}
83
+ input_requirement_sets.each do |req_set_id, input_rows|
84
+ input_rows.each do |input_row| # NOTE: use row order from source file
85
+ csv << REQUIREMENTS_OUTPUT_HEADERS.map do |header|
86
+ header == 'Req Set' ? req_set_id : input_row[header] || input_row["#{header}*"]
87
+ end
85
88
  end
86
89
  end
87
90
  end
@@ -96,19 +99,13 @@ module InfernoRequirementsTools
96
99
  CSV.generate(+"\xEF\xBB\xBF") do |csv| # start with an unnecessary BOM to make viewing in excel easier
97
100
  csv << PLANNED_NOT_TESTED_OUTPUT_HEADERS
98
101
 
99
- INPUT_SETS.each do |req_set_id|
100
- input_rows = input_rows_for_set(req_set_id)
101
- input_rows.each do |row| # note: use row order from source file
102
- not_verifiable = row['Verifiable?']&.downcase == 'no' || row['Verifiable?']&.downcase == 'false'
103
- not_tested = row['Planning To Test?']&.downcase == 'no' || row['Planning To Test?']&.downcase == 'false'
104
- next unless not_verifiable || not_tested
105
-
106
- csv << [
107
- req_set_id,
108
- row['ID*'],
109
- not_verifiable ? 'Not Verifiable' : 'Not Tested',
110
- not_verifiable ? row['Verifiability Details'] : row['Planning To Test Details']
111
- ]
102
+ input_requirement_sets.each do |req_set_id, input_rows|
103
+ input_rows.each do |row|
104
+ if spreadsheet_value_falsy?(row['Verifiable?'])
105
+ csv << [req_set_id, row['ID*'], 'Not Verifiable', row['Verifiability Details']]
106
+ elsif spreadsheet_value_falsy?(row['Planning To Test?'])
107
+ csv << [req_set_id, row['ID*'], 'Not Tested', row['Planning To Test Details']]
108
+ end
112
109
  end
113
110
  end
114
111
  end
@@ -118,22 +115,9 @@ module InfernoRequirementsTools
118
115
  @old_planned_not_tested_csv ||= File.read(PLANNED_NOT_TESTED_OUTPUT_FILE)
119
116
  end
120
117
 
121
- def check_for_req_set_files(input_directory)
122
- available_worksheets = Dir.glob(File.join(input_directory, '*.xlsx')).reject { |f| f.include?('~$') }
123
-
124
- INPUT_SETS.each do |req_set_id|
125
- req_set_file = available_worksheets&.find { |worksheet_file| worksheet_file.include?(req_set_id) }
126
-
127
- if req_set_file&.empty?
128
- puts "Could not find input file for set #{req_set_id} in directory #{input_directory}. Aborting requirements collection..."
129
- exit(1)
130
- end
131
- input_file_map[req_set_id] = req_set_file
132
- end
133
- end
134
-
135
118
  def run(input_directory)
136
- check_for_req_set_files(input_directory)
119
+ @input_directory = input_directory
120
+ check_presence_of_input_files
137
121
 
138
122
  update_requirements =
139
123
  if File.exist?(REQUIREMENTS_OUTPUT_FILE)
@@ -177,7 +161,8 @@ module InfernoRequirementsTools
177
161
  end
178
162
 
179
163
  def run_check(input_directory)
180
- check_for_req_set_files(input_directory)
164
+ @input_directory = input_directory
165
+ check_presence_of_input_files
181
166
 
182
167
  requirements_ok =
183
168
  if File.exist?(REQUIREMENTS_OUTPUT_FILE)
@@ -217,6 +202,22 @@ module InfernoRequirementsTools
217
202
  MESSAGE
218
203
  exit(1)
219
204
  end
205
+
206
+ def check_presence_of_input_files
207
+ input_requirement_sets.each do |req_set_id, rows|
208
+ next unless rows.nil?
209
+
210
+ puts %(
211
+ Could not find input file for set #{req_set_id} in directory #{input_directory}. Aborting requirements
212
+ collection..."
213
+ )
214
+ exit(1)
215
+ end
216
+ end
217
+
218
+ def spreadsheet_value_falsy?(str)
219
+ str&.downcase == 'no' || str&.downcase == 'false'
220
+ end
220
221
  end
221
222
  end
222
223
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'CSV'
3
+ require 'csv'
4
4
  require_relative '../ext/inferno_core/runnable'
5
5
 
6
6
  module InfernoRequirementsTools
@@ -51,12 +51,15 @@ module InfernoRequirementsTools
51
51
  INPUT_FILE = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', INPUT_FILE_NAME).freeze
52
52
  NOT_TESTED_FILE_NAME = "#{TEST_KIT_ID}_out_of_scope_requirements.csv".freeze
53
53
  NOT_TESTED_FILE = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', NOT_TESTED_FILE_NAME).freeze
54
+ OUTPUT_HEADERS = INPUT_HEADERS + TEST_SUITES.flat_map do |suite|
55
+ ["#{suite.title} #{SHORT_ID_HEADER}", "#{suite.title} #{FULL_ID_HEADER}"]
56
+ end
54
57
  OUTPUT_FILE_NAME = "#{TEST_KIT_ID}_requirements_coverage.csv".freeze
55
58
  OUTPUT_FILE = File.join('lib', TEST_KIT_CODE_FOLDER, 'requirements', 'generated', OUTPUT_FILE_NAME).freeze
56
59
 
57
60
  def input_rows
58
61
  @input_rows ||=
59
- CSV.parse(File.open(INPUT_FILE, "r:bom|utf-8"), headers: true).map do |row|
62
+ CSV.parse(File.open(INPUT_FILE, 'r:bom|utf-8'), headers: true).map do |row|
60
63
  row.to_h.slice(*INPUT_HEADERS)
61
64
  end
62
65
  end
@@ -66,12 +69,12 @@ module InfernoRequirementsTools
66
69
  end
67
70
 
68
71
  def load_not_tested_requirements
69
- return {} unless File.exists?(NOT_TESTED_FILE)
72
+ return {} unless File.exist?(NOT_TESTED_FILE)
70
73
 
71
74
  not_tested_requirements = {}
72
- CSV.parse(File.open(NOT_TESTED_FILE, "r:bom|utf-8"), headers: true).each do |row|
75
+ CSV.parse(File.open(NOT_TESTED_FILE, 'r:bom|utf-8'), headers: true).each do |row|
73
76
  row_hash = row.to_h
74
- not_tested_requirements[ "#{row_hash['Req Set']}@#{row_hash['ID']}"] = row_hash
77
+ not_tested_requirements["#{row_hash['Req Set']}@#{row_hash['ID']}"] = row_hash
75
78
  end
76
79
 
77
80
  not_tested_requirements
@@ -95,26 +98,24 @@ module InfernoRequirementsTools
95
98
  end
96
99
  end
97
100
 
101
+ # rubocop:disable Metrics/CyclomaticComplexity
98
102
  def new_csv
99
103
  @new_csv ||=
100
104
  CSV.generate(+"\xEF\xBB\xBF") do |csv|
101
- output_headers = TEST_SUITES.each_with_object(INPUT_HEADERS.dup) do |suite, headers|
102
- headers << "#{suite.title} #{SHORT_ID_HEADER}"
103
- headers << "#{suite.title} #{FULL_ID_HEADER}"
104
- end
105
-
106
- csv << output_headers
107
- input_rows.each do |row| # note: use row order from source file
105
+ csv << OUTPUT_HEADERS
106
+ input_rows.each do |row| # NOTE: use row order from source file
108
107
  next if row['Conformance'] == 'DEPRECATED' # filter out deprecated rows
109
- row_actor = row['Actor']
108
+
110
109
  TEST_SUITES.each do |suite|
111
110
  suite_actor = SUITE_ID_TO_ACTOR_MAP[suite.id]
112
- if row_actor&.include?(suite_actor)
111
+ if row['Actor']&.include?(suite_actor)
113
112
  set_and_req_id = "#{row['Req Set']}@#{row['ID']}"
114
- suite_requirement_items = inferno_requirements_map[set_and_req_id]&.filter { |item| item[:suite_id] == suite.id}
113
+ suite_requirement_items = inferno_requirements_map[set_and_req_id]&.filter do |item|
114
+ item[:suite_id] == suite.id
115
+ end
115
116
  short_ids = suite_requirement_items&.map { |item| item[:short_id] }
116
117
  full_ids = suite_requirement_items&.map { |item| item[:full_id] }
117
- if short_ids.blank? && not_tested_requirements_map.has_key?(set_and_req_id)
118
+ if short_ids.blank? && not_tested_requirements_map.key?(set_and_req_id)
118
119
  row["#{suite.title} #{SHORT_ID_HEADER}"] = 'Not Tested'
119
120
  row["#{suite.title} #{FULL_ID_HEADER}"] = 'Not Tested'
120
121
  else
@@ -131,6 +132,7 @@ module InfernoRequirementsTools
131
132
  end
132
133
  end
133
134
  end
135
+ # rubocop:enable Metrics/CyclomaticComplexity
134
136
 
135
137
  def input_requirement_ids
136
138
  @input_requirement_ids ||= input_rows.map { |row| "#{row['Req Set']}@#{row['ID']}" }
@@ -138,9 +140,7 @@ module InfernoRequirementsTools
138
140
 
139
141
  # The requirements present in Inferno that aren't in the input spreadsheet
140
142
  def unmatched_requirements_map
141
- @unmatched_requirements_map ||= inferno_requirements_map.filter do |requirement_id, _|
142
- !input_requirement_ids.include?(requirement_id)
143
- end
143
+ @unmatched_requirements_map ||= inferno_requirements_map.except(*input_requirement_ids)
144
144
  end
145
145
 
146
146
  def old_csv
@@ -233,6 +233,8 @@ module InfernoRequirementsTools
233
233
  # ---------------+------------+----------
234
234
  # req-id-1 | short-id-1 | full-id-1
235
235
  # req-id-2 | short-id-2 | full-id-2
236
+ #
237
+ # rubocop:disable Metrics/CyclomaticComplexity
236
238
  def output_requirements_map_table(requirements_map)
237
239
  headers = %w[requirement_id short_id full_id]
238
240
  col_widths = headers.map(&:length)
@@ -258,6 +260,7 @@ module InfernoRequirementsTools
258
260
  end
259
261
  puts
260
262
  end
263
+ # rubocop:enable Metrics/CyclomaticComplexity
261
264
  end
262
265
  end
263
266
  end
@@ -32,9 +32,11 @@ module SubscriptionsTestKit
32
32
 
33
33
  unless subscription_status_entry.request.present? &&
34
34
  (
35
- subscription_id ?
36
- subscription_status_entry.request.url.end_with?("Subscription/#{subscription_id}/$status") :
35
+ if subscription_id
36
+ subscription_status_entry.request.url.end_with?("Subscription/#{subscription_id}/$status")
37
+ else
37
38
  subscription_status_entry.request.url.match?(%r{Subscription/[^/]+/\$status\z})
39
+ end
38
40
  )
39
41
 
40
42
  add_message('error',
@@ -32,7 +32,7 @@ module SubscriptionsTestKit
32
32
  existing_subscription_request = requests.find { |r| r.status == 201 }
33
33
  if existing_subscription_request.present?
34
34
  subscription_hash = JSON.parse(existing_subscription_request.response_body)
35
- error_text = 'Inferno only supports one subscription per test run. Subscription already created with '\
35
+ error_text = 'Inferno only supports one subscription per test run. Subscription already created with ' \
36
36
  "ID #{subscription_hash['id']}"
37
37
  response.body = operation_outcome('error', 'business-rule', error_text).to_json
38
38
  return
@@ -74,10 +74,14 @@ module SubscriptionsTestKit
74
74
  return operation_outcome('error', 'value', 'channel.endpoint is not recognized as a conformant URL')
75
75
  end
76
76
 
77
- heartbeat_period = subscription.channel&.extension&.find do |e|
77
+ heartbeat_period = find_heartbeat_period(subscription)
78
+ operation_outcome('error', 'not-supported', 'heartbeatPeriod is not supported') unless heartbeat_period.nil?
79
+ end
80
+
81
+ def find_heartbeat_period(subscription)
82
+ subscription&.channel&.extension&.find do |e|
78
83
  e.url == 'http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-heartbeat-period'
79
84
  end
80
- operation_outcome('error', 'not-supported', 'heartbeatPeriod is not supported') unless heartbeat_period.nil?
81
85
  end
82
86
 
83
87
  def valid_url?(url)
@@ -30,18 +30,9 @@ module SubscriptionsTestKit
30
30
  return
31
31
  end
32
32
 
33
- id_params = params&.parameter&.filter { |p| p.name == 'id' }
34
-
35
- if id_params&.any? && id_params&.none? { |p| p.valueString == subscription.id }
33
+ unless subscription_params_match?(params)
36
34
  not_found
37
35
  return
38
- else
39
- status_params = params&.parameter&.filter { |p| p.name == 'status' }
40
- subscription_status = determine_subscription_status_code(subscription.id)
41
- if status_params&.any? && status_params.none? { |p| p.valueString == subscription_status }
42
- not_found
43
- return
44
- end
45
36
  end
46
37
  end
47
38
 
@@ -54,6 +45,16 @@ module SubscriptionsTestKit
54
45
  request.url).to_json
55
46
  end
56
47
 
48
+ def subscription_params_match?(params)
49
+ id_params = find_params(params, 'id')
50
+
51
+ return false if id_params&.any? && id_params&.none? { |p| p.valueString == subscription.id }
52
+
53
+ status_params = find_params(params, 'status')
54
+ subscription_status = determine_subscription_status_code(subscription.id)
55
+ status_params.nil? || status_params.none? || status_params.any { p.valueString == subscription_status }
56
+ end
57
+
57
58
  def tags
58
59
  [SUBSCRIPTION_STATUS_TAG]
59
60
  end
@@ -63,8 +64,12 @@ module SubscriptionsTestKit
63
64
  response.body = operation_outcome('error', 'not-found').to_json
64
65
  end
65
66
 
67
+ def find_params(params, name)
68
+ params&.parameter&.filter { |p| p.name == name }
69
+ end
70
+
66
71
  def base_subscription_url
67
- request.url.sub(%r{(#{Regexp.escape(FHIR_SUBSCRIPTION_PATH)}).*}, '\1')
72
+ request.url.sub(/(#{Regexp.escape(FHIR_SUBSCRIPTION_PATH)}).*/, '\1')
68
73
  end
69
74
  end
70
75
  end