8784: Fix test for latest firefox.
[arvados.git] / apps / workbench / test / controllers / pipeline_instances_controller_test.rb
index 70b7493db25deec7abceea0ee28da69069efc99c..f55c3ac8720fab7e481a10b3f90946f7616256c3 100644 (file)
@@ -3,54 +3,162 @@ 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)
-
-    r = [{started_at: 1, finished_at: 5}]
-    assert_equal 4, determine_wallclock_runtime(r)
-
-    r = [{started_at: 1, finished_at: 2}, {started_at: 3, finished_at: 5}]
-    assert_equal 3, determine_wallclock_runtime(r)
-
-    r = [{started_at: 3, finished_at: 5}, {started_at: 1, finished_at: 2}]
-    assert_equal 3, determine_wallclock_runtime(r)
-
-    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)
-
-    r = [{started_at: 1, finished_at: 5}, {started_at: 2, finished_at: 3}]
-    assert_equal 4, determine_wallclock_runtime(r)
-
-    r = [{started_at: 3, finished_at: 5}, {started_at: 1, finished_at: 4}]
-    assert_equal 4, 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)
+
+    # yield
+    yield pi_uuid, pt_fixture
+
+    # delete the pipeline instance
+    use_token :active
+    PipelineInstance.where(uuid: pi_uuid).first.destroy
+  end
 
-    r = [{started_at: 1, finished_at: 4}, {started_at: 3, finished_at: 5}]
-    assert_equal 4, determine_wallclock_runtime(r)
+  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
 
-    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 "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
 
-    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)
+  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
 
-  class RequestDuck
-    def self.host
-      "localhost"
-    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
 
-    def self.port
-      8080
-    end
+  # 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).
+
+  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)
+
+    # 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]
+
+    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
 
-    def self.protocol
-      "http"
-    end
+  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)
+
+    # 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]
+
+    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
@@ -84,14 +192,19 @@ class PipelineInstancesControllerTest < ActionController::TestCase
     @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',
-     'zzzzz-8i9sb-graphstage10000',
-     'zzzzz-8i9sb-graphstage20000',
-     'b519d9cb706a29fc7ea24dbea2f05851+93',
-     'fa7aeb5140e2848d39b416daeef4ffc5+45',
-     'zzzzz-4zz18-bv31uwvy3neko22',
-     'zzzzz-4zz18-uukreo9rbgwsujx'].each do |k|
+     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_"
@@ -104,16 +217,14 @@ class PipelineInstancesControllerTest < ActionController::TestCase
         :pips => pips,
         :only_components => true }
 
-    # hash -> owned_by_active
-    assert /hash_4fe459abe02d9b365932b8f5dc419439ab4e2577_99914b932bd37a50b983c5e7c90ae93b&#45;&gt;fa7aeb5140e2848d39b416daeef4ffc5\+45/.match(prov_svg)
+    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('+','\\\+')
 
-    # owned_by_active -> hash2
-    assert /fa7aeb5140e2848d39b416daeef4ffc5\+45&#45;&gt;hash2_4fe459abe02d9b365932b8f5dc419439ab4e2577_4900033ec5cfaf8a63566f3664aeaa70/.match(prov_svg)
+    assert_match /#{stage1_id}&#45;&gt;#{stage1_out}/, prov_svg
 
-    #File::open "./tmp/stuff1.svg", "w" do |f|
-    #  f.write "<?xml version=\"1.0\" ?>\n"
-    #  f.write prov_svg
-    #end
+    assert_match /#{stage1_out}&#45;&gt;#{stage2_id}/, prov_svg
 
   end
 
@@ -171,19 +282,25 @@ class PipelineInstancesControllerTest < ActionController::TestCase
     @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],
-     ['zzzzz-8i9sb-graphstage10000', 3],
-     ['zzzzz-8i9sb-graphstage20000', 1],
-     ['zzzzz-8i9sb-graphstage30000', 2],
-     ['b519d9cb706a29fc7ea24dbea2f05851+93', 1],
-     ['fa7aeb5140e2848d39b416daeef4ffc5+45', 3],
-     ['ea10d51bcf88862dbcc36eb292017dfd+45', 2],
-     ['zzzzz-4zz18-bv31uwvy3neko22', 3],
-     ['zzzzz-4zz18-uukreo9rbgwsujx', 1],
-     ['zzzzz-4zz18-uukreo9rbgwsujj', 2]
+     [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_"
@@ -196,22 +313,19 @@ class PipelineInstancesControllerTest < ActionController::TestCase
         :pips => pips,
         :only_components => true }
 
-    # owned_by_active -> hash2 (stuff)
-    assert /fa7aeb5140e2848d39b416daeef4ffc5\+45&#45;&gt;hash2_4fe459abe02d9b365932b8f5dc419439ab4e2577_4900033ec5cfaf8a63566f3664aeaa70/.match(prov_svg)
+    collection1_id = collection1.portable_data_hash.gsub('+','\\\+')
 
-    # owned_by_active -> hash2 (stuff2)
-    assert /fa7aeb5140e2848d39b416daeef4ffc5\+45&#45;&gt;hash2_4fe459abe02d9b365932b8f5dc419439ab4e2577_02a085407e751d00b5dc88f1bd5e8247/.match(prov_svg)
+    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)}"
 
-    # hash2 (stuff) -> GPL
-    assert /hash2_4fe459abe02d9b365932b8f5dc419439ab4e2577_4900033ec5cfaf8a63566f3664aeaa70&#45;&gt;b519d9cb706a29fc7ea24dbea2f05851\+93/.match(prov_svg)
+    stage2_out = stage2[:output].gsub('+','\\\+')
+    stage3_out = stage3[:output].gsub('+','\\\+')
 
-    # hash2 (stuff2) -> baz file
-    assert /hash2_4fe459abe02d9b365932b8f5dc419439ab4e2577_02a085407e751d00b5dc88f1bd5e8247&#45;&gt;ea10d51bcf88862dbcc36eb292017dfd\+45/.match(prov_svg)
+    assert_match /#{collection1_id}&#45;&gt;#{stage2_id}/, prov_svg
+    assert_match /#{collection1_id}&#45;&gt;#{stage3_id}/, prov_svg
 
-    # File::open "./tmp/stuff2.svg", "w" do |f|
-    #   f.write "<?xml version=\"1.0\" ?>\n"
-    #   f.write prov_svg
-    # end
+    assert_match /#{stage2_id}&#45;&gt;#{stage2_out}/, prov_svg
+    assert_match /#{stage3_id}&#45;&gt;#{stage3_out}/, prov_svg
 
   end