1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
7 class ApplicationControllerTest < ActionController::TestCase
8 # These tests don't do state-changing API calls. Save some time by
9 # skipping the database reset.
10 reset_api_fixtures :after_each_test, false
11 reset_api_fixtures :after_suite, true
14 @user_dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('users')['active']['uuid'])
17 test "links for object" do
20 ac = ApplicationController.new
22 link_head_uuid = api_fixture('links')['foo_file_readable_by_active']['head_uuid']
24 links = ac.send :links_for_object, link_head_uuid
26 assert links, 'Expected links'
27 assert links.is_a?(Array), 'Expected an array'
28 assert links.size > 0, 'Expected at least one link'
29 assert links[0][:uuid], 'Expected uuid for the head_link'
32 test "preload links for objects and uuids" do
35 ac = ApplicationController.new
37 link1_head_uuid = api_fixture('links')['foo_file_readable_by_active']['head_uuid']
38 link2_uuid = api_fixture('links')['bar_file_readable_by_active']['uuid']
39 link3_head_uuid = api_fixture('links')['bar_file_readable_by_active']['head_uuid']
41 link2_object = User.find(api_fixture('users')['active']['uuid'])
42 link2_object_uuid = link2_object['uuid']
44 uuids = [link1_head_uuid, link2_object, link3_head_uuid]
45 links = ac.send :preload_links_for_objects, uuids
47 assert links, 'Expected links'
48 assert links.is_a?(Hash), 'Expected a hash'
49 assert links.size == 3, 'Expected two objects in the preloaded links hash'
50 assert links[link1_head_uuid], 'Expected links for the passed in link head_uuid'
51 assert links[link2_object_uuid], 'Expected links for the passed in object uuid'
52 assert links[link3_head_uuid], 'Expected links for the passed in link head_uuid'
54 # invoke again for this same input. this time, the preloaded data will be returned
55 links = ac.send :preload_links_for_objects, uuids
56 assert links, 'Expected links'
57 assert links.is_a?(Hash), 'Expected a hash'
58 assert links.size == 3, 'Expected two objects in the preloaded links hash'
59 assert links[link1_head_uuid], 'Expected links for the passed in link head_uuid'
62 [ [:preload_links_for_objects, [] ],
63 [:preload_collections_for_objects, [] ],
64 [:preload_log_collections_for_objects, [] ],
65 [:preload_objects_for_dataclass, [] ],
66 [:preload_for_pdhs, [] ],
68 test "preload data for empty array input #{input}" do
71 ac = ApplicationController.new
73 if input[0] == :preload_objects_for_dataclass
74 objects = ac.send input[0], @user_dataclass, input[1]
76 objects = ac.send input[0], input[1]
79 assert objects, 'Expected objects'
80 assert objects.is_a?(Hash), 'Expected a hash'
81 assert objects.size == 0, 'Expected no objects in the preloaded hash'
85 [ [:preload_links_for_objects, 'input not an array'],
86 [:preload_links_for_objects, nil],
87 [:links_for_object, nil],
88 [:preload_collections_for_objects, 'input not an array'],
89 [:preload_collections_for_objects, nil],
90 [:collections_for_object, nil],
91 [:preload_log_collections_for_objects, 'input not an array'],
92 [:preload_log_collections_for_objects, nil],
93 [:log_collections_for_object, nil],
94 [:preload_objects_for_dataclass, 'input not an array'],
95 [:preload_objects_for_dataclass, nil],
96 [:object_for_dataclass, 'some_dataclass', nil],
97 [:object_for_dataclass, nil, 'some_uuid'],
98 [:preload_for_pdhs, 'input not an array'],
99 [:preload_for_pdhs, nil],
101 test "preload data for wrong type input #{input}" do
104 ac = ApplicationController.new
106 if input[0] == :object_for_dataclass
107 assert_raise ArgumentError do
108 ac.send input[0], input[1], input[2]
111 assert_raise ArgumentError do
112 ac.send input[0], input[1]
118 [ [:links_for_object, 'no-such-uuid' ],
119 [:collections_for_object, 'no-such-uuid' ],
120 [:log_collections_for_object, 'no-such-uuid' ],
121 [:object_for_dataclass, 'no-such-uuid' ],
122 [:collection_for_pdh, 'no-such-pdh' ],
124 test "get data for no such uuid #{input}" do
127 ac = ApplicationController.new
129 if input[0] == :object_for_dataclass
130 object = ac.send input[0], @user_dataclass, input[1]
131 assert_not object, 'Expected no object'
133 objects = ac.send input[0], input[1]
134 assert objects, 'Expected objects'
135 assert objects.is_a?(Array), 'Expected a array'
141 test "get 10 objects of data class user" do
144 ac = ApplicationController.new
146 objects = ac.send :get_n_objects_of_class, @user_dataclass, 10
148 assert objects, 'Expected objects'
149 assert objects.is_a?(ArvadosResourceList), 'Expected an ArvadosResourceList'
151 first_object = objects.first
152 assert first_object, 'Expected at least one object'
153 assert_equal 'User', first_object.class.name, 'Expected user object'
155 # invoke it again. this time, the preloaded info will be returned
156 objects = ac.send :get_n_objects_of_class, @user_dataclass, 10
157 assert objects, 'Expected objects'
158 assert_equal 'User', objects.first.class.name, 'Expected user object'
163 [@user_dataclass, 0],
164 [@user_dataclass, -1],
165 [@user_dataclass, nil] ].each do |input|
166 test "get_n_objects for incorrect input #{input}" do
169 ac = ApplicationController.new
171 assert_raise ArgumentError do
172 ac.send :get_n_objects_of_class, input[0], input[1]
177 test "collections for object" do
180 ac = ApplicationController.new
182 uuid = api_fixture('collections')['foo_file']['uuid']
184 collections = ac.send :collections_for_object, uuid
186 assert collections, 'Expected collections'
187 assert collections.is_a?(Array), 'Expected an array'
188 assert collections.size == 1, 'Expected one collection object'
189 assert_equal collections[0][:uuid], uuid, 'Expected uuid not found in collections'
192 test "preload collections for given uuids" do
195 ac = ApplicationController.new
197 uuid1 = api_fixture('collections')['foo_file']['uuid']
198 uuid2 = api_fixture('collections')['bar_file']['uuid']
200 uuids = [uuid1, uuid2]
201 collections = ac.send :preload_collections_for_objects, uuids
203 assert collections, 'Expected collection'
204 assert collections.is_a?(Hash), 'Expected a hash'
205 assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
206 assert collections[uuid1], 'Expected collections for the passed in uuid'
207 assert_equal collections[uuid1].size, 1, 'Expected one collection for the passed in uuid'
208 assert collections[uuid2], 'Expected collections for the passed in uuid'
209 assert_equal collections[uuid2].size, 1, 'Expected one collection for the passed in uuid'
211 # invoke again for this same input. this time, the preloaded data will be returned
212 collections = ac.send :preload_collections_for_objects, uuids
213 assert collections, 'Expected collection'
214 assert collections.is_a?(Hash), 'Expected a hash'
215 assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
216 assert collections[uuid1], 'Expected collections for the passed in uuid'
219 test "log collections for object" do
222 ac = ApplicationController.new
224 uuid = api_fixture('logs')['system_adds_foo_file']['object_uuid']
226 collections = ac.send :log_collections_for_object, uuid
228 assert collections, 'Expected collections'
229 assert collections.is_a?(Array), 'Expected an array'
230 assert collections.size == 1, 'Expected one collection object'
231 assert_equal collections[0][:uuid], uuid, 'Expected uuid not found in collections'
234 test "preload log collections for given uuids" do
237 ac = ApplicationController.new
239 uuid1 = api_fixture('logs')['system_adds_foo_file']['object_uuid']
240 uuid2 = api_fixture('collections')['bar_file']['uuid']
242 uuids = [uuid1, uuid2]
243 collections = ac.send :preload_log_collections_for_objects, uuids
245 assert collections, 'Expected collection'
246 assert collections.is_a?(Hash), 'Expected a hash'
247 assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
248 assert collections[uuid1], 'Expected collections for the passed in uuid'
249 assert_equal collections[uuid1].size, 1, 'Expected one collection for the passed in uuid'
250 assert collections[uuid2], 'Expected collections for the passed in uuid'
251 assert_equal collections[uuid2].size, 1, 'Expected one collection for the passed in uuid'
253 # invoke again for this same input. this time, the preloaded data will be returned
254 collections = ac.send :preload_log_collections_for_objects, uuids
255 assert collections, 'Expected collection'
256 assert collections.is_a?(Hash), 'Expected a hash'
257 assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
258 assert collections[uuid1], 'Expected collections for the passed in uuid'
261 test "object for dataclass" do
264 ac = ApplicationController.new
266 dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('jobs')['running']['uuid'])
267 uuid = api_fixture('jobs')['running']['uuid']
269 obj = ac.send :object_for_dataclass, dataclass, uuid
271 assert obj, 'Expected object'
272 assert 'Job', obj.class
273 assert_equal uuid, obj['uuid'], 'Expected uuid not found'
274 assert_equal api_fixture('jobs')['running']['script_version'], obj['script_version'],
275 'Expected script_version not found'
278 test "preload objects for dataclass" do
281 ac = ApplicationController.new
283 dataclass = ArvadosBase.resource_class_for_uuid(api_fixture('jobs')['running']['uuid'])
285 uuid1 = api_fixture('jobs')['running']['uuid']
286 uuid2 = api_fixture('jobs')['running_cancelled']['uuid']
288 uuids = [uuid1, uuid2]
289 users = ac.send :preload_objects_for_dataclass, dataclass, uuids
291 assert users, 'Expected objects'
292 assert users.is_a?(Hash), 'Expected a hash'
294 assert users.size == 2, 'Expected two objects in the preloaded hash'
295 assert users[uuid1], 'Expected user object for the passed in uuid'
296 assert users[uuid2], 'Expected user object for the passed in uuid'
298 # invoke again for this same input. this time, the preloaded data will be returned
299 users = ac.send :preload_objects_for_dataclass, dataclass, uuids
300 assert users, 'Expected objects'
301 assert users.is_a?(Hash), 'Expected a hash'
302 assert users.size == 2, 'Expected two objects in the preloaded hash'
304 # invoke again for this with one more uuid
305 uuids << api_fixture('jobs')['foobar']['uuid']
306 users = ac.send :preload_objects_for_dataclass, dataclass, uuids
307 assert users, 'Expected objects'
308 assert users.is_a?(Hash), 'Expected a hash'
309 assert users.size == 3, 'Expected two objects in the preloaded hash'
312 test "preload one collection each for given portable_data_hash list" do
315 ac = ApplicationController.new
317 pdh1 = api_fixture('collections')['foo_file']['portable_data_hash']
318 pdh2 = api_fixture('collections')['bar_file']['portable_data_hash']
321 collections = ac.send :preload_for_pdhs, pdhs
323 assert collections, 'Expected collections map'
324 assert collections.is_a?(Hash), 'Expected a hash'
325 # Each pdh has more than one collection; however, we should get only one for each
326 assert collections.size == 2, 'Expected two objects in the preloaded collection hash'
327 assert collections[pdh1], 'Expected collections for the passed in pdh #{pdh1}'
328 assert_equal collections[pdh1].size, 1, "Expected one collection for the passed in pdh #{pdh1}"
329 assert collections[pdh2], 'Expected collections for the passed in pdh #{pdh2}'
330 assert_equal collections[pdh2].size, 1, "Expected one collection for the passed in pdh #{pdh2}"
333 test "requesting a nonexistent object returns 404" do
334 # We're really testing ApplicationController's find_object_by_uuid.
335 # It's easiest to do that by instantiating a concrete controller.
336 @controller = NodesController.new
337 get(:show, params: {id: "zzzzz-zzzzz-zzzzzzzzzzzzzzz"}, session: session_for(:admin))
341 test "requesting to the API server includes X-Request-Id header" do
344 stub_api_client.stubs(:post).with do |url, query, header={}|
347 end.returns fake_api_response('{}', 200, {})
349 Rails.configuration.Users.AnonymousUserToken =
350 api_fixture("api_client_authorizations", "anonymous", "api_token")
351 @controller = ProjectsController.new
352 test_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
353 get(:show, params: {id: test_uuid})
355 assert_not_nil got_header
356 assert_includes got_header, 'X-Request-Id'
357 assert_match /^req-[0-9a-zA-Z]{20}$/, got_header["X-Request-Id"]
360 test "current request_id is nil after a request" do
361 @controller = NodesController.new
362 get(:index, params: {}, session: session_for(:active))
363 assert_nil Thread.current[:request_id]
366 test "X-Request-Id header" do
367 @controller = NodesController.new
368 get(:index, params: {}, session: session_for(:active))
369 assert_match /^req-[0-9a-zA-Z]{20}$/, response.headers['X-Request-Id']
372 [".navbar .login-menu a",
373 ".navbar .login-menu .dropdown-menu a"
374 ].each do |css_selector|
375 test "login link at #{css_selector.inspect} includes return_to param" do
376 # Without an anonymous token, we're immediately redirected to login.
377 Rails.configuration.Users.AnonymousUserToken =
378 api_fixture("api_client_authorizations", "anonymous", "api_token")
379 @controller = ProjectsController.new
380 test_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
381 get(:show, params: {id: test_uuid})
382 login_link = css_select(css_selector).first
383 assert_not_nil(login_link, "failed to select login link")
384 login_href = URI.unescape(login_link.attributes["href"].value)
385 # The parameter needs to include the full URL to work.
386 assert_includes(login_href, "://")
387 assert_match(/[\?&]return_to=[^&]*\/projects\/#{test_uuid}(&|$)/,
392 test "Workbench returns 4xx when API server is unreachable" do
393 # We're really testing ApplicationController's render_exception.
394 # Our primary concern is that it doesn't raise an error and
396 orig_api_server = Rails.configuration.Services.Controller.ExternalURL
398 # The URL should look valid in all respects, and avoid talking over a
399 # network. 100::/64 is the IPv6 discard prefix, so it's perfect.
400 Rails.configuration.Services.Controller.ExternalURL = "https://[100::f]:1/"
401 @controller = NodesController.new
402 get(:index, params: {}, session: session_for(:active))
403 assert_includes(405..422, @response.code.to_i,
404 "bad response code when API server is unreachable")
406 Rails.configuration.Services.Controller.ExternalURL = orig_api_server
411 [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project']],
412 [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project'], false],
413 [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project']],
414 [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project'], false],
415 [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project']],
416 [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project'], false],
417 [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project']],
418 [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project'], false],
419 [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project']],
420 [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project'], false],
421 ].each do |controller, fixture, anon_config=true|
422 test "#{controller} show method with anonymous config #{anon_config ? '' : 'not '}enabled" do
424 Rails.configuration.Users.AnonymousUserToken = api_fixture('api_client_authorizations')['anonymous']['api_token']
426 Rails.configuration.Users.AnonymousUserToken = ""
429 @controller = controller
431 get(:show, params: {id: fixture['uuid']})
435 if controller.class == JobsController
436 assert_includes @response.inspect, fixture['script']
438 assert_includes @response.inspect, fixture['name']
441 assert_response :redirect
442 assert_match /\/users\/welcome/, @response.redirect_url
451 test "invoke show with include_accept_encoding_header config #{config}" do
452 Rails.configuration.APIResponseCompression = config
454 @controller = CollectionsController.new
455 get(:show, params: {id: api_fixture('collections')['foo_file']['uuid']}, session: session_for(:admin))
457 assert_equal([['.', 'foo', 3]], assigns(:object).files)
461 test 'Edit name and verify that a duplicate is not created' do
462 @controller = ProjectsController.new
463 project = api_fixture("groups")["aproject"]
464 post :update, params: {
470 }, session: session_for(:active)
471 assert_includes @response.body, 'test name'
472 updated = assigns(:object)
473 assert_equal updated.uuid, project["uuid"]
474 assert_equal 'test name', updated.name
478 [VirtualMachinesController.new, 'hostname', false],
479 [UsersController.new, 'first_name', true],
480 ].each do |controller, expect_str, expect_home_link|
481 test "access #{controller.controller_name} index as admin and verify Home link is#{' not' if !expect_home_link} shown" do
482 @controller = controller
484 get :index, params: {}, session: session_for(:admin)
487 assert_includes @response.body, expect_str
489 home_link = "/projects/#{api_fixture('users')['active']['uuid']}"
492 refute_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
494 assert_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
500 [VirtualMachinesController.new, 'hostname', true],
501 [UsersController.new, 'first_name', false],
502 ].each do |controller, expect_str, expect_delete_link|
503 test "access #{controller.controller_name} index as admin and verify Delete option is#{' not' if !expect_delete_link} shown" do
504 @controller = controller
506 get :index, params: {}, session: session_for(:admin)
509 assert_includes @response.body, expect_str
510 if expect_delete_link
511 refute_empty css_select('[data-method=delete]')
513 assert_empty css_select('[data-method=delete]')