15133: Move removing jobs API code+tests
[arvados.git] / apps / workbench / test / controllers / pipeline_instances_controller_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6
7 class PipelineInstancesControllerTest < ActionController::TestCase
8   include PipelineInstancesHelper
9
10   def create_instance_long_enough_to(instance_attrs={})
11     # create 'two_part' pipeline with the given instance attributes
12     pt_fixture = api_fixture('pipeline_templates')['two_part']
13     post :create, params: {
14       pipeline_instance: instance_attrs.merge({
15         pipeline_template_uuid: pt_fixture['uuid']
16       }),
17       format: :json
18     }, session: session_for(:active)
19     assert_response :success
20     pi_uuid = assigns(:object).uuid
21     assert_not_nil assigns(:object)
22
23     # yield
24     yield pi_uuid, pt_fixture
25
26     # delete the pipeline instance
27     use_token :active
28     PipelineInstance.where(uuid: pi_uuid).first.destroy
29   end
30
31   test "can render pipeline instance with tagged collections" do
32     # Make sure to pass in a tagged collection to test that part of the rendering behavior.
33     get(:show,
34         params: {id: api_fixture("pipeline_instances")["pipeline_with_tagged_collection_input"]["uuid"]},
35         session: session_for(:active))
36     assert_response :success
37   end
38
39   test "update script_parameters one at a time using merge param" do
40       template_fixture = api_fixture('pipeline_templates')['two_part']
41       post :update, params: {
42         id: api_fixture("pipeline_instances")["pipeline_to_merge_params"]["uuid"],
43         pipeline_instance: {
44           components: {
45             "part-two" => {
46               script_parameters: {
47                 integer_with_value: {
48                   value: 9
49                 },
50                 plain_string: {
51                   value: 'quux'
52                 },
53               }
54             }
55           }
56         },
57         merge: true,
58         format: :json
59       }, session: session_for(:active)
60       assert_response :success
61       assert_not_nil assigns(:object)
62       orig_params = template_fixture['components']['part-two']['script_parameters']
63       new_params = assigns(:object).components[:'part-two'][:script_parameters]
64       orig_params.keys.each do |k|
65         unless %w(integer_with_value plain_string).index(k)
66           assert_equal orig_params[k].to_json, new_params[k.to_sym].to_json
67         end
68       end
69   end
70
71   test "component rendering copes with unexpected components format" do
72     get(:show,
73         params: {id: api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]},
74         session: session_for(:active))
75     assert_response :success
76   end
77
78   test "dates in JSON components are parsed" do
79     get(:show,
80         params: {id: api_fixture('pipeline_instances')['has_component_with_completed_jobs']['uuid']},
81         session: session_for(:active))
82     assert_response :success
83     assert_not_nil assigns(:object)
84     assert_not_nil assigns(:object).components[:foo][:job]
85     start_at = assigns(:object).components[:foo][:job][:started_at]
86     start_at = Time.parse(start_at) if (start_at.andand.class == String)
87     assert start_at.is_a? Time
88     finished_at = assigns(:object).components[:foo][:job][:started_at]
89     finished_at = Time.parse(finished_at) if (finished_at.andand.class == String)
90     assert finished_at.is_a? Time
91   end
92
93   # The next two tests ensure that a pipeline instance can be copied
94   # when the template has components that do not exist in the
95   # instance (ticket #4000).
96
97   test "generate graph" do
98
99     use_token 'admin'
100
101     pipeline_for_graph = {
102       state: 'Complete',
103       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9',
104       components: {
105         stage1: {
106           repository: 'foo',
107           script: 'hash',
108           script_version: 'master',
109           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
110           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
111         },
112         stage2: {
113           repository: 'foo',
114           script: 'hash2',
115           script_version: 'master',
116           script_parameters: {
117             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45'
118           },
119           job: {uuid: 'zzzzz-8i9sb-graphstage20000'},
120           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx'
121         }
122       }
123     }
124
125     @controller.params['tab_pane'] = "Graph"
126     provenance, pips = @controller.graph([pipeline_for_graph])
127
128     graph_test_collection1 = find_fixture Collection, "graph_test_collection1"
129     stage1 = find_fixture Job, "graph_stage1"
130     stage2 = find_fixture Job, "graph_stage2"
131
132     ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1',
133      'component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2',
134      stage1.uuid,
135      stage2.uuid,
136      stage1.output,
137      stage2.output,
138      pipeline_for_graph[:components][:stage1][:output_uuid],
139      pipeline_for_graph[:components][:stage2][:output_uuid]
140     ].each do |k|
141
142       assert_not_nil provenance[k], "Expected key #{k} in provenance set"
143       assert_equal 1, pips[k], "Expected key #{k} in pips set" if !k.start_with? "component_"
144     end
145
146     prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
147         :request => RequestDuck,
148         :all_script_parameters => true,
149         :combine_jobs => :script_and_version,
150         :pips => pips,
151         :only_components => true }
152
153     stage1_id = "#{stage1[:script]}_#{stage1[:script_version]}_#{Digest::MD5.hexdigest(stage1[:script_parameters].to_json)}"
154     stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}"
155
156     stage1_out = stage1[:output].gsub('+','\\\+')
157
158     assert_match /#{stage1_id}&#45;&gt;#{stage1_out}/, prov_svg
159
160     assert_match /#{stage1_out}&#45;&gt;#{stage2_id}/, prov_svg
161
162   end
163
164   test "generate graph compare" do
165
166     use_token 'admin'
167
168     pipeline_for_graph1 = {
169       state: 'Complete',
170       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9',
171       components: {
172         stage1: {
173           repository: 'foo',
174           script: 'hash',
175           script_version: 'master',
176           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
177           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
178         },
179         stage2: {
180           repository: 'foo',
181           script: 'hash2',
182           script_version: 'master',
183           script_parameters: {
184             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45'
185           },
186           job: {uuid: 'zzzzz-8i9sb-graphstage20000'},
187           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx'
188         }
189       }
190     }
191
192     pipeline_for_graph2 = {
193       state: 'Complete',
194       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc0',
195       components: {
196         stage1: {
197           repository: 'foo',
198           script: 'hash',
199           script_version: 'master',
200           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
201           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
202         },
203         stage2: {
204           repository: 'foo',
205           script: 'hash2',
206           script_version: 'master',
207           script_parameters: {
208           },
209           job: {uuid: 'zzzzz-8i9sb-graphstage30000'},
210           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujj'
211         }
212       }
213     }
214
215     @controller.params['tab_pane'] = "Graph"
216     provenance, pips = @controller.graph([pipeline_for_graph1, pipeline_for_graph2])
217
218     collection1 = find_fixture Collection, "graph_test_collection1"
219
220     stage1 = find_fixture Job, "graph_stage1"
221     stage2 = find_fixture Job, "graph_stage2"
222     stage3 = find_fixture Job, "graph_stage3"
223
224     [['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1', nil],
225      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2', nil],
226      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage1', nil],
227      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage2', nil],
228      [stage1.uuid, 3],
229      [stage2.uuid, 1],
230      [stage3.uuid, 2],
231      [stage1.output, 3],
232      [stage2.output, 1],
233      [stage3.output, 2],
234      [pipeline_for_graph1[:components][:stage1][:output_uuid], 3],
235      [pipeline_for_graph1[:components][:stage2][:output_uuid], 1],
236      [pipeline_for_graph2[:components][:stage2][:output_uuid], 2]
237     ].each do |k|
238       assert_not_nil provenance[k[0]], "Expected key #{k[0]} in provenance set"
239       assert_equal k[1], pips[k[0]], "Expected key #{k} in pips" if !k[0].start_with? "component_"
240     end
241
242     prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
243         :request => RequestDuck,
244         :all_script_parameters => true,
245         :combine_jobs => :script_and_version,
246         :pips => pips,
247         :only_components => true }
248
249     collection1_id = collection1.portable_data_hash.gsub('+','\\\+')
250
251     stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}"
252     stage3_id = "#{stage3[:script]}_#{stage3[:script_version]}_#{Digest::MD5.hexdigest(stage3[:script_parameters].to_json)}"
253
254     stage2_out = stage2[:output].gsub('+','\\\+')
255     stage3_out = stage3[:output].gsub('+','\\\+')
256
257     assert_match /#{collection1_id}&#45;&gt;#{stage2_id}/, prov_svg
258     assert_match /#{collection1_id}&#45;&gt;#{stage3_id}/, prov_svg
259
260     assert_match /#{stage2_id}&#45;&gt;#{stage2_out}/, prov_svg
261     assert_match /#{stage3_id}&#45;&gt;#{stage3_out}/, prov_svg
262
263   end
264
265 end