From 15eb38c01dd5dcac1719baad039341ddf7e1baa2 Mon Sep 17 00:00:00 2001 From: Shaumik-Ashraf Date: Thu, 5 Dec 2024 11:02:00 -0500 Subject: [PATCH] FI-2961 execute preset support (#565) * add --preset option to execute and define #load_preset * cop * debug preset loading * wip * wip preset_id refactor * impl * debug and rspec * fix * use reverse_merge; remove test_sessions_repo#apply_preset --- config/presets/dev_validator_preset.json | 26 +++++++++++ lib/inferno/apps/cli/execute.rb | 53 +++++++++++++++++++++-- lib/inferno/apps/cli/main.rb | 12 ++++- lib/inferno/config/boot/executor.rb | 1 + spec/fixtures/dev_validator_preset_2.json | 26 +++++++++++ spec/inferno/cli/execute_spec.rb | 26 ++++++++++- 6 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 config/presets/dev_validator_preset.json create mode 100644 spec/fixtures/dev_validator_preset_2.json diff --git a/config/presets/dev_validator_preset.json b/config/presets/dev_validator_preset.json new file mode 100644 index 000000000..04f0d700f --- /dev/null +++ b/config/presets/dev_validator_preset.json @@ -0,0 +1,26 @@ +{ + "title": "Preset for Validator Suite", + "id": "dev_validator_preset", + "test_suite_id": "dev_validator", + "inputs": [ + { + "name": "url", + "value": "https://hapi.fhir.org/baseR4", + "_title": "FHIR Server Base Url", + "_type": "text" + }, + { + "name": "access_token", + "value": null, + "_title": "Bearer/Access Token", + "_type": "text", + "_optional": true + }, + { + "name": "patient_id", + "value": "1234321", + "_title": "Patient ID", + "_type": "text" + } + ] +} diff --git a/lib/inferno/apps/cli/execute.rb b/lib/inferno/apps/cli/execute.rb index 3a03d10e8..1ca5b1d76 100644 --- a/lib/inferno/apps/cli/execute.rb +++ b/lib/inferno/apps/cli/execute.rb @@ -44,10 +44,12 @@ def run(options) self.options = options - outputter.print_start_message(options) + outputter.print_start_message(self.options) + + load_preset_file_and_set_preset_id results = [] - outputter.print_around_run(options) do + outputter.print_around_run(self.options) do if all_selected_groups_and_tests.empty? test_run = create_test_run(suite) run_one(suite, test_run) @@ -102,6 +104,18 @@ def outputter @outputter ||= OUTPUTTERS[options[:outputter]].new end + def load_preset_file_and_set_preset_id + return unless options[:preset_file] + raise StandardError, 'Cannot use `--preset-id` and `--preset-file` options together' if options[:preset_id] + + raise StandardError, "File #{options[:preset_file]} not found" unless File.exist? options[:preset_file] + + options[:preset_id] = JSON.parse(File.read(options[:preset_file]))['id'] + raise StandardError, "Preset #{options[:preset_file]} is missing id" if options[:preset_id].nil? + + presets_repo.insert_from_file(options[:preset_file]) + end + def all_selected_groups_and_tests @all_selected_groups_and_tests ||= runnables_by_short_id + groups + tests end @@ -109,7 +123,7 @@ def all_selected_groups_and_tests def run_one(runnable, test_run) verify_runnable( runnable, - thor_hash_to_inputs_array(options[:inputs]), + thor_hash_to_inputs_array(inputs_and_preset), test_session.suite_options ) @@ -118,6 +132,33 @@ def run_one(runnable, test_run) dispatch_job(test_run) end + def inputs_and_preset + if preset + preset_inputs = preset.inputs.to_h do |preset_input| + [preset_input[:name], preset_input[:value]] + end + + options.fetch(:inputs, {}).reverse_merge(preset_inputs) + else + options.fetch(:inputs, {}) + end + end + + def preset + return unless options[:preset_id] + + @preset ||= presets_repo.find(options[:preset_id]) + + raise StandardError, "Preset #{options[:preset_id]} not found" if @preset.nil? + + unless presets_repo.presets_for_suite(suite.id).include?(@preset) + raise StandardError, + "Preset #{options[:preset_id]} is incompatible with suite #{suite.id}" + end + + @preset + end + def suite @suite ||= Inferno::Repositories::TestSuites.new.find(options[:suite]) @@ -156,6 +197,10 @@ def session_data_repo @session_data_repo ||= Inferno::Repositories::SessionData.new end + def presets_repo + @presets_repo ||= Inferno::Repositories::Presets.new + end + def test_session @test_session ||= test_sessions_repo.create({ test_suite_id: suite.id, @@ -169,7 +214,7 @@ def create_params(test_session, runnable) { test_session_id: test_session.id, runnable_id_key(runnable) => runnable.id, - inputs: thor_hash_to_inputs_array(options[:inputs]) + inputs: thor_hash_to_inputs_array(inputs_and_preset) } end diff --git a/lib/inferno/apps/cli/main.rb b/lib/inferno/apps/cli/main.rb index 5b926b28c..19db80278 100644 --- a/lib/inferno/apps/cli/main.rb +++ b/lib/inferno/apps/cli/main.rb @@ -125,7 +125,15 @@ def version option :inputs, aliases: ['-i'], type: :hash, - desc: 'Inputs (i.e: --inputs=foo:bar goo:baz)' + desc: 'Inputs (i.e: --inputs=foo:bar goo:baz); will merge and override preset inputs' + option :preset_id, + aliases: ['-P'], + type: :string, + desc: 'Inferno preset id; cannot be used with `--preset-file`' + option :preset_file, + aliases: ['-p'], + type: :string, + desc: 'Path to an Inferno preset file for inputs; cannot be used with `--preset-id`' option :outputter, aliases: ['-o'], default: 'console', @@ -142,7 +150,7 @@ def version desc: 'Display this message' def execute Execute.boot_full_inferno - Execute.new.run(options) + Execute.new.run(options.dup) # dup to unfreeze Thor options end # https://github.com/rails/thor/issues/244 - Make Thor exit(1) on Errors/Exceptions diff --git a/lib/inferno/config/boot/executor.rb b/lib/inferno/config/boot/executor.rb index 84b28a2c8..63a8b451d 100644 --- a/lib/inferno/config/boot/executor.rb +++ b/lib/inferno/config/boot/executor.rb @@ -10,5 +10,6 @@ end target_container.start :suites + target_container.start :presets end end diff --git a/spec/fixtures/dev_validator_preset_2.json b/spec/fixtures/dev_validator_preset_2.json new file mode 100644 index 000000000..59227fe8f --- /dev/null +++ b/spec/fixtures/dev_validator_preset_2.json @@ -0,0 +1,26 @@ +{ + "title": "Preset for Validator Suite in RSpec", + "id": "dev_validator_preset_2", + "test_suite_id": "dev_validator", + "inputs": [ + { + "name": "url", + "value": "https://hapi.fhir.org/baseR4", + "_title": "FHIR Server Base Url", + "_type": "text" + }, + { + "name": "access_token", + "value": null, + "_title": "Bearer/Access Token", + "_type": "text", + "_optional": true + }, + { + "name": "patient_id", + "value": "1234321", + "_title": "Patient ID", + "_type": "text" + } + ] +} diff --git a/spec/inferno/cli/execute_spec.rb b/spec/inferno/cli/execute_spec.rb index 37969ce64..e7545ff3d 100644 --- a/spec/inferno/cli/execute_spec.rb +++ b/spec/inferno/cli/execute_spec.rb @@ -237,12 +237,16 @@ end let(:inputs) { { 'url' => 'https://example.com', 'patient_id' => '1' } } + let(:preset_id) { 'dev_validator_preset' } + let(:preset_file) { Inferno::Application.root.join('spec/fixtures/dev_validator_preset_2.json').to_s } - it 'works on dev_validator suite' do + before do stub_request(:post, "#{ENV.fetch('FHIR_RESOURCE_VALIDATOR_URL')}/validate") .with(query: hash_including({})) .to_return(status: 200, body: success_outcome.to_json) + end + it 'works on dev_validator suite' do stub_request(:get, 'https://example.com/Patient/1') .to_return(status: 200, body: FHIR::Patient.new({ name: { given: 'Smith' } }).to_json) @@ -251,5 +255,25 @@ .to raise_error(an_instance_of(SystemExit).and(having_attributes(status: 0))) end.to output(/.+/).to_stdout end + + it 'works with preset id' do + stub_request(:get, 'https://hapi.fhir.org/baseR4/Patient/1234321') + .to_return(status: 200, body: FHIR::Patient.new({ name: { given: 'Smith' } }).to_json) + + expect do + expect { instance.run({ suite:, preset_id:, outputter: 'plain', verbose: true }) } + .to raise_error(an_instance_of(SystemExit).and(having_attributes(status: 0))) + end.to output(/.+/).to_stdout + end + + it 'works with preset file' do + stub_request(:get, 'https://hapi.fhir.org/baseR4/Patient/1234321') + .to_return(status: 200, body: FHIR::Patient.new({ name: { given: 'Smith' } }).to_json) + + expect do + expect { instance.run({ suite:, preset_file:, outputter: 'plain', verbose: true }) } + .to raise_error(an_instance_of(SystemExit).and(having_attributes(status: 0))) + end.to output(/.+/).to_stdout + end end end