inferno_core 0.6.11 → 0.6.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b899985c0f229c1f91aa9218d09f9f502ed315c537398edc911e989cbe61c6a8
4
- data.tar.gz: '08cca457d4bb79bdb70487a14fa07f8274dac19faae5623bedef8797541e993b'
3
+ metadata.gz: a7e5f22313a778dd7b70ba70eae9513bda158a7c0cd6beba9a2d195b3a37f077
4
+ data.tar.gz: 8e7d6155fad17e00ccc48257853f4df2cc816d0965cc0b18a50515bbd26d343f
5
5
  SHA512:
6
- metadata.gz: 36b765180f9d7168c91238e1c7a63e8cc281f3a795f068e3d6ddb317d419d90dc2401585b8fde37e3d27c0c82fda9ef66dba3c6bd96201f1dafa9d3be301b53f
7
- data.tar.gz: d83ffd4c0fec0cf1d3621a7f563c9d369d671bfce5f11c9bdcac9f287c5918a3c37f3ff340cd3429b4dbb1e96e9daa936ce50fe4bd889420cb66c0a41020a777
6
+ metadata.gz: 56b26537ac370a6b475ab33895ad91e3d5cf5ff588cd3fa5be979c6e250e5aa703a76c093ea46c787ecd2c4f061f1f55e39c43ba245219618c18b9a44e522f3e
7
+ data.tar.gz: a1549f23c6e8c501286bc490c6821be19da1a730b950a497bdd7a2c7d61c392090e0ac1603f251e4d033abedbc4457e1dfcbf7ecac5bcfcd875392e7125ca4ef
@@ -27,10 +27,11 @@ module Inferno
27
27
 
28
28
  ig_path = absolute_path_with_home_expansion(ig_path)
29
29
  data_path = absolute_path_with_home_expansion(data_path) if data_path
30
+ config_path = absolute_path_with_home_expansion(options[:config]) if options[:config]
30
31
 
31
32
  Dir.chdir(tmpdir) do
32
33
  Migration.new.run(Logger::FATAL) # Hide migration output for evaluator
33
- evaluate(ig_path, data_path, options)
34
+ evaluate(ig_path, data_path, config_path, options)
34
35
  end
35
36
  ensure
36
37
  system("#{services_base_command} down #{services_names}")
@@ -57,14 +58,14 @@ module Inferno
57
58
  end
58
59
 
59
60
  # @see Inferno::CLI::Main#evaluate
60
- def evaluate(ig_path, data_path, options)
61
+ def evaluate(ig_path, data_path, config_path, options)
61
62
  # NOTE: repositories is required here rather than at the top of the file because
62
63
  # the tree of requires means that this file and its requires get required by every CLI app.
63
64
  # Sequel::Model, used in some repositories, fetches the table schema at instantiation.
64
65
  # This breaks the `migrate` task by fetching a table before the task runs/creates it.
65
66
  require_relative '../../repositories'
66
67
 
67
- validate_args(ig_path, data_path)
68
+ validate_args(ig_path, data_path, config_path)
68
69
  ig = Inferno::Repositories::IGs.new.find_or_load(ig_path)
69
70
 
70
71
  check_ig_version(ig)
@@ -80,17 +81,17 @@ module Inferno
80
81
 
81
82
  evaluator = Inferno::DSL::FHIREvaluation::Evaluator.new(ig, validator)
82
83
 
83
- config = Inferno::DSL::FHIREvaluation::Config.new
84
+ config = Inferno::DSL::FHIREvaluation::Config.new(config_path)
84
85
  results = evaluator.evaluate(data, config)
85
86
  output_results(results, options[:output])
86
87
  end
87
88
 
88
- def validate_args(ig_path, data_path)
89
+ def validate_args(ig_path, data_path, config_path)
89
90
  raise 'A path to an IG is required!' unless ig_path
90
91
 
91
- return unless data_path && (!File.directory? data_path)
92
+ raise "Provided path '#{data_path}' is not a directory" if data_path && (!File.directory? data_path)
92
93
 
93
- raise "Provided path '#{data_path}' is not a directory"
94
+ raise "Provided path '#{config_path}' does not exist" if config_path && (!File.exist? config_path)
94
95
  end
95
96
 
96
97
  def check_ig_version(ig)
@@ -6,8 +6,8 @@ require_relative 'services'
6
6
  require_relative 'suite'
7
7
  require_relative 'suites'
8
8
  require_relative 'new'
