require 'json' require 'test_plugin_helper' require 'foreman_tasks/test_helpers' require "#{ForemanTasks::Engine.root}/test/support/dummy_dynflow_action" class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase include ForemanTasks::TestHelpers::WithInThreadExecutor # override default send behavior for the test class TestConnectorPlaybookExecutionReporterTask < InsightsCloud::Async::ConnectorPlaybookExecutionReporterTask def send_report(report) output[:saved_reports] = (output[:saved_reports] || []) << report end end setup do feature = RemoteExecutionFeature.register( :rh_cloud_connector_run_playbook, N_('Run RH Cloud playbook'), description: N_('Run playbook genrated by Red Hat remediations app'), host_action_button: false, provided_inputs: ['playbook_url', 'report_url', 'correlation_id', 'report_interval'] ) puts "REX Register: #{feature.id}" @job_invocation = generate_job_invocation # reset connector feature ID cache TestConnectorPlaybookExecutionReporterTask.instance_variable_set(:@connector_feature_id, nil) puts RemoteExecutionFeature.all.to_a end test 'It reports finish playbook messages' do TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(true) actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation) actual_report = actual.output[:saved_reports].first.to_s assert_equal 1, actual.output[:saved_reports].size assert_not_nil actual_report actual_jsonl = read_jsonl(actual_report) assert_not_nil actual_report_finished = actual_jsonl.find { |l| l['type'] == 'playbook_run_completed' } assert_equal 'TEST_CORRELATION', actual_report_finished['correlation_id'] assert_equal 'success', actual_report_finished['status'] assert_not_nil actual_host_finished = actual_jsonl.find { |l| l['type'] == 'playbook_run_finished' && l['host'] == @host1.insights.uuid } assert_equal 'TEST_CORRELATION', actual_host_finished['correlation_id'] assert_equal 'success', actual_host_finished['status'] end test 'It reports single progress message for done host' do TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, true) actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation) actual_report = actual.output[:saved_reports].first.to_s assert_equal 1, actual.output[:saved_reports].size assert_not_nil actual_report actual_jsonl = read_jsonl(actual_report) actual_host_updates = actual_jsonl .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid } assert_equal 1, actual_host_updates.size assert_equal 0, actual_host_updates.first['sequence'] end test 'It reports two progress messages for in progress host' do TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, false, true) host1_task = @job_invocation.template_invocations.joins(:host).where(hosts: {name: @host1.name}).first.run_host_job_task host1_task.state = 'running' host1_task.save! actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation) assert_equal 2, actual.output[:saved_reports].size first_report = actual.output[:saved_reports].first.to_s actual_jsonl = read_jsonl(first_report) actual_host_updates = actual_jsonl .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid } assert_equal 1, actual_host_updates.size assert_equal 0, actual_host_updates.first['sequence'] actual_host_updates = actual_jsonl .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid } assert_equal 1, actual_host_updates.size assert_equal 0, actual_host_updates.first['sequence'] second_report = actual.output[:saved_reports].last.to_s actual_jsonl = read_jsonl(second_report) actual_host_updates = actual_jsonl .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid } assert_equal 1, actual_host_updates.size assert_equal 1, actual_host_updates.first['sequence'] actual_host_updates = actual_jsonl .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid } assert_equal 0, actual_host_updates.size end private def generate_job_invocation job_template = FactoryBot.build( :job_template, :template => 'BLEH' ) feature = RemoteExecutionFeature.feature!(:rh_cloud_connector_run_playbook).id puts "Generated feature: #{feature}" job_invocation = FactoryBot.create( :job_invocation, remote_execution_feature_id: feature, task_id: FactoryBot.create(:dynflow_task).id ) job_template.template_inputs << playbook_url_input = FactoryBot.build(:template_input, :name => 'playbook_url', :input_type => 'user', :required => true) job_template.template_inputs << report_url_input = FactoryBot.build(:template_input, :name => 'report_url', :input_type => 'user', :required => true) job_template.template_inputs << correlation_id_input = FactoryBot.build(:template_input, :name => 'correlation_id', :input_type => 'user', :required => true) job_template.template_inputs << report_interval_input = FactoryBot.build(:template_input, :name => 'report_interval', :input_type => 'user', :required => true) template_invocation = FactoryBot.build(:template_invocation, :template => job_template, :job_invocation => job_invocation ) template_invocation.input_values << FactoryBot.create( :template_invocation_input_value, :template_invocation => template_invocation, :template_input => playbook_url_input, :value => 'http://example.com/TEST_PLAYBOOK' ) template_invocation.input_values << FactoryBot.create( :template_invocation_input_value, :template_invocation => template_invocation, :template_input => report_url_input, :value => 'http://example.com/TEST_REPORT' ) template_invocation.input_values << FactoryBot.create( :template_invocation_input_value, :template_invocation => template_invocation, :template_input => correlation_id_input, :value => 'TEST_CORRELATION' ) template_invocation.input_values << FactoryBot.create( :template_invocation_input_value, :template_invocation => template_invocation, :template_input => report_interval_input, :value => '1' ) @host1 = FactoryBot.create(:host, :with_insights_hits, name: 'host1') @host1.insights.uuid = 'TEST_UUID1' @host1.insights.save! @host2 = FactoryBot.create(:host, :with_insights_hits, name: 'host2') @host2.insights.uuid = 'TEST_UUID2' @host2.insights.save! targeting = FactoryBot.create(:targeting, hosts: [@host1, @host2]) job_invocation.targeting = targeting job_invocation.save! job_invocation.template_invocations << FactoryBot.create( :template_invocation, run_host_job_task: FactoryBot.create(:dynflow_task), host_id: @host1.id ) job_invocation.template_invocations << FactoryBot.create( :template_invocation, run_host_job_task: FactoryBot.create(:dynflow_task), host_id: @host2.id ) fake_output = (1..5).map do |i| { 'timestamp' => (Time.now - (5 - i)).to_f, 'output' => "#{i}\n" } end Support::DummyDynflowAction.any_instance.stubs(:live_output).returns(fake_output) Support::DummyDynflowAction.any_instance.stubs(:exit_status).returns(0) job_invocation end def read_jsonl(jsonl) jsonl.lines.map { |l| JSON.parse(l) } end end