closes #11060
[arvados.git] / apps / workbench / test / controllers / application_controller_test.rb
1 require 'test_helper'
2
3 class ApplicationControllerTest < ActionController::TestCase
4   # These tests don't do state-changing API calls. Save some time by
5   # skipping the database reset.
6   reset_api_fixtures :after_each_test, false
7   reset_api_fixtures :after_suite, true
8
9   setup do
10     @user_dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('users')['active']['uuid'])
11   end
12
13   test "links for object" do
14     use_token :active
15
16     ac = ApplicationController.new
17
18     link_head_uuid = api_fixture('links')['foo_file_readable_by_active']['head_uuid']
19
20     links = ac.send :links_for_object, link_head_uuid
21
22     assert links, 'Expected links'
23     assert links.is_a?(Array), 'Expected an array'
24     assert links.size > 0, 'Expected at least one link'
25     assert links[0][:uuid], 'Expected uuid for the head_link'
26   end
27
28   test "preload links for objects and uuids" do
29     use_token :active
30
31     ac = ApplicationController.new
32
33     link1_head_uuid = api_fixture('links')['foo_file_readable_by_active']['head_uuid']
34     link2_uuid = api_fixture('links')['bar_file_readable_by_active']['uuid']
35     link3_head_uuid = api_fixture('links')['bar_file_readable_by_active']['head_uuid']
36
37     link2_object = User.find(api_fixture('users')['active']['uuid'])
38     link2_object_uuid = link2_object['uuid']
39
40     uuids = [link1_head_uuid, link2_object, link3_head_uuid]
41     links = ac.send :preload_links_for_objects, uuids
42
43     assert links, 'Expected links'
44     assert links.is_a?(Hash), 'Expected a hash'
45     assert links.size == 3, 'Expected two objects in the preloaded links hash'
46     assert links[link1_head_uuid], 'Expected links for the passed in link head_uuid'
47     assert links[link2_object_uuid], 'Expected links for the passed in object uuid'
48     assert links[link3_head_uuid], 'Expected links for the passed in link head_uuid'
49
50     # invoke again for this same input. this time, the preloaded data will be returned
51     links = ac.send :preload_links_for_objects, uuids
52     assert links, 'Expected links'
53     assert links.is_a?(Hash), 'Expected a hash'
54     assert links.size == 3, 'Expected two objects in the preloaded links hash'
55     assert links[link1_head_uuid], 'Expected links for the passed in link head_uuid'
56   end
57
58   [ [:preload_links_for_objects, [] ],
59     [:preload_collections_for_objects, [] ],
60     [:preload_log_collections_for_objects, [] ],
61     [:preload_objects_for_dataclass, [] ],
62     [:preload_for_pdhs, [] ],
63   ].each do |input|
64     test "preload data for empty array input #{input}" do
65       use_token :active
66
67       ac = ApplicationController.new
68
69       if input[0] == :preload_objects_for_dataclass
70         objects = ac.send input[0], @user_dataclass, input[1]
71       else
72         objects = ac.send input[0], input[1]
73       end
74
75       assert objects, 'Expected objects'
76       assert objects.is_a?(Hash), 'Expected a hash'
77       assert objects.size == 0, 'Expected no objects in the preloaded hash'
78     end
79   end
80
81   [ [:preload_links_for_objects, 'input not an array'],
82     [:preload_links_for_objects, nil],
83     [:links_for_object, nil],
84     [:preload_collections_for_objects, 'input not an array'],
85     [:preload_collections_for_objects, nil],
86     [:collections_for_object, nil],
87     [:preload_log_collections_for_objects, 'input not an array'],
88     [:preload_log_collections_for_objects, nil],
89     [:log_collections_for_object, nil],
90     [:preload_objects_for_dataclass, 'input not an array'],
91     [:preload_objects_for_dataclass, nil],
92     [:object_for_dataclass, 'some_dataclass', nil],
93     [:object_for_dataclass, nil, 'some_uuid'],
94     [:preload_for_pdhs, 'input not an array'],
95     [:preload_for_pdhs, nil],
96   ].each do |input|
97     test "preload data for wrong type input #{input}" do
98       use_token :active
99
100       ac = ApplicationController.new
101
102       if input[0] == :object_for_dataclass
103         assert_raise ArgumentError do
104           ac.send input[0], input[1], input[2]
105         end
106       else
107         assert_raise ArgumentError do
108           ac.send input[0], input[1]
109         end
110       end
111     end
112   end
113
114   [ [:links_for_object, 'no-such-uuid' ],
115     [:collections_for_object, 'no-such-uuid' ],
116     [:log_collections_for_object, 'no-such-uuid' ],
117     [:object_for_dataclass, 'no-such-uuid' ],
118     [:collection_for_pdh, 'no-such-pdh' ],
119   ].each do |input|
120     test "get data for no such uuid #{input}" do
121       use_token :active
122
123       ac = ApplicationController.new
124
125       if input[0] == :object_for_dataclass
126         object = ac.send input[0], @user_dataclass, input[1]
127         assert_not object, 'Expected no object'
128       else
129         objects = ac.send input[0], input[1]
130         assert objects, 'Expected objects'
131         assert objects.is_a?(Array), 'Expected a array'
132         assert_empty objects
133       end
134     end
135   end
136
137   test "get 10 objects of data class user" do
138     use_token :active
139
140     ac = ApplicationController.new
141
142     objects = ac.send :get_n_objects_of_class, @user_dataclass, 10
143
144     assert objects, 'Expected objects'
145     assert objects.is_a?(ArvadosResourceList), 'Expected an ArvadosResourceList'
146
147     first_object = objects.first
148     assert first_object, 'Expected at least one object'
149     assert_equal 'User', first_object.class.name, 'Expected user object'
150
151     # invoke it again. this time, the preloaded info will be returned
152     objects = ac.send :get_n_objects_of_class, @user_dataclass, 10
153     assert objects, 'Expected objects'
154     assert_equal 'User', objects.first.class.name, 'Expected user object'
155   end
156
157   [ ['User', 10],
158     [nil, 10],
159     [@user_dataclass, 0],
160     [@user_dataclass, -1],
161     [@user_dataclass, nil] ].each do |input|
162     test "get_n_objects for incorrect input #{input}" do
163       use_token :active
164
165       ac = ApplicationController.new
166
167       assert_raise ArgumentError do
168         ac.send :get_n_objects_of_class, input[0], input[1]
169       end
170     end
171   end
172
173   test "collections for object" do
174     use_token :active
175
176     ac = ApplicationController.new
177
178     uuid = api_fixture('collections')['foo_file']['uuid']
179
180     collections = ac.send :collections_for_object, uuid
181
182     assert collections, 'Expected collections'
183     assert collections.is_a?(Array), 'Expected an array'
184     assert collections.size == 1, 'Expected one collection object'
185     assert_equal collections[0][:uuid], uuid, 'Expected uuid not found in collections'
186   end
187
188   test "preload collections for given uuids" do
189     use_token :active
190
191     ac = ApplicationController.new
192
193     uuid1 = api_fixture('collections')['foo_file']['uuid']
194     uuid2 = api_fixture('collections')['bar_file']['uuid']
195
196     uuids = [uuid1, uuid2]
197     collections = ac.send :preload_collections_for_objects, uuids
198
199     assert collections, 'Expected collection'
200     assert collections.is_a?(Hash), 'Expected a hash'
201     assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
202     assert collections[uuid1], 'Expected collections for the passed in uuid'
203     assert_equal collections[uuid1].size, 1, 'Expected one collection for the passed in uuid'
204     assert collections[uuid2], 'Expected collections for the passed in uuid'
205     assert_equal collections[uuid2].size, 1, 'Expected one collection for the passed in uuid'
206
207     # invoke again for this same input. this time, the preloaded data will be returned
208     collections = ac.send :preload_collections_for_objects, uuids
209     assert collections, 'Expected collection'
210     assert collections.is_a?(Hash), 'Expected a hash'
211     assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
212     assert collections[uuid1], 'Expected collections for the passed in uuid'
213   end
214
215   test "log collections for object" do
216     use_token :active
217
218     ac = ApplicationController.new
219
220     uuid = api_fixture('logs')['system_adds_foo_file']['object_uuid']
221
222     collections = ac.send :log_collections_for_object, uuid
223
224     assert collections, 'Expected collections'
225     assert collections.is_a?(Array), 'Expected an array'
226     assert collections.size == 1, 'Expected one collection object'
227     assert_equal collections[0][:uuid], uuid, 'Expected uuid not found in collections'
228   end
229
230   test "preload log collections for given uuids" do
231     use_token :active
232
233     ac = ApplicationController.new
234
235     uuid1 = api_fixture('logs')['system_adds_foo_file']['object_uuid']
236     uuid2 = api_fixture('collections')['bar_file']['uuid']
237
238     uuids = [uuid1, uuid2]
239     collections = ac.send :preload_log_collections_for_objects, uuids
240
241     assert collections, 'Expected collection'
242     assert collections.is_a?(Hash), 'Expected a hash'
243     assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
244     assert collections[uuid1], 'Expected collections for the passed in uuid'
245     assert_equal collections[uuid1].size, 1, 'Expected one collection for the passed in uuid'
246     assert collections[uuid2], 'Expected collections for the passed in uuid'
247     assert_equal collections[uuid2].size, 1, 'Expected one collection for the passed in uuid'
248
249     # invoke again for this same input. this time, the preloaded data will be returned
250     collections = ac.send :preload_log_collections_for_objects, uuids
251     assert collections, 'Expected collection'
252     assert collections.is_a?(Hash), 'Expected a hash'
253     assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
254     assert collections[uuid1], 'Expected collections for the passed in uuid'
255   end
256
257   test "object for dataclass" do
258     use_token :active
259
260     ac = ApplicationController.new
261
262     dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('jobs')['running']['uuid'])
263     uuid = api_fixture('jobs')['running']['uuid']
264
265     obj = ac.send :object_for_dataclass, dataclass, uuid
266
267     assert obj, 'Expected object'
268     assert 'Job', obj.class
269     assert_equal uuid, obj['uuid'], 'Expected uuid not found'
270     assert_equal api_fixture('jobs')['running']['script_version'], obj['script_version'],
271       'Expected script_version not found'
272   end
273
274   test "preload objects for dataclass" do
275     use_token :active
276
277     ac = ApplicationController.new
278
279     dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('jobs')['running']['uuid'])
280
281     uuid1 = api_fixture('jobs')['running']['uuid']
282     uuid2 = api_fixture('jobs')['running_cancelled']['uuid']
283
284     uuids = [uuid1, uuid2]
285     users = ac.send :preload_objects_for_dataclass, dataclass, uuids
286
287     assert users, 'Expected objects'
288     assert users.is_a?(Hash), 'Expected a hash'
289
290     assert users.size == 2, 'Expected two objects in the preloaded hash'
291     assert users[uuid1], 'Expected user object for the passed in uuid'
292     assert users[uuid2], 'Expected user object for the passed in uuid'
293
294     # invoke again for this same input. this time, the preloaded data will be returned
295     users = ac.send :preload_objects_for_dataclass, dataclass, uuids
296     assert users, 'Expected objects'
297     assert users.is_a?(Hash), 'Expected a hash'
298     assert users.size == 2, 'Expected two objects in the preloaded hash'
299
300     # invoke again for this with one more uuid
301     uuids << api_fixture('jobs')['foobar']['uuid']
302     users = ac.send :preload_objects_for_dataclass, dataclass, uuids
303     assert users, 'Expected objects'
304     assert users.is_a?(Hash), 'Expected a hash'
305     assert users.size == 3, 'Expected two objects in the preloaded hash'
306   end
307
308   test "preload one collection each for given portable_data_hash list" do
309     use_token :active
310
311     ac = ApplicationController.new
312
313     pdh1 = api_fixture('collections')['foo_file']['portable_data_hash']
314     pdh2 = api_fixture('collections')['bar_file']['portable_data_hash']
315
316     pdhs = [pdh1, pdh2]
317     collections = ac.send :preload_for_pdhs, pdhs
318
319     assert collections, 'Expected collections map'
320     assert collections.is_a?(Hash), 'Expected a hash'
321     # Each pdh has more than one collection; however, we should get only one for each
322     assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
323     assert collections[pdh1], 'Expected collections for the passed in pdh #{pdh1}'
324     assert_equal collections[pdh1].size, 1, 'Expected one collection for the passed in pdh #{pdh1}'
325     assert collections[pdh2], 'Expected collections for the passed in pdh #{pdh2}'
326     assert_equal collections[pdh2].size, 1, 'Expected one collection for the passed in pdh #{pdh2}'
327   end
328
329   test "requesting a nonexistent object returns 404" do
330     # We're really testing ApplicationController's find_object_by_uuid.
331     # It's easiest to do that by instantiating a concrete controller.
332     @controller = NodesController.new
333     get(:show, {id: "zzzzz-zzzzz-zzzzzzzzzzzzzzz"}, session_for(:admin))
334     assert_response 404
335   end
336
337   test "requesting to the API server includes client_session_id param" do
338     got_query = nil
339     stub_api_calls
340     stub_api_client.stubs(:post).with do |url, query, opts={}|
341       got_query = query
342       true
343     end.returns fake_api_response('{}', 200, {})
344
345     Rails.configuration.anonymous_user_token =
346       api_fixture("api_client_authorizations", "anonymous", "api_token")
347     @controller = ProjectsController.new
348     test_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
349     get(:show, {id: test_uuid})
350
351     assert_includes got_query, 'current_request_id'
352     assert_match /\d{10}-\d{9}/, got_query['current_request_id']
353   end
354
355   test "current_request_id is nil after a request" do
356     @controller = NodesController.new
357     get(:index, {}, session_for(:active))
358     assert_nil Thread.current[:current_request_id]
359   end
360
361   [".navbar .login-menu a",
362    ".navbar .login-menu .dropdown-menu a"
363   ].each do |css_selector|
364     test "login link at #{css_selector.inspect} includes return_to param" do
365       # Without an anonymous token, we're immediately redirected to login.
366       Rails.configuration.anonymous_user_token =
367         api_fixture("api_client_authorizations", "anonymous", "api_token")
368       @controller = ProjectsController.new
369       test_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
370       get(:show, {id: test_uuid})
371       login_link = css_select(css_selector).first
372       assert_not_nil(login_link, "failed to select login link")
373       login_href = URI.unescape(login_link.attributes["href"])
374       # The parameter needs to include the full URL to work.
375       assert_includes(login_href, "://")
376       assert_match(/[\?&]return_to=[^&]*\/projects\/#{test_uuid}(&|$)/,
377                    login_href)
378     end
379   end
380
381   test "Workbench returns 4xx when API server is unreachable" do
382     # We're really testing ApplicationController's render_exception.
383     # Our primary concern is that it doesn't raise an error and
384     # return 500.
385     orig_api_server = Rails.configuration.arvados_v1_base
386     begin
387       # The URL should look valid in all respects, and avoid talking over a
388       # network.  100::/64 is the IPv6 discard prefix, so it's perfect.
389       Rails.configuration.arvados_v1_base = "https://[100::f]:1/"
390       @controller = NodesController.new
391       get(:index, {}, session_for(:active))
392       assert_includes(405..422, @response.code.to_i,
393                       "bad response code when API server is unreachable")
394     ensure
395       Rails.configuration.arvados_v1_base = orig_api_server
396     end
397   end
398
399   [
400     [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project']],
401     [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project'], false],
402     [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project']],
403     [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project'], false],
404     [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project']],
405     [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project'], false],
406     [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project']],
407     [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project'], false],
408     [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project']],
409     [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project'], false],
410   ].each do |controller, fixture, anon_config=true|
411     test "#{controller} show method with anonymous config #{anon_config ? '' : 'not '}enabled" do
412       if anon_config
413         Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
414       else
415         Rails.configuration.anonymous_user_token = false
416       end
417
418       @controller = controller
419
420       get(:show, {id: fixture['uuid']})
421
422       if anon_config
423         assert_response 200
424         if controller.class == JobsController
425           assert_includes @response.inspect, fixture['script']
426         else
427           assert_includes @response.inspect, fixture['name']
428         end
429       else
430         assert_response :redirect
431         assert_match /\/users\/welcome/, @response.redirect_url
432       end
433     end
434   end
435
436   [
437     true,
438     false,
439   ].each do |config|
440     test "invoke show with include_accept_encoding_header config #{config}" do
441       Rails.configuration.include_accept_encoding_header_in_api_requests = config
442
443       @controller = CollectionsController.new
444       get(:show, {id: api_fixture('collections')['foo_file']['uuid']}, session_for(:admin))
445
446       assert_equal([['.', 'foo', 3]], assigns(:object).files)
447     end
448   end
449
450   test 'Edit name and verify that a duplicate is not created' do
451     @controller = ProjectsController.new
452     project = api_fixture("groups")["aproject"]
453     post :update, {
454       id: project["uuid"],
455       project: {
456         name: 'test name'
457       },
458       format: :json
459     }, session_for(:active)
460     assert_includes @response.body, 'test name'
461     updated = assigns(:object)
462     assert_equal updated.uuid, project["uuid"]
463     assert_equal 'test name', updated.name
464   end
465
466   [
467     [VirtualMachinesController.new, 'hostname', false],
468     [UsersController.new, 'first_name', true],
469   ].each do |controller, expect_str, expect_home_link|
470     test "access #{controller.controller_name} index as admin and verify Home link is#{' not' if !expect_home_link} shown" do
471       @controller = controller
472
473       get :index, {}, session_for(:admin)
474
475       assert_response 200
476       assert_includes @response.body, expect_str
477
478       home_link = "/projects/#{api_fixture('users')['active']['uuid']}"
479
480       if expect_home_link
481         refute_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
482       else
483         assert_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
484       end
485     end
486   end
487
488   [
489     [VirtualMachinesController.new, 'hostname', true],
490     [UsersController.new, 'first_name', false],
491   ].each do |controller, expect_str, expect_delete_link|
492     test "access #{controller.controller_name} index as admin and verify Delete option is#{' not' if !expect_delete_link} shown" do
493       @controller = controller
494
495       get :index, {}, session_for(:admin)
496
497       assert_response 200
498       assert_includes @response.body, expect_str
499       if expect_delete_link
500         refute_empty css_select('[data-method=delete]')
501       else
502         assert_empty css_select('[data-method=delete]')
503       end
504     end
505   end
506 end