Merge branch '18794-config-health'
[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 "component rendering copes with unexpected components format" do
40     get(:show,
41         params: {id: api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]},
42         session: session_for(:active))
43     assert_response :success
44   end
45
46   test "dates in JSON components are parsed" do
47     get(:show,
48         params: {id: api_fixture('pipeline_instances')['has_component_with_completed_jobs']['uuid']},
49         session: session_for(:active))
50     assert_response :success
51     assert_not_nil assigns(:object)
52     assert_not_nil assigns(:object).components[:foo][:job]
53     start_at = assigns(:object).components[:foo][:job][:started_at]
54     start_at = Time.parse(start_at) if (start_at.andand.class == String)
55     assert start_at.is_a? Time
56     finished_at = assigns(:object).components[:foo][:job][:started_at]
57     finished_at = Time.parse(finished_at) if (finished_at.andand.class == String)
58     assert finished_at.is_a? Time
59   end
60
61   # The next two tests ensure that a pipeline instance can be copied
62   # when the template has components that do not exist in the
63   # instance (ticket #4000).
64
65   test "generate graph" do
66
67     use_token 'admin'
68
69     pipeline_for_graph = {
70       state: 'Complete',
71       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9',
72       components: {
73         stage1: {
74           repository: 'foo',
75           script: 'hash',
76           script_version: 'master',
77           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
78           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
79         },
80         stage2: {
81           repository: 'foo',
82           script: 'hash2',
83           script_version: 'master',
84           script_parameters: {
85             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45'
86           },
87           job: {uuid: 'zzzzz-8i9sb-graphstage20000'},
88           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx'
89         }
90       }
91     }
92
93     @controller.params['tab_pane'] = "Graph"
94     provenance, pips = @controller.graph([pipeline_for_graph])
95
96     graph_test_collection1 = find_fixture Collection, "graph_test_collection1"
97     stage1 = find_fixture Job, "graph_stage1"
98     stage2 = find_fixture Job, "graph_stage2"
99
100     ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1',
101      'component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2',
102      stage1.uuid,
103      stage2.uuid,
104      stage1.output,
105      stage2.output,
106      pipeline_for_graph[:components][:stage1][:output_uuid],
107      pipeline_for_graph[:components][:stage2][:output_uuid]
108     ].each do |k|
109
110       assert_not_nil provenance[k], "Expected key #{k} in provenance set"
111       assert_equal 1, pips[k], "Expected key #{k} in pips set" if !k.start_with? "component_"
112     end
113
114     prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
115         :request => RequestDuck,
116         :all_script_parameters => true,
117         :combine_jobs => :script_and_version,
118         :pips => pips,
119         :only_components => true }
120
121     stage1_id = "#{stage1[:script]}_#{stage1[:script_version]}_#{Digest::MD5.hexdigest(stage1[:script_parameters].to_json)}"
122     stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}"
123
124     stage1_out = stage1[:output].gsub('+','\\\+')
125
126     assert_match /#{stage1_id}&#45;&gt;#{stage1_out}/, prov_svg
127
128     assert_match /#{stage1_out}&#45;&gt;#{stage2_id}/, prov_svg
129
130   end
131
132   test "generate graph compare" do
133
134     use_token 'admin'
135
136     pipeline_for_graph1 = {
137       state: 'Complete',
138       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc9',
139       components: {
140         stage1: {
141           repository: 'foo',
142           script: 'hash',
143           script_version: 'master',
144           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
145           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
146         },
147         stage2: {
148           repository: 'foo',
149           script: 'hash2',
150           script_version: 'master',
151           script_parameters: {
152             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45'
153           },
154           job: {uuid: 'zzzzz-8i9sb-graphstage20000'},
155           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujx'
156         }
157       }
158     }
159
160     pipeline_for_graph2 = {
161       state: 'Complete',
162       uuid: 'zzzzz-d1hrv-9fm8l10i9z2kqc0',
163       components: {
164         stage1: {
165           repository: 'foo',
166           script: 'hash',
167           script_version: 'master',
168           job: {uuid: 'zzzzz-8i9sb-graphstage10000'},
169           output_uuid: 'zzzzz-4zz18-bv31uwvy3neko22'
170         },
171         stage2: {
172           repository: 'foo',
173           script: 'hash2',
174           script_version: 'master',
175           script_parameters: {
176           },
177           job: {uuid: 'zzzzz-8i9sb-graphstage30000'},
178           output_uuid: 'zzzzz-4zz18-uukreo9rbgwsujj'
179         }
180       }
181     }
182
183     @controller.params['tab_pane'] = "Graph"
184     provenance, pips = @controller.graph([pipeline_for_graph1, pipeline_for_graph2])
185
186     collection1 = find_fixture Collection, "graph_test_collection1"
187
188     stage1 = find_fixture Job, "graph_stage1"
189     stage2 = find_fixture Job, "graph_stage2"
190     stage3 = find_fixture Job, "graph_stage3"
191
192     [['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage1', nil],
193      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc9_stage2', nil],
194      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage1', nil],
195      ['component_zzzzz-d1hrv-9fm8l10i9z2kqc0_stage2', nil],
196      [stage1.uuid, 3],
197      [stage2.uuid, 1],
198      [stage3.uuid, 2],
199      [stage1.output, 3],
200      [stage2.output, 1],
201      [stage3.output, 2],
202      [pipeline_for_graph1[:components][:stage1][:output_uuid], 3],
203      [pipeline_for_graph1[:components][:stage2][:output_uuid], 1],
204      [pipeline_for_graph2[:components][:stage2][:output_uuid], 2]
205     ].each do |k|
206       assert_not_nil provenance[k[0]], "Expected key #{k[0]} in provenance set"
207       assert_equal k[1], pips[k[0]], "Expected key #{k} in pips" if !k[0].start_with? "component_"
208     end
209
210     prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
211         :request => RequestDuck,
212         :all_script_parameters => true,
213         :combine_jobs => :script_and_version,
214         :pips => pips,
215         :only_components => true }
216
217     collection1_id = collection1.portable_data_hash.gsub('+','\\\+')
218
219     stage2_id = "#{stage2[:script]}_#{stage2[:script_version]}_#{Digest::MD5.hexdigest(stage2[:script_parameters].to_json)}"
220     stage3_id = "#{stage3[:script]}_#{stage3[:script_version]}_#{Digest::MD5.hexdigest(stage3[:script_parameters].to_json)}"
221
222     stage2_out = stage2[:output].gsub('+','\\\+')
223     stage3_out = stage3[:output].gsub('+','\\\+')
224
225     assert_match /#{collection1_id}&#45;&gt;#{stage2_id}/, prov_svg
226     assert_match /#{collection1_id}&#45;&gt;#{stage3_id}/, prov_svg
227
228     assert_match /#{stage2_id}&#45;&gt;#{stage2_out}/, prov_svg
229     assert_match /#{stage3_id}&#45;&gt;#{stage3_out}/, prov_svg
230
231   end
232
233 end