X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/c446dd845656e944c2d3f91f8d00e320c37b66a1..e7672a0b679659d6ba90d23b4e102fba747fa9ef:/apps/workbench/test/controllers/pipeline_instances_controller_test.rb diff --git a/apps/workbench/test/controllers/pipeline_instances_controller_test.rb b/apps/workbench/test/controllers/pipeline_instances_controller_test.rb index d9f915bc90..f55c3ac872 100644 --- a/apps/workbench/test/controllers/pipeline_instances_controller_test.rb +++ b/apps/workbench/test/controllers/pipeline_instances_controller_test.rb @@ -3,38 +3,330 @@ require 'test_helper' class PipelineInstancesControllerTest < ActionController::TestCase include PipelineInstancesHelper - test "one" do - r = [{started_at: 1, finished_at: 3}] - assert_equal 2, determine_wallclock_runtime(r) + def create_instance_long_enough_to(instance_attrs={}) + # create 'two_part' pipeline with the given instance attributes + pt_fixture = api_fixture('pipeline_templates')['two_part'] + post :create, { + pipeline_instance: instance_attrs.merge({ + pipeline_template_uuid: pt_fixture['uuid'] + }), + format: :json + }, session_for(:active) + assert_response :success + pi_uuid = assigns(:object).uuid + assert_not_nil assigns(:object) - r = [{started_at: 1, finished_at: 5}] - assert_equal 4, determine_wallclock_runtime(r) + # yield + yield pi_uuid, pt_fixture - r = [{started_at: 1, finished_at: 2}, {started_at: 3, finished_at: 5}] - assert_equal 3, determine_wallclock_runtime(r) + # delete the pipeline instance + use_token :active + PipelineInstance.where(uuid: pi_uuid).first.destroy + end + + test "pipeline instance components populated after create" do + create_instance_long_enough_to do |new_instance_uuid, template_fixture| + assert_equal(template_fixture['components'].to_json, + assigns(:object).components.to_json) + end + end + + test "can render pipeline instance with tagged collections" do + # Make sure to pass in a tagged collection to test that part of the rendering behavior. + get(:show, + {id: api_fixture("pipeline_instances")["pipeline_with_tagged_collection_input"]["uuid"]}, + session_for(:active)) + assert_response :success + end + + test "update script_parameters one at a time using merge param" do + template_fixture = api_fixture('pipeline_templates')['two_part'] + post :update, { + id: api_fixture("pipeline_instances")["pipeline_to_merge_params"]["uuid"], + pipeline_instance: { + components: { + "part-two" => { + script_parameters: { + integer_with_value: { + value: 9 + }, + plain_string: { + value: 'quux' + }, + } + } + } + }, + merge: true, + format: :json + }, session_for(:active) + assert_response :success + assert_not_nil assigns(:object) + orig_params = template_fixture['components']['part-two']['script_parameters'] + new_params = assigns(:object).components[:'part-two'][:script_parameters] + orig_params.keys.each do |k| + unless %w(integer_with_value plain_string).index(k) + assert_equal orig_params[k].to_json, new_params[k.to_sym].to_json + end + end + end + + test "component rendering copes with unexpected components format" do + get(:show, + {id: api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]}, + session_for(:active)) + assert_response :success + end + + test "dates in JSON components are parsed" do + get(:show, + {id: api_fixture('pipeline_instances')['has_component_with_completed_jobs']['uuid']}, + session_for(:active)) + assert_response :success + assert_not_nil assigns(:object) + assert_not_nil assigns(:object).components[:foo][:job] + start_at = assigns(:object).components[:foo][:job][:started_at] + start_at = Time.parse(start_at) if (start_at.andand.class == String) + assert start_at.is_a? Time + finished_at = assigns(:object).components[:foo][:job][:started_at] + finished_at = Time.parse(finished_at) if (finished_at.andand.class == String) + assert finished_at.is_a? Time + end - r = [{started_at: 3, finished_at: 5}, {started_at: 1, finished_at: 2}] - assert_equal 3, determine_wallclock_runtime(r) + # The next two tests ensure that a pipeline instance can be copied + # when the template has components that do not exist in the + # instance (ticket #4000). - r = [{started_at: 3, finished_at: 5}, {started_at: 1, finished_at: 2}, - {started_at: 2, finished_at: 4}] - assert_equal 4, determine_wallclock_runtime(r) + test "copy pipeline instance with components=use_latest" do + post(:copy, + { + id: api_fixture('pipeline_instances')['pipeline_with_newer_template']['uuid'], + components: 'use_latest', + script: 'use_latest', + pipeline_instance: { + state: 'RunningOnServer' + } + }, + session_for(:active)) + assert_response 302 + assert_not_nil assigns(:object) - r = [{started_at: 1, finished_at: 5}, {started_at: 2, finished_at: 3}] - assert_equal 4, determine_wallclock_runtime(r) + # Component 'foo' has script parameters only in the pipeline instance. + # Component 'bar' is present only in the pipeline_template. + # Test that the copied pipeline instance includes parameters for + # component 'foo' from the source instance, and parameters for + # component 'bar' from the source template. + # + assert_not_nil assigns(:object).components[:foo] + foo = assigns(:object).components[:foo] + assert_not_nil foo[:script_parameters] + assert_not_nil foo[:script_parameters][:input] + assert_equal 'foo instance input', foo[:script_parameters][:input][:title] - r = [{started_at: 3, finished_at: 5}, {started_at: 1, finished_at: 4}] - assert_equal 4, determine_wallclock_runtime(r) + assert_not_nil assigns(:object).components[:bar] + bar = assigns(:object).components[:bar] + assert_not_nil bar[:script_parameters] + assert_not_nil bar[:script_parameters][:input] + assert_equal 'bar template input', bar[:script_parameters][:input][:title] + end - r = [{started_at: 1, finished_at: 4}, {started_at: 3, finished_at: 5}] - assert_equal 4, determine_wallclock_runtime(r) + test "copy pipeline instance on newer template works with script=use_same" do + post(:copy, + { + id: api_fixture('pipeline_instances')['pipeline_with_newer_template']['uuid'], + components: 'use_latest', + script: 'use_same', + pipeline_instance: { + state: 'RunningOnServer' + } + }, + session_for(:active)) + assert_response 302 + assert_not_nil assigns(:object) - r = [{started_at: 1, finished_at: 4}, {started_at: 3, finished_at: 5}, - {started_at: 5, finished_at: 8}] - assert_equal 7, determine_wallclock_runtime(r) + # Test that relevant component parameters were copied from both + # the source instance and source template, respectively (see + # previous test) + # + assert_not_nil assigns(:object).components[:foo] + foo = assigns(:object).components[:foo] + assert_not_nil foo[:script_parameters] + assert_not_nil foo[:script_parameters][:input] + assert_equal 'foo instance input', foo[:script_parameters][:input][:title] - r = [{started_at: 1, finished_at: 4}, {started_at: 3, finished_at: 5}, - {started_at: 6, finished_at: 8}] - assert_equal 6, determine_wallclock_runtime(r) + assert_not_nil assigns(:object).components[:bar] + bar = assigns(:object).components[:bar] + assert_not_nil bar[:script_parameters] + assert_not_nil bar[:script_parameters][:input] + assert_equal 'bar template input', bar[:script_parameters][:input][:title] end + + test "generate graph" do + + use_token 'admin' + + pipeline_for_graph = { + state: 'Complete', + uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9', + components: { + stage1: { + repository: 'foo', + script: 'hash', + script_version: 'master', + job: {uuid: 'zzzzz-8i9sb-graphstage10000'}, + output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22' + }, + stage2: { + repository: 'foo', + script: 'hash2', + script_version: 'master', + script_parameters: { + input: 'fa7aeb5140e2848d39b416daeef4ffc5+45' + }, + job: {uuid: 'zzzzz-8i9sb-graphstage20000'}, + output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx' + } + } + } + + @controller.params['tab_pane'] = "Graph" + provenance, pips = @controller.graph([pipeline_for_graph]) + + graph_test_collection1 = find_fixture Collection, "graph_test_collection1" + stage1 = find_fixture Job, "graph_stage1" + stage2 = find_fixture Job, "graph_stage2" + + ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1', + 'component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2', + stage1.uuid, + stage2.uuid, + stage1.output, + stage2.output, + pipeline_for_graph[:components][:stage1][:output_uuid], + pipeline_for_graph[:components][:stage2][:output_uuid] + ].each do |k| + + assert_not_nil provenance[k], "Expected key #{k} in provenance set" + assert_equal 1, pips[k], "Expected key #{k} in pips set" if !k.start_with? "component_" + end + + prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", { + :request => RequestDuck, + :all_script_parameters => true, + :combine_jobs => :script_and_version, + :pips => pips, + :only_components => true } + + stage1_id = "#{stage1[:script]}_#{stage1[:script_version]}_#{Digest::MD5.hexdigest(stage1[:script_parameters].to_json)}" + stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}" + + stage1_out = stage1[:output].gsub('+','\\\+') + + assert_match /#{stage1_id}->#{stage1_out}/, prov_svg + + assert_match /#{stage1_out}->#{stage2_id}/, prov_svg + + end + + test "generate graph compare" do + + use_token 'admin' + + pipeline_for_graph1 = { + state: 'Complete', + uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9', + components: { + stage1: { + repository: 'foo', + script: 'hash', + script_version: 'master', + job: {uuid: 'zzzzz-8i9sb-graphstage10000'}, + output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22' + }, + stage2: { + repository: 'foo', + script: 'hash2', + script_version: 'master', + script_parameters: { + input: 'fa7aeb5140e2848d39b416daeef4ffc5+45' + }, + job: {uuid: 'zzzzz-8i9sb-graphstage20000'}, + output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx' + } + } + } + + pipeline_for_graph2 = { + state: 'Complete', + uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc0', + components: { + stage1: { + repository: 'foo', + script: 'hash', + script_version: 'master', + job: {uuid: 'zzzzz-8i9sb-graphstage10000'}, + output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22' + }, + stage2: { + repository: 'foo', + script: 'hash2', + script_version: 'master', + script_parameters: { + }, + job: {uuid: 'zzzzz-8i9sb-graphstage30000'}, + output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujj' + } + } + } + + @controller.params['tab_pane'] = "Graph" + provenance, pips = @controller.graph([pipeline_for_graph1, pipeline_for_graph2]) + + collection1 = find_fixture Collection, "graph_test_collection1" + + stage1 = find_fixture Job, "graph_stage1" + stage2 = find_fixture Job, "graph_stage2" + stage3 = find_fixture Job, "graph_stage3" + + [['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1', nil], + ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2', nil], + ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage1', nil], + ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage2', nil], + [stage1.uuid, 3], + [stage2.uuid, 1], + [stage3.uuid, 2], + [stage1.output, 3], + [stage2.output, 1], + [stage3.output, 2], + [pipeline_for_graph1[:components][:stage1][:output_uuid], 3], + [pipeline_for_graph1[:components][:stage2][:output_uuid], 1], + [pipeline_for_graph2[:components][:stage2][:output_uuid], 2] + ].each do |k| + assert_not_nil provenance[k[0]], "Expected key #{k[0]} in provenance set" + assert_equal k[1], pips[k[0]], "Expected key #{k} in pips" if !k[0].start_with? "component_" + end + + prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", { + :request => RequestDuck, + :all_script_parameters => true, + :combine_jobs => :script_and_version, + :pips => pips, + :only_components => true } + + collection1_id = collection1.portable_data_hash.gsub('+','\\\+') + + stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}" + stage3_id = "#{stage3[:script]}_#{stage3[:script_version]}_#{Digest::MD5.hexdigest(stage3[:script_parameters].to_json)}" + + stage2_out = stage2[:output].gsub('+','\\\+') + stage3_out = stage3[:output].gsub('+','\\\+') + + assert_match /#{collection1_id}->#{stage2_id}/, prov_svg + assert_match /#{collection1_id}->#{stage3_id}/, prov_svg + + assert_match /#{stage2_id}->#{stage2_out}/, prov_svg + assert_match /#{stage3_id}->#{stage3_out}/, prov_svg + + end + end