inferno_core 0.6.10 → 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 +4 -4
- data/lib/inferno/apps/cli/evaluate.rb +8 -7
- data/lib/inferno/apps/cli/execute.rb +17 -21
- data/lib/inferno/apps/cli/main.rb +12 -1
- data/lib/inferno/apps/cli/requirements.rb +49 -0
- data/lib/inferno/apps/cli/requirements_coverage_checker.rb +238 -0
- data/lib/inferno/apps/cli/suite.rb +9 -0
- data/lib/inferno/apps/web/controllers/test_sessions/results/io_value.rb +68 -0
- data/lib/inferno/apps/web/router.rb +13 -5
- data/lib/inferno/apps/web/serializers/result.rb +2 -2
- data/lib/inferno/apps/web/serializers/serializer.rb +7 -0
- data/lib/inferno/apps/web/serializers/test.rb +1 -1
- data/lib/inferno/apps/web/serializers/test_group.rb +1 -1
- data/lib/inferno/apps/web/serializers/test_suite.rb +2 -2
- data/lib/inferno/dsl/fhir_client.rb +9 -1
- data/lib/inferno/dsl/http_client.rb +9 -1
- data/lib/inferno/dsl/runnable.rb +15 -30
- data/lib/inferno/dsl/suite_requirements.rb +26 -26
- data/lib/inferno/entities/result.rb +39 -0
- data/lib/inferno/entities/test_kit.rb +4 -0
- data/lib/inferno/feature.rb +9 -0
- data/lib/inferno/public/bundle.js +34 -34
- data/lib/inferno/repositories/repository.rb +19 -0
- data/lib/inferno/repositories/requirements.rb +4 -3
- data/lib/inferno/repositories/results.rb +6 -3
- data/lib/inferno/repositories/runnable_repository.rb +29 -0
- data/lib/inferno/repositories/test_groups.rb +2 -2
- data/lib/inferno/repositories/test_sessions.rb +1 -0
- data/lib/inferno/repositories/test_suites.rb +2 -2
- data/lib/inferno/repositories/tests.rb +2 -2
- data/lib/inferno/repositories.rb +1 -0
- data/lib/inferno/version.rb +1 -1
- metadata +6 -3
- data/spec/features_helper.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7e5f22313a778dd7b70ba70eae9513bda158a7c0cd6beba9a2d195b3a37f077
|
4
|
+
data.tar.gz: 8e7d6155fad17e00ccc48257853f4df2cc816d0965cc0b18a50515bbd26d343f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
92
|
+
raise "Provided path '#{data_path}' is not a directory" if data_path && (!File.directory? data_path)
|
92
93
|
|
93
|
-
raise "Provided path '#{
|
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,6 +6,7 @@ Dir[File.join(__dir__, 'execute', '*_outputter.rb')].each { |outputter| require
|
|
6
6
|
|
7
7
|
module Inferno
|
8
8
|
module CLI
|
9
|
+
# @private
|
9
10
|
class Execute
|
10
11
|
include ::Inferno::Utils::VerifyRunnable
|
11
12
|
include ::Inferno::Utils::PersistInputs
|
@@ -45,7 +46,6 @@ module Inferno
|
|
45
46
|
self.options = options
|
46
47
|
|
47
48
|
outputter.print_start_message(self.options)
|
48
|
-
|
49
49
|
load_preset_file_and_set_preset_id
|
50
50
|
|
51
51
|
results = []
|
@@ -104,18 +104,6 @@ module Inferno
|
|
104
104
|
@outputter ||= OUTPUTTERS[options[:outputter]].new
|
105
105
|
end
|
106
106
|
|
107
|
-
def load_preset_file_and_set_preset_id
|
108
|
-
return unless options[:preset_file]
|
109
|
-
raise StandardError, 'Cannot use `--preset-id` and `--preset-file` options together' if options[:preset_id]
|
110
|
-
|
111
|
-
raise StandardError, "File #{options[:preset_file]} not found" unless File.exist? options[:preset_file]
|
112
|
-
|
113
|
-
options[:preset_id] = JSON.parse(File.read(options[:preset_file]))['id']
|
114
|
-
raise StandardError, "Preset #{options[:preset_file]} is missing id" if options[:preset_id].nil?
|
115
|
-
|
116
|
-
presets_repo.insert_from_file(options[:preset_file])
|
117
|
-
end
|
118
|
-
|
119
107
|
def all_selected_groups_and_tests
|
120
108
|
@all_selected_groups_and_tests ||= runnables_by_short_id + groups + tests
|
121
109
|
end
|
@@ -123,7 +111,7 @@ module Inferno
|
|
123
111
|
def run_one(runnable, test_run)
|
124
112
|
verify_runnable(
|
125
113
|
runnable,
|
126
|
-
|
114
|
+
all_inputs,
|
127
115
|
test_session.suite_options
|
128
116
|
)
|
129
117
|
|
@@ -132,15 +120,14 @@ module Inferno
|
|
132
120
|
dispatch_job(test_run)
|
133
121
|
end
|
134
122
|
|
135
|
-
def
|
123
|
+
def all_inputs
|
136
124
|
if preset
|
137
|
-
|
138
|
-
|
139
|
-
end
|
125
|
+
test_sessions_repo.apply_preset(test_session, preset.id)
|
126
|
+
preset_inputs = session_data_repo.get_all_from_session(test_session.id)
|
140
127
|
|
141
|
-
options.fetch(:inputs, {}).
|
128
|
+
thor_hash_to_inputs_array(options.fetch(:inputs, {})) + preset_inputs.map(&:to_hash)
|
142
129
|
else
|
143
|
-
options.fetch(:inputs, {})
|
130
|
+
thor_hash_to_inputs_array(options.fetch(:inputs, {}))
|
144
131
|
end
|
145
132
|
end
|
146
133
|
|
@@ -159,6 +146,15 @@ module Inferno
|
|
159
146
|
@preset
|
160
147
|
end
|
161
148
|
|
149
|
+
def load_preset_file_and_set_preset_id
|
150
|
+
return unless options[:preset_file]
|
151
|
+
raise StandardError, 'Cannot use `--preset-id` and `--preset-file` options together' if options[:preset_id]
|
152
|
+
|
153
|
+
raise StandardError, "File #{options[:preset_file]} not found" unless File.exist? options[:preset_file]
|
154
|
+
|
155
|
+
options[:preset_id] = presets_repo.insert_from_file(options[:preset_file]).id
|
156
|
+
end
|
157
|
+
|
162
158
|
def suite
|
163
159
|
@suite ||= Inferno::Repositories::TestSuites.new.find(options[:suite])
|
164
160
|
|
@@ -214,7 +210,7 @@ module Inferno
|
|
214
210
|
{
|
215
211
|
test_session_id: test_session.id,
|
216
212
|
runnable_id_key(runnable) => runnable.id,
|
217
|
-
inputs:
|
213
|
+
inputs: all_inputs
|
218
214
|
}
|
219
215
|
end
|
220
216
|
|
@@ -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
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'erb'
|
2
|
+
require_relative '../../feature'
|
2
3
|
|
3
4
|
Dir.glob(File.join(__dir__, 'controllers', '**', '*.rb')).each { |path| require_relative path }
|
4
5
|
|
@@ -33,6 +34,9 @@ module Inferno
|
|
33
34
|
get '/:id/results',
|
34
35
|
to: Inferno::Web::Controllers::TestSessions::Results::Index,
|
35
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
|
36
40
|
get '/:id/session_data',
|
37
41
|
to: Inferno::Web::Controllers::TestSessions::SessionData::Index
|
38
42
|
put '/:id/session_data/apply_preset',
|
@@ -47,13 +51,17 @@ module Inferno
|
|
47
51
|
put '/:id/check_configuration',
|
48
52
|
to: Inferno::Web::Controllers::TestSuites::CheckConfiguration,
|
49
53
|
as: :check_configuration
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
if Feature.requirements_enabled?
|
55
|
+
get ':id/requirements',
|
56
|
+
to: Inferno::Web::Controllers::TestSuites::Requirements::Index,
|
57
|
+
as: :requirements
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
61
|
+
if Feature.requirements_enabled?
|
62
|
+
scope 'requirements' do
|
63
|
+
get '/:id', to: Inferno::Web::Controllers::Requirements::Show, as: :show
|
64
|
+
end
|
57
65
|
end
|
58
66
|
|
59
67
|
get '/requests/:id', to: Inferno::Web::Controllers::Requests::Show, as: :requests_show
|
@@ -21,11 +21,11 @@ module Inferno
|
|
21
21
|
field :optional?, name: :optional
|
22
22
|
|
23
23
|
field :inputs do |result, _options|
|
24
|
-
result.
|
24
|
+
result.handle_large_io('inputs')
|
25
25
|
end
|
26
26
|
|
27
27
|
field :outputs do |result, _options|
|
28
|
-
result.
|
28
|
+
result.handle_large_io('outputs')
|
29
29
|
end
|
30
30
|
|
31
31
|
association :messages, blueprint: Message, if: :field_present?
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'oj'
|
2
2
|
require 'blueprinter'
|
3
|
+
require_relative '../../../feature'
|
3
4
|
|
4
5
|
module Inferno
|
5
6
|
module Web
|
@@ -13,6 +14,12 @@ module Inferno
|
|
13
14
|
result.send(name).present?
|
14
15
|
end
|
15
16
|
end
|
17
|
+
|
18
|
+
# When removing the feature flag, replace all instances of this method
|
19
|
+
# with `.field_present?`
|
20
|
+
def self.field_present_and_requirements_enabled?(field_name, result, options)
|
21
|
+
field_present?(field_name, result, options) && Feature.requirements_enabled?
|
22
|
+
end
|
16
23
|
end
|
17
24
|
end
|
18
25
|
end
|
@@ -20,7 +20,7 @@ module Inferno
|
|
20
20
|
field :input_instructions
|
21
21
|
field :user_runnable?, name: :user_runnable
|
22
22
|
field :optional?, name: :optional
|
23
|
-
field :verifies_requirements, if: :
|
23
|
+
field :verifies_requirements, if: :field_present_and_requirements_enabled?
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -32,7 +32,7 @@ module Inferno
|
|
32
32
|
Input.render_as_hash(group.available_inputs(suite_options).values)
|
33
33
|
end
|
34
34
|
field :output_definitions, name: :outputs, extractor: HashValueExtractor
|
35
|
-
field :verifies_requirements, if: :
|
35
|
+
field :verifies_requirements, if: :field_present_and_requirements_enabled?
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|