10029: Added test to check for client_session_id parameter existence and format.
[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     use_token :active do
339       fixture = api_fixture("collections")["foo_collection_in_aproject"]
340       c = Collection.find(fixture['uuid'])
341
342       got_query = nil
343       stub_api_calls
344       stub_api_client.expects(:post).with do |url, query, opts={}|
345         got_query = query
346         true
347       end.returns fake_api_response('{}', 200, {})
348       c.name = "name change for testing"
349       c.save
350
351       assert_includes got_query, 'client_session_id'
352       assert_match /\d{10}-\d{9}/, got_query['client_session_id']
353     end
354   end
355
356   [".navbar .login-menu a",
357    ".navbar .login-menu .dropdown-menu a"
358   ].each do |css_selector|
359     test "login link at #{css_selector.inspect} includes return_to param" do
360       # Without an anonymous token, we're immediately redirected to login.
361       Rails.configuration.anonymous_user_token =
362         api_fixture("api_client_authorizations", "anonymous", "api_token")
363       @controller = ProjectsController.new
364       test_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
365       get(:show, {id: test_uuid})
366       login_link = css_select(css_selector).first
367       assert_not_nil(login_link, "failed to select login link")
368       login_href = URI.unescape(login_link.attributes["href"])
369       # The parameter needs to include the full URL to work.
370       assert_includes(login_href, "://")
371       assert_match(/[\?&]return_to=[^&]*\/projects\/#{test_uuid}(&|$)/,
372                    login_href)
373     end
374   end
375
376   test "Workbench returns 4xx when API server is unreachable" do
377     # We're really testing ApplicationController's render_exception.
378     # Our primary concern is that it doesn't raise an error and
379     # return 500.
380     orig_api_server = Rails.configuration.arvados_v1_base
381     begin
382       # The URL should look valid in all respects, and avoid talking over a
383       # network.  100::/64 is the IPv6 discard prefix, so it's perfect.
384       Rails.configuration.arvados_v1_base = "https://[100::f]:1/"
385       @controller = NodesController.new
386       get(:index, {}, session_for(:active))
387       assert_includes(405..422, @response.code.to_i,
388                       "bad response code when API server is unreachable")
389     ensure
390       Rails.configuration.arvados_v1_base = orig_api_server
391     end
392   end
393
394   [
395     [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project']],
396     [CollectionsController.new, api_fixture('collections')['user_agreement_in_anonymously_accessible_project'], false],
397     [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project']],
398     [JobsController.new, api_fixture('jobs')['running_job_in_publicly_accessible_project'], false],
399     [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project']],
400     [PipelineInstancesController.new, api_fixture('pipeline_instances')['pipeline_in_publicly_accessible_project'], false],
401     [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project']],
402     [PipelineTemplatesController.new, api_fixture('pipeline_templates')['pipeline_template_in_publicly_accessible_project'], false],
403     [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project']],
404     [ProjectsController.new, api_fixture('groups')['anonymously_accessible_project'], false],
405   ].each do |controller, fixture, anon_config=true|
406     test "#{controller} show method with anonymous config enabled" do
407       if anon_config
408         Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
409       else
410         Rails.configuration.anonymous_user_token = false
411       end
412
413       @controller = controller
414
415       get(:show, {id: fixture['uuid']})
416
417       if anon_config
418         assert_response 200
419         if controller.class == JobsController
420           assert_includes @response.inspect, fixture['script']
421         else
422           assert_includes @response.inspect, fixture['name']
423         end
424       else
425         assert_response :redirect
426         assert_match /\/users\/welcome/, @response.redirect_url
427       end
428     end
429   end
430
431   [
432     true,
433     false,
434   ].each do |config|
435     test "invoke show with include_accept_encoding_header config #{config}" do
436       Rails.configuration.include_accept_encoding_header_in_api_requests = config
437
438       @controller = CollectionsController.new
439       get(:show, {id: api_fixture('collections')['foo_file']['uuid']}, session_for(:admin))
440
441       assert_equal([['.', 'foo', 3]], assigns(:object).files)
442     end
443   end
444
445   test 'Edit name and verify that a duplicate is not created' do
446     @controller = ProjectsController.new
447     project = api_fixture("groups")["aproject"]
448     post :update, {
449       id: project["uuid"],
450       project: {
451         name: 'test name'
452       },
453       format: :json
454     }, session_for(:active)
455     assert_includes @response.body, 'test name'
456     updated = assigns(:object)
457     assert_equal updated.uuid, project["uuid"]
458     assert_equal 'test name', updated.name
459   end
460
461   [
462     [VirtualMachinesController.new, 'hostname', false],
463     [UsersController.new, 'first_name', true],
464   ].each do |controller, expect_str, expect_home_link|
465     test "access #{controller.controller_name} index as admin and verify Home link is#{' not' if !expect_home_link} shown" do
466       @controller = controller
467
468       get :index, {}, session_for(:admin)
469
470       assert_response 200
471       assert_includes @response.body, expect_str
472
473       home_link = "/projects/#{api_fixture('users')['active']['uuid']}"
474
475       if expect_home_link
476         refute_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
477       else
478         assert_empty css_select("[href=\"/projects/#{api_fixture('users')['active']['uuid']}\"]")
479       end
480     end
481   end
482
483   [
484     [VirtualMachinesController.new, 'hostname', true],
485     [UsersController.new, 'first_name', false],
486   ].each do |controller, expect_str, expect_delete_link|
487     test "access #{controller.controller_name} index as admin and verify Delete option is#{' not' if !expect_delete_link} shown" do
488       @controller = controller
489
490       get :index, {}, session_for(:admin)
491
492       assert_response 200
493       assert_includes @response.body, expect_str
494       if expect_delete_link
495         refute_empty css_select('[data-method=delete]')
496       else
497         assert_empty css_select('[data-method=delete]')
498       end
499     end
500   end
501 end