subscriptions_test_kit 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
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