9
- require_relative '../../version'
10
9
  require_relative 'execute'
10
+ require_relative '../../version'
11
11
 
12
12
  module Inferno
13
13
  module CLI
@@ -34,6 +34,9 @@ module Inferno
34
34
 
35
35
  # Loads the us core ig and evaluate the data included in the IG's example folder, with results redirected to outcome.json as an OperationOutcome
36
36
  `bundle exec inferno evaluate ./uscore.tgz --output outcome.json`
37
+
38
+ # Loads the us core ig and evaluate the data included in the IG's example folder using a custom configuration file
39
+ `bundle exec inferno evaluate ./uscore.tgz --config ./custom_config.yml`
37
40
  LONGDESC
38
41
  # TODO: Add options below as arguments
39
42
  option :data_path,
@@ -45,6 +48,10 @@ module Inferno
45
48
  aliases: ['-o'],
46
49
  type: :string,
47
50
  desc: 'Export evaluation result to outcome.json as an OperationOutcome'
51
+ option :config,
52
+ aliases: ['-c'],
53
+ type: :string,
54
+ desc: 'Path to a custom configuration file'
48
55
  def evaluate(ig_path)
49
56
  Evaluate.new.run(ig_path, options[:data_path], options)
50
57
  end
@@ -92,6 +99,10 @@ module Inferno
92
99
 
93
100
  desc 'suites', 'List available test suites'
94
101
  def suites
102
+ ENV['NO_DB'] = 'true'
103
+
104
+ require_relative '../../../inferno'
105
+
95
106
  Suites.new.run
96
107
  end
97
108
 
@@ -1,3 +1,4 @@
1
+ require_relative 'requirements_coverage_checker'
1
2
  require_relative 'requirements_exporter'
2
3
 
3
4
  module Inferno
@@ -23,6 +24,54 @@ module Inferno
23
24
  ENV['NO_DB'] = 'true'
24
25
  RequirementsExporter.new.run_check
25
26
  end
27
+
28
+ desc 'coverage [TEST_SUITE_ID]',
29
+ "Check whether all of a test suite's requirements are tested. If no test suite id is provided, " \
30
+ 'all test suites in the current test kit will be checked.'
31
+ long_desc <<~LONGDESC
32
+ Check whether all of the requirements declared by a test suite are
33
+ tested by the tests in the test suite
34
+ LONGDESC
35
+ def coverage(test_suite_id = nil)
36
+ ENV['NO_DB'] = 'true'
37
+
38
+ require_relative '../../../inferno'
39
+
40
+ Inferno::Application.start(:requirements)
41
+
42
+ if test_suite_id.present?
43
+ RequirementsCoverageChecker.new(test_suite_id).run
44
+ else
45
+ Inferno::Repositories::TestSuites.all.each do |test_suite|
46
+ if Object.const_source_location(test_suite.to_s).first.start_with? Dir.pwd
47
+ RequirementsCoverageChecker.new(test_suite.id).run
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ desc 'check_coverage [TEST_SUITE_ID]',
54
+ 'Check whether the coverage CSV files are up to date'
55
+ long_desc <<~LONGDESC
56
+ Check whether the coverage CSV files are up to date
57
+ LONGDESC
58
+ def check_coverage(test_suite_id = nil)
59
+ ENV['NO_DB'] = 'true'
60
+
61
+ require_relative '../../../inferno'
62
+
63
+ Inferno::Application.start(:requirements)
64
+
65
+ if test_suite_id.present?
66
+ RequirementsCoverageChecker.new(test_suite_id).run_check
67
+ else
68
+ Inferno::Repositories::TestSuites.all.each do |test_suite|
69
+ if Object.const_source_location(test_suite.to_s).first.start_with? Dir.pwd
70
+ RequirementsCoverageChecker.new(test_suite.id).run_check
71
+ end
72
+ end
73
+ end
74
+ end
26
75
  end
27
76
  end
28
77
  end
@@ -0,0 +1,238 @@
1
+ require_relative 'requirements_exporter'
2
+
3
+ module Inferno
4
+ module CLI
5
+ class RequirementsCoverageChecker
6
+ attr_accessor :test_suite_id, :test_suite
7
+
8
+ def initialize(test_suite_id)
9
+ self.test_suite_id = test_suite_id
10
+ self.test_suite = Inferno::Repositories::TestSuites.new.find(test_suite_id)
11
+ end
12
+
13
+ def short_id_header
14
+ "#{test_suite.title} Short ID(s)"
15
+ end
16
+
17
+ def full_id_header
18
+ "#{test_suite.title} Full ID(s)"
19
+ end
20
+
21
+ def output_headers
22
+ [
23
+ *(RequirementsExporter::REQUIREMENTS_OUTPUT_HEADERS - ['Sub-Requirement(s)']),
24
+ short_id_header,
25
+ full_id_header
26
+ ]
27
+ end
28
+
29
+ def test_kit_name
30
+ local_test_kit_gem.name
31
+ end
32
+
33
+ def base_requirements_folder
34
+ RequirementsExporter.new.base_requirements_folder
35
+ end
36
+
37
+ def output_folder
38
+ @output_folder ||= File.join(base_requirements_folder, 'generated')
39
+ end
40
+
41
+ def output_file_name
42
+ "#{test_suite_id}_requirements_coverage.csv"
43
+ end
44
+
45
+ def output_file_path
46
+ @output_file_path ||= File.join(output_folder, output_file_name)
47
+ end
48
+
49
+ def suite_requirements
50
+ @suite_requirements ||=
51
+ Inferno::Repositories::Requirements.new.requirements_for_suite(test_suite_id)
52
+ end
53
+
54
+ def tested_requirement_ids
55
+ @tested_requirement_ids ||= test_suite.all_verified_requirements
56
+ end
57
+
58
+ def suite_runnables
59
+ @suite_runnables ||= [test_suite] + test_suite.all_descendants
60
+ end
61
+
62
+ def untested_requirements
63
+ @untested_requirements ||= []
64
+ end
65
+
66
+ def new_csv # rubocop:disable Metrics/CyclomaticComplexity
67
+ @new_csv ||=
68
+ CSV.generate(+"\xEF\xBB\xBF") do |csv|
69
+ csv << output_headers
70
+
71
+ suite_requirements.each do |requirement|
72
+ if requirement.not_tested_reason.present?
73
+ long_ids = 'NA'
74
+ short_ids = 'NA'
75
+ else
76
+ runnables_for_requirement =
77
+ suite_runnables.select { |runnable| runnable.verifies_requirements.include? requirement.id }
78
+ long_ids = runnables_for_requirement&.map(&:id)&.join(', ')
79
+ short_ids =
80
+ runnables_for_requirement
81
+ &.map { |runnable| runnable < Inferno::Entities::TestSuite ? 'suite' : runnable.short_id }
82
+ &.join(', ')
83
+ end
84
+
85
+ untested_requirements << runnables_for_requirement if runnables_for_requirement.blank?
86
+
87
+ row = [
88
+ requirement.requirement_set,
89
+ requirement.id.delete_prefix("#{requirement.requirement_set}@"),
90
+ requirement.url,
91
+ requirement.requirement,
92
+ requirement.conformance,
93
+ requirement.actor,
94
+ requirement.conditionality,
95
+ requirement.not_tested_reason,
96
+ requirement.not_tested_details,
97
+ short_ids,
98
+ long_ids
99
+ ]
100
+
101
+ csv << row
102
+ end
103
+ end
104
+ end
105
+
106
+ def input_requirement_ids
107
+ @input_requirement_ids ||= input_rows.map { |row| "#{row['Req Set']}@#{row['ID']}" }
108
+ end
109
+
110
+ # The requirements present in Inferno that aren't in the input spreadsheet
111
+ def unmatched_requirement_ids
112
+ @unmatched_requirement_ids ||=
113
+ tested_requirement_ids - suite_requirements.map(&:id)
114
+ end
115
+
116
+ def unmatched_requirement_rows
117
+ unmatched_requirement_ids.flat_map do |requirement_id|
118
+ runnables_for_requirement =
119
+ suite_runnables.select { |runnable| runnable.verifies_requirements.include? requirement_id }
120
+
121
+ runnables_for_requirement.map do |runnable|
122
+ [requirement_id, runnable.short_id, runnable.id]
123
+ end
124
+ end
125
+ end
126
+
127
+ def old_csv
128
+ @old_csv ||= File.read(output_file_path)
129
+ end
130
+
131
+ def run
132
+ unless test_suite.present?
133
+ puts "Could not find test suite: #{test_suite_id}. Aborting requirements coverage generation..."
134
+ exit(1)
135
+ end
136
+
137
+ puts
138
+
139
+ if unmatched_requirement_ids.present?
140
+ puts "WARNING: The following requirements indicated in the test suite #{test_suite_id} are not present in " \
141
+ "the suite's requirement sets:"
142
+ output_requirements_map_table(unmatched_requirement_rows)
143
+ end
144
+
145
+ if File.exist?(output_file_path)
146
+ if old_csv == new_csv
147
+ puts "'#{output_file_name}' file is up to date."
148
+ return
149
+ else
150
+ puts 'Requirements coverage has changed.'
151
+ end
152
+ else
153
+ puts "No existing #{output_file_name}."
154
+ end
155
+
156
+ puts "Writing to file #{output_file_path}..."
157
+ FileUtils.mkdir_p(output_folder)
158
+ File.write(output_file_path, new_csv)
159
+ puts 'Done.'
160
+ end
161
+
162
+ def run_check
163
+ unless test_suite.present?
164
+ puts "Could not find test suite: #{test_suite_id}. Aborting requirements coverage generation..."
165
+ exit(1)
166
+ end
167
+
168
+ puts
169
+
170
+ if unmatched_requirement_ids.any?
171
+ puts "WARNING: The following requirements indicated in the test suite #{test_suite_id} are not present in " \
172
+ "the suite's requirement sets:"
173
+ output_requirements_map_table(unmatched_requirement_rows)
174
+ end
175
+
176
+ if File.exist?(output_file_path)
177
+ if old_csv == new_csv
178
+ puts "'#{output_file_name}' file is up to date."
179
+ return unless unmatched_requirement_ids.present?
180
+ else
181
+ puts <<~MESSAGE
182
+ #{output_file_name} file is out of date.
183
+ To regenerate the file, run:
184
+
185
+ bundle exec inferno requirements coverage
186
+
187
+ MESSAGE
188
+ end
189
+ else
190
+ puts <<~MESSAGE
191
+ No existing #{output_file_name} file.
192
+ To generate the file, run:
193
+
194
+ bundle exec inferno requirements coverage
195
+
196
+ MESSAGE
197
+ end
198
+
199
+ puts 'Check failed.'
200
+ exit(1)
201
+ end
202
+
203
+ # Output the requirements in the map like so:
204
+ #
205
+ # requirement_id | short_id | full_id
206
+ # ---------------+------------+----------
207
+ # req-id-1 | short-id-1 | full-id-1
208
+ # req-id-2 | short-id-2 | full-id-2
209
+ #
210
+ def output_requirements_map_table(requirements_rows)
211
+ headers = %w[requirement_id short_id full_id]
212
+ col_widths = headers.map(&:length)
213
+ col_widths[0] = [col_widths[0], *requirements_rows.map { |row| row[0].length }].max
214
+ col_widths[1] = [col_widths[1], *requirements_rows.map { |row| row[1].length }].max
215
+ col_widths[2] = [col_widths[2], *requirements_rows.map { |row| row[2].length }].max
216
+ col_widths.map! { |width| width + 3 }
217
+
218
+ puts [
219
+ headers[0].ljust(col_widths[0]),
220
+ headers[1].ljust(col_widths[1]),
221
+ headers[2].ljust(col_widths[2])
222
+ ].join(' | ')
223
+ puts col_widths.map { |width| '-' * width }.join('-+-')
224
+ output_requirements_map_table_contents(requirements_rows, col_widths)
225
+ end
226
+
227
+ def output_requirements_map_table_contents(requirements_rows, col_widths)
228
+ requirements_rows.each do |requirements_row|
229
+ puts [
230
+ requirements_row[0].ljust(col_widths[0]),
231
+ requirements_row[1].ljust(col_widths[1]),
232
+ requirements_row[2].ljust(col_widths[2])
233
+ ].join(' | ')
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -15,6 +15,9 @@ module Inferno
15
15
  option :filename, banner: '<filename>', aliases: [:f]
16
16
  def input_template(suite_id)
17
17
  ENV['NO_DB'] = 'true'
18
+
19
+ require_relative '../../../inferno'
20
+
18
21
  SuiteInputTemplate.new.run(suite_id, options)
19
22
  end
20
23
 
@@ -24,6 +27,9 @@ module Inferno
24
27
  LONGDESC
25
28
  def describe(suite_id)
26
29
  ENV['NO_DB'] = 'true'
30
+
31
+ require_relative '../../../inferno'
32
+
27
33
  Inferno::Application.start(:suites)
28
34
 
29
35
  suite = Inferno::Repositories::TestSuites.new.find(suite_id)
@@ -59,6 +65,9 @@ module Inferno
59
65
  LONGDESC
60
66
  def lock_short_ids(suite_id)
61
67
  ENV['NO_DB'] = 'true'
68
+
69
+ require_relative '../../../inferno'
70
+
62
71
  Inferno::Application.start(:suites)
63
72
 
64
73
  suite = Inferno::Repositories::TestSuites.new.find(suite_id)
@@ -0,0 +1,68 @@
1
+ module Inferno
2
+ module Web
3
+ module Controllers
4
+ module TestSessions
5
+ module Results
6
+ class IoValue < Controller
7
+ include Import[
8
+ test_sessions_repo: 'inferno.repositories.test_sessions',
9
+ results_repo: 'inferno.repositories.results'
10
+ ]
11
+
12
+ def handle(req, res)
13
+ test_session_id = req.params[:id]
14
+ result_id = req.params[:result_id]
15
+ type = req.params[:type]
16
+ name = req.params[:name]
17
+
18
+ unless %w[inputs outputs].include?(type)
19
+ res.status = 400
20
+ res.body = { error: 'Invalid I/O type. Must be "inputs" or "outputs".' }.to_json
21
+ return
22
+ end
23
+
24
+ test_session_results = results_repo.current_results_for_test_session(test_session_id)
25
+ result = test_session_results.find { |r| r.id == result_id }
26
+
27
+ if result.nil?
28
+ res.status = 404
29
+ res.body = { error: "Result '#{result_id}' not found for test session '#{test_session_id}'." }.to_json
30
+ return
31
+ end
32
+
33
+ entry = result_io_by_name(result, type, name)
34
+ value = entry&.dig('value')
35
+
36
+ if value.blank?
37
+ res.status = 404
38
+ res.body = { error: "#{type.singularize.capitalize} '#{name}' not found or missing value." }.to_json
39
+ return
40
+ end
41
+
42
+ res.body = value.is_a?(Hash) ? value.to_json : value
43
+ res.content_type = content_type_for(value)
44
+ end
45
+
46
+ private
47
+
48
+ def result_io_by_name(result, io_type, name)
49
+ result.public_send(io_type)
50
+ .find { |item| item['name'] == name }
51
+ end
52
+
53
+ def content_type_for(value)
54
+ return 'application/json' if value.is_a?(Hash)
55
+
56
+ trimmed = value&.strip
57
+
58
+ return 'application/json' if trimmed&.start_with?('{', '[')
59
+ return 'application/xml' if trimmed&.start_with?('<') && trimmed.end_with?('>')
60
+
61
+ 'text/plain'
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -34,6 +34,9 @@ module Inferno
34
34
  get '/:id/results',
35
35
  to: Inferno::Web::Controllers::TestSessions::Results::Index,
36
36
  as: :results
37
+ get '/:id/results/:result_id/io/:type/:name',
38
+ to: Inferno::Web::Controllers::TestSessions::Results::IoValue,
39
+ as: :result_io_value
37
40
  get '/:id/session_data',
38
41
  to: Inferno::Web::Controllers::TestSessions::SessionData::Index
39
42
  put '/:id/session_data/apply_preset',
@@ -21,11 +21,11 @@ module Inferno
21
21
  field :optional?, name: :optional
22
22
 
23
23
  field :inputs do |result, _options|
24
- result.input_json.present? ? JSON.parse(result.input_json) : []
24
+ result.handle_large_io('inputs')
25
25
  end
26
26
 
27
27
  field :outputs do |result, _options|
28
- result.output_json.present? ? JSON.parse(result.output_json) : []
28
+ result.handle_large_io('outputs')
29
29
  end
30
30
 
31
31
  association :messages, blueprint: Message, if: :field_present?
@@ -52,7 +52,11 @@ module Inferno
52
52
  # @see Inferno::DSL::FHIRClientBuilder
53
53
  def fhir_client(client = :default)
54
54
  fhir_clients[client] ||=
55
- FHIRClientBuilder.new.build(self, self.class.fhir_client_definitions[client])
55
+ FHIRClientBuilder.new.build(self, find_fhir_client_definition(client))
56
+ end
57
+
58
+ def find_fhir_client_definition(client)
59
+ self.class.find_fhir_client_definition(client)
56
60
  end
57
61
 
58
62
  # @private
@@ -443,6 +447,10 @@ module Inferno
443
447
  @fhir_client_definitions ||= {}
444
448
  end
445
449
 
450
+ def find_fhir_client_definition(client)
451
+ fhir_client_definitions[client] || parent&.find_fhir_client_definition(client)
452
+ end
453
+
446
454
  # Define a FHIR client to be used by a Runnable.
447
455
  #
448
456
  # @param name [Symbol] a name used to reference this particular client
@@ -46,7 +46,7 @@ module Inferno
46
46
  def http_client(client = :default)
47
47
  return http_clients[client] if http_clients[client]
48
48
 
49
- definition = self.class.http_client_definitions[client]
49
+ definition = find_http_client_definition(client)
50
50
  return nil if definition.nil?
51
51
 
52
52
  tcp_exception_handler do
@@ -54,6 +54,10 @@ module Inferno
54
54
  end
55
55
  end
56
56
 
57
+ def find_http_client_definition(client)
58
+ self.class.find_http_client_definition(client)
59
+ end
60
+
57
61
  # @private
58
62
  def http_clients
59
63
  @http_clients ||= {}
@@ -196,6 +200,10 @@ module Inferno
196
200
  @http_client_definitions ||= {}
197
201
  end
198
202
 
203
+ def find_http_client_definition(client)
204
+ http_client_definitions[client] || parent&.find_http_client_definition(client)
205
+ end
206
+
199
207
  # Define a HTTP client to be used by a Runnable.
200
208
  #
201
209
  # @param name [Symbol] a name used to reference this particular client
@@ -39,6 +39,7 @@ module Inferno
39
39
  # @private
40
40
  VARIABLES_NOT_TO_COPY = [
41
41
  :@id, # New runnable will have a different id
42
+ :@database_id, # New runnable will have a different database_id
42
43
  :@parent, # New runnable unlikely to have the same parent
43
44
  :@all_children, # New subclasses have to be made for each child
44
45
  :@test_count, # Needs to be recalculated
@@ -164,22 +165,6 @@ module Inferno
164
165
  klass.output output_name
165
166
  end
166
167
 
167
- new_fhir_client_definitions = klass.instance_variable_get(:@fhir_client_definitions) || {}
168
- fhir_client_definitions.each do |name, definition|
169
- next if new_fhir_client_definitions.include? name
170
-
171
- new_fhir_client_definitions[name] = definition.dup
172
- end
173
- klass.instance_variable_set(:@fhir_client_definitions, new_fhir_client_definitions)
174
-
175
- new_http_client_definitions = klass.instance_variable_get(:@http_client_definitions) || {}
176
- http_client_definitions.each do |name, definition|
177
- next if new_http_client_definitions.include? name
178
-
179
- new_http_client_definitions[name] = definition.dup
180
- end
181
- klass.instance_variable_set(:@http_client_definitions, new_http_client_definitions)
182
-
183
168
  klass.config(config)
184
169
 
185
170
  klass.all_children.select!(&:required?) if hash_args.delete(:exclude_optional)
@@ -210,20 +195,20 @@ module Inferno
210
195
  def id(new_id = nil)
211
196
  return @id if new_id.nil? && @id.present?
212
197
 
213
- prefix =
214
- if parent
215
- "#{parent.id}-"
216
- else
217
- ''
218
- end
219
-
198
+ prefix = parent ? "#{parent.id}-" : ''
220
199
  @base_id = new_id || @base_id || default_id
200
+ @id = "#{prefix}#{@base_id}"
221
201
 
222
- final_id = "#{prefix}#{@base_id}"
202
+ if @id.length > 255
203
+ hash = Digest::SHA1.hexdigest(@id)[0...10]
204
+ @database_id = "#{@id[0...244]}-#{hash}"
205
+ end
223
206
 
224
- raise Exceptions::InvalidRunnableIdException, final_id if final_id.length > 255
207
+ @id
208
+ end
225
209
 
226
- @id = final_id
210
+ def database_id
211
+ @database_id ||= id
227
212
  end
228
213
 
229
214
  # Set/Get a runnable's title
@@ -571,10 +556,10 @@ module Inferno
571
556
  end
572
557
 
573
558
  # @private
574
- def all_requirements(suite_options = [])
575
- children(suite_options).flat_map do |child|
576
- child.verifies_requirements + child.all_requirements(suite_options)
577
- end
559
+ def all_verified_requirements(suite_options = [])
560
+ verifies_requirements + children(suite_options).flat_map do |child|
561
+ child.all_verified_requirements(suite_options)
562
+ end.uniq
578
563
  end
579
564
 
580
565
  # @private