20470: Handle nil selection, selecting on writable_by
[arvados.git] / services / api / test / functional / arvados / v1 / groups_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 Arvados::V1::GroupsControllerTest < ActionController::TestCase
8
9   test "attempt to delete group that cannot be seen" do
10     Rails.configuration.Users.RoleGroupsVisibleToAll = false
11     authorize_with :active
12     post :destroy, params: {id: groups(:empty_lonely_group).uuid}
13     assert_response 404
14   end
15
16   test "attempt to delete group without read or write access" do
17     authorize_with :active
18     post :destroy, params: {id: groups(:empty_lonely_group).uuid}
19     assert_response 403
20   end
21
22   test "attempt to delete group without write access" do
23     authorize_with :active
24     post :destroy, params: {id: groups(:all_users).uuid}
25     assert_response 403
26   end
27
28   test "get list of projects" do
29     authorize_with :active
30     get :index, params: {filters: [['group_class', '=', 'project']], format: :json}
31     assert_response :success
32     group_uuids = []
33     json_response['items'].each do |group|
34       assert_equal 'project', group['group_class']
35       group_uuids << group['uuid']
36     end
37     assert_includes group_uuids, groups(:aproject).uuid
38     assert_includes group_uuids, groups(:asubproject).uuid
39     assert_includes group_uuids, groups(:private).uuid
40     assert_not_includes group_uuids, groups(:system_group).uuid
41     assert_not_includes group_uuids, groups(:private_and_can_read_foofile).uuid
42   end
43
44   test "get list of groups that are not projects" do
45     authorize_with :active
46     get :index, params: {filters: [['group_class', '!=', 'project']], format: :json}
47     assert_response :success
48     group_uuids = []
49     json_response['items'].each do |group|
50       assert_not_equal 'project', group['group_class']
51       group_uuids << group['uuid']
52     end
53     assert_not_includes group_uuids, groups(:aproject).uuid
54     assert_not_includes group_uuids, groups(:asubproject).uuid
55   end
56
57   test "get list of groups with bogus group_class" do
58     authorize_with :active
59     get :index, params: {
60       filters: [['group_class', '=', 'nogrouphasthislittleclass']],
61       format: :json,
62     }
63     assert_response :success
64     assert_equal [], json_response['items']
65     assert_equal 0, json_response['items_available']
66   end
67
68   def check_project_contents_response disabled_kinds=[]
69     assert_response :success
70     assert_operator 2, :<=, json_response['items_available']
71     assert_operator 2, :<=, json_response['items'].count
72     kinds = json_response['items'].collect { |i| i['kind'] }.uniq
73     expect_kinds = %w'arvados#group arvados#specimen arvados#pipelineTemplate arvados#job' - disabled_kinds
74     assert_equal expect_kinds, (expect_kinds & kinds)
75
76     json_response['items'].each do |i|
77       if i['kind'] == 'arvados#group'
78         assert(i['group_class'] == 'project',
79                "group#contents returned a non-project group")
80       end
81     end
82
83     disabled_kinds.each do |d|
84       assert_equal true, !kinds.include?(d)
85     end
86   end
87
88   test 'get group-owned objects' do
89     authorize_with :active
90     get :contents, params: {
91       id: groups(:aproject).uuid,
92       format: :json,
93     }
94     check_project_contents_response
95   end
96
97   test "user with project read permission can see project objects" do
98     authorize_with :project_viewer
99     get :contents, params: {
100       id: groups(:aproject).uuid,
101       format: :json,
102     }
103     check_project_contents_response
104   end
105
106   test "list objects across projects" do
107     authorize_with :project_viewer
108     get :contents, params: {
109       format: :json,
110       filters: [['uuid', 'is_a', 'arvados#specimen']]
111     }
112     assert_response :success
113     found_uuids = json_response['items'].collect { |i| i['uuid'] }
114     [[:in_aproject, true],
115      [:in_asubproject, true],
116      [:owned_by_private_group, false]].each do |specimen_fixture, should_find|
117       if should_find
118         assert_includes found_uuids, specimens(specimen_fixture).uuid, "did not find specimen fixture '#{specimen_fixture}'"
119       else
120         refute_includes found_uuids, specimens(specimen_fixture).uuid, "found specimen fixture '#{specimen_fixture}'"
121       end
122     end
123   end
124
125   test "list trashed collections and projects" do
126     authorize_with :active
127     get(:contents, params: {
128           format: :json,
129           include_trash: true,
130           filters: [
131             ['uuid', 'is_a', ['arvados#collection', 'arvados#group']],
132             ['is_trashed', '=', true],
133           ],
134           limit: 10000,
135         })
136     assert_response :success
137     found_uuids = json_response['items'].collect { |i| i['uuid'] }
138     assert_includes found_uuids, groups(:trashed_project).uuid
139     refute_includes found_uuids, groups(:aproject).uuid
140     assert_includes found_uuids, collections(:expired_collection).uuid
141     refute_includes found_uuids, collections(:w_a_z_file).uuid
142   end
143
144   test "list objects in home project" do
145     authorize_with :active
146     get :contents, params: {
147       format: :json,
148       limit: 200,
149       id: users(:active).uuid
150     }
151     assert_response :success
152     found_uuids = json_response['items'].collect { |i| i['uuid'] }
153     assert_includes found_uuids, specimens(:owned_by_active_user).uuid, "specimen did not appear in home project"
154     refute_includes found_uuids, specimens(:in_asubproject).uuid, "specimen appeared unexpectedly in home project"
155   end
156
157   test "list collections in home project" do
158     authorize_with :active
159     get(:contents, params: {
160           format: :json,
161           filters: [
162             ['uuid', 'is_a', 'arvados#collection'],
163           ],
164           limit: 200,
165           id: users(:active).uuid,
166         })
167     assert_response :success
168     found_uuids = json_response['items'].collect { |i| i['uuid'] }
169     assert_includes found_uuids, collections(:collection_owned_by_active).uuid, "collection did not appear in home project"
170     refute_includes found_uuids, collections(:collection_owned_by_active_past_version_1).uuid, "collection appeared unexpectedly in home project"
171   end
172
173   test "list collections in home project, including old versions" do
174     authorize_with :active
175     get(:contents, params: {
176           format: :json,
177           include_old_versions: true,
178           filters: [
179             ['uuid', 'is_a', 'arvados#collection'],
180           ],
181           limit: 200,
182           id: users(:active).uuid,
183         })
184     assert_response :success
185     found_uuids = json_response['items'].collect { |i| i['uuid'] }
186     assert_includes found_uuids, collections(:collection_owned_by_active).uuid, "collection did not appear in home project"
187     assert_includes found_uuids, collections(:collection_owned_by_active_past_version_1).uuid, "old collection version did not appear in home project"
188   end
189
190   test "user with project read permission can see project collections" do
191     authorize_with :project_viewer
192     get :contents, params: {
193       id: groups(:asubproject).uuid,
194       format: :json,
195     }
196     ids = json_response['items'].map { |item| item["uuid"] }
197     assert_includes ids, collections(:baz_file_in_asubproject).uuid
198   end
199
200   [
201     ['collections.name', 'asc', :<=, "name"],
202     ['collections.name', 'desc', :>=, "name"],
203     ['name', 'asc', :<=, "name"],
204     ['name', 'desc', :>=, "name"],
205     ['collections.created_at', 'asc', :<=, "created_at"],
206     ['collections.created_at', 'desc', :>=, "created_at"],
207     ['created_at', 'asc', :<=, "created_at"],
208     ['created_at', 'desc', :>=, "created_at"],
209   ].each do |column, order, operator, field|
210     test "user with project read permission can sort projects on #{column} #{order}" do
211       authorize_with :project_viewer
212       get :contents, params: {
213         id: groups(:asubproject).uuid,
214         format: :json,
215         filters: [['uuid', 'is_a', "arvados#collection"]],
216         order: "#{column} #{order}"
217       }
218       sorted_values = json_response['items'].collect { |item| item[field] }
219       if field == "name"
220         # Here we avoid assuming too much about the database
221         # collation. Both "alice"<"Bob" and "alice">"Bob" can be
222         # correct. Hopefully it _is_ safe to assume that if "a" comes
223         # before "b" in the ascii alphabet, "aX">"bY" is never true for
224         # any strings X and Y.
225         reliably_sortable_names = sorted_values.select do |name|
226           name[0] >= 'a' && name[0] <= 'z'
227         end.uniq do |name|
228           name[0]
229         end
230         # Preserve order of sorted_values. But do not use &=. If
231         # sorted_values has out-of-order duplicates, we want to preserve
232         # them here, so we can detect them and fail the test below.
233         sorted_values.select! do |name|
234           reliably_sortable_names.include? name
235         end
236       end
237       assert_sorted(operator, sorted_values)
238     end
239   end
240
241   def assert_sorted(operator, sorted_items)
242     actually_checked_anything = false
243     previous = nil
244     sorted_items.each do |entry|
245       if !previous.nil?
246         assert_operator(previous, operator, entry,
247                         "Entries sorted incorrectly.")
248         actually_checked_anything = true
249       end
250       previous = entry
251     end
252     assert actually_checked_anything, "Didn't even find two items to compare."
253   end
254
255   # Even though the project_viewer tests go through other controllers,
256   # I'm putting them here so they're easy to find alongside the other
257   # project tests.
258   def check_new_project_link_fails(link_attrs)
259     @controller = Arvados::V1::LinksController.new
260     post :create, params: {
261       link: {
262         link_class: "permission",
263         name: "can_read",
264         head_uuid: groups(:aproject).uuid,
265       }.merge(link_attrs)
266     }
267     assert_includes(403..422, response.status)
268   end
269
270   test "user with project read permission can't add users to it" do
271     authorize_with :project_viewer
272     check_new_project_link_fails(tail_uuid: users(:spectator).uuid)
273   end
274
275   test "user with project read permission can't add items to it" do
276     authorize_with :project_viewer
277     check_new_project_link_fails(tail_uuid: collections(:baz_file).uuid)
278   end
279
280   test "user with project read permission can't rename items in it" do
281     authorize_with :project_viewer
282     @controller = Arvados::V1::LinksController.new
283     post :update, params: {
284       id: jobs(:running).uuid,
285       name: "Denied test name",
286     }
287     assert_includes(403..404, response.status)
288   end
289
290   test "user with project read permission can't remove items from it" do
291     @controller = Arvados::V1::PipelineTemplatesController.new
292     authorize_with :project_viewer
293     post :update, params: {
294       id: pipeline_templates(:two_part).uuid,
295       pipeline_template: {
296         owner_uuid: users(:project_viewer).uuid,
297       }
298     }
299     assert_response 403
300   end
301
302   test "user with project read permission can't delete it" do
303     authorize_with :project_viewer
304     post :destroy, params: {id: groups(:aproject).uuid}
305     assert_response 403
306   end
307
308   test 'get group-owned objects with limit' do
309     authorize_with :active
310     get :contents, params: {
311       id: groups(:aproject).uuid,
312       limit: 1,
313       format: :json,
314     }
315     assert_response :success
316     assert_operator 1, :<, json_response['items_available']
317     assert_equal 1, json_response['items'].count
318   end
319
320   test 'get group-owned objects with limit and offset' do
321     authorize_with :active
322     get :contents, params: {
323       id: groups(:aproject).uuid,
324       limit: 1,
325       offset: 12345,
326       format: :json,
327     }
328     assert_response :success
329     assert_operator 1, :<, json_response['items_available']
330     assert_equal 0, json_response['items'].count
331   end
332
333   test 'get group-owned objects with select' do
334     authorize_with :active
335     get :contents, params: {
336       id: groups(:aproject).uuid,
337       limit: 100,
338       format: :json,
339       select: ["uuid", "collections.name"]
340     }
341     assert_response :success
342     assert_equal 17, json_response['items_available']
343     assert_equal 17, json_response['items'].count
344     json_response['items'].each do |item|
345       # Expect collections to have a name field, other items should not.
346       if item["kind"] == "arvados#collection"
347         assert !item["name"].nil?
348       else
349         assert item["name"].nil?
350       end
351     end
352   end
353
354   test 'get group-owned objects with additional filter matching nothing' do
355     authorize_with :active
356     get :contents, params: {
357       id: groups(:aproject).uuid,
358       filters: [['uuid', 'in', ['foo_not_a_uuid','bar_not_a_uuid']]],
359       format: :json,
360     }
361     assert_response :success
362     assert_equal [], json_response['items']
363     assert_equal 0, json_response['items_available']
364   end
365
366   %w(offset limit).each do |arg|
367     ['foo', '', '1234five', '0x10', '-8'].each do |val|
368       test "Raise error on bogus #{arg} parameter #{val.inspect}" do
369         authorize_with :active
370         get :contents, params: {
371           :id => groups(:aproject).uuid,
372           :format => :json,
373           arg => val,
374         }
375         assert_response 422
376       end
377     end
378   end
379
380   test "Collection contents don't include manifest_text or unsigned_manifest_text" do
381     authorize_with :active
382     get :contents, params: {
383       id: groups(:aproject).uuid,
384       filters: [["uuid", "is_a", "arvados#collection"]],
385       format: :json,
386     }
387     assert_response :success
388     refute(json_response["items"].any? { |c| not c["portable_data_hash"] },
389            "response included an item without a portable data hash")
390     refute(json_response["items"].any? { |c| c.include?("manifest_text") },
391            "response included an item with manifest_text")
392     refute(json_response["items"].any? { |c| c.include?("unsigned_manifest_text") },
393            "response included an item with unsigned_manifest_text")
394   end
395
396   test 'get writable_by list for owned group' do
397     authorize_with :active
398     get :show, params: {
399       id: groups(:aproject).uuid,
400       format: :json
401     }
402     assert_response :success
403     assert_not_nil(json_response['writable_by'],
404                    "Should receive uuid list in 'writable_by' field")
405     assert_includes(json_response['writable_by'], users(:active).uuid,
406                     "owner should be included in writable_by list")
407   end
408
409   test 'no writable_by list for group with read-only access' do
410     authorize_with :rominiadmin
411     get :show, params: {
412       id: groups(:testusergroup_admins).uuid,
413       format: :json
414     }
415     assert_response :success
416     assert_equal([json_response['owner_uuid']],
417                  json_response['writable_by'],
418                  "Should only see owner_uuid in 'writable_by' field")
419   end
420
421   test 'get writable_by list by admin user' do
422     authorize_with :admin
423     get :show, params: {
424       id: groups(:testusergroup_admins).uuid,
425       format: :json
426     }
427     assert_response :success
428     assert_not_nil(json_response['writable_by'],
429                    "Should receive uuid list in 'writable_by' field")
430     assert_includes(json_response['writable_by'],
431                     users(:admin).uuid,
432                     "Current user should be included in 'writable_by' field")
433   end
434
435   test 'creating subproject with duplicate name fails' do
436     authorize_with :active
437     post :create, params: {
438       group: {
439         name: 'A Project',
440         owner_uuid: users(:active).uuid,
441         group_class: 'project',
442       },
443     }
444     assert_response 422
445     response_errors = json_response['errors']
446     assert_not_nil response_errors, 'Expected error in response'
447     assert(response_errors.first.include?('duplicate key'),
448            "Expected 'duplicate key' error in #{response_errors.first}")
449   end
450
451   test 'creating duplicate named subproject succeeds with ensure_unique_name' do
452     authorize_with :active
453     post :create, params: {
454       group: {
455         name: 'A Project',
456         owner_uuid: users(:active).uuid,
457         group_class: 'project',
458       },
459       ensure_unique_name: true
460     }
461     assert_response :success
462     new_project = json_response
463     assert_not_equal(new_project['uuid'],
464                      groups(:aproject).uuid,
465                      "create returned same uuid as existing project")
466     assert_match(/^A Project \(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z\)$/,
467                  new_project['name'])
468   end
469
470   [
471     [['owner_uuid', '!=', 'zzzzz-tpzed-xurymjxw79nv3jz'], 200,
472         'zzzzz-d1hrv-subprojpipeline', 'zzzzz-d1hrv-1xfj6xkicf2muk2'],
473     [["pipeline_instances.state", "not in", ["Complete", "Failed"]], 200,
474         'zzzzz-d1hrv-1xfj6xkicf2muk2', 'zzzzz-d1hrv-i3e77t9z5y8j9cc'],
475     [['container_requests.requesting_container_uuid', '=', nil], 200,
476         'zzzzz-xvhdp-cr4queuedcontnr', 'zzzzz-xvhdp-cr4requestercn2'],
477     [['container_requests.no_such_column', '=', nil], 422],
478     [['container_requests.', '=', nil], 422],
479     [['.requesting_container_uuid', '=', nil], 422],
480     [['no_such_table.uuid', '!=', 'zzzzz-tpzed-xurymjxw79nv3jz'], 422],
481   ].each do |filter, expect_code, expect_uuid, not_expect_uuid|
482     test "get contents with '#{filter}' filter" do
483       authorize_with :active
484       get :contents, params: {filters: [filter], format: :json}
485       assert_response expect_code
486       if expect_code == 200
487         assert_not_empty json_response['items']
488         item_uuids = json_response['items'].collect {|item| item['uuid']}
489         assert_includes(item_uuids, expect_uuid)
490         assert_not_includes(item_uuids, not_expect_uuid)
491       end
492     end
493   end
494
495   test 'get contents with jobs and pipeline instances disabled' do
496     Rails.configuration.API.DisabledAPIs = ConfigLoader.to_OrderedOptions(
497       {'jobs.index'=>{}, 'pipeline_instances.index'=>{}})
498
499     authorize_with :active
500     get :contents, params: {
501       id: groups(:aproject).uuid,
502       format: :json,
503     }
504     check_project_contents_response %w'arvados#pipelineInstance arvados#job'
505   end
506
507   test 'get contents with low max_index_database_read' do
508     # Some result will certainly have at least 12 bytes in a
509     # restricted column
510     Rails.configuration.API.MaxIndexDatabaseRead = 12
511     authorize_with :active
512     get :contents, params: {
513           id: groups(:aproject).uuid,
514           format: :json,
515         }
516     assert_response :success
517     assert_not_empty(json_response['items'])
518     assert_operator(json_response['items'].count,
519                     :<, json_response['items_available'])
520   end
521
522   test 'get contents, recursive=true' do
523     authorize_with :active
524     params = {
525       id: groups(:aproject).uuid,
526       recursive: true,
527       format: :json,
528     }
529     get :contents, params: params
530     owners = json_response['items'].map do |item|
531       item['owner_uuid']
532     end
533     assert_includes(owners, groups(:aproject).uuid)
534     assert_includes(owners, groups(:asubproject).uuid)
535   end
536
537   [false, nil].each do |recursive|
538     test "get contents, recursive=#{recursive.inspect}" do
539       authorize_with :active
540       params = {
541         id: groups(:aproject).uuid,
542         format: :json,
543       }
544       params[:recursive] = false if recursive == false
545       get :contents, params: params
546       owners = json_response['items'].map do |item|
547         item['owner_uuid']
548       end
549       assert_includes(owners, groups(:aproject).uuid)
550       refute_includes(owners, groups(:asubproject).uuid)
551     end
552   end
553
554   test 'get home project contents, recursive=true' do
555     authorize_with :active
556     get :contents, params: {
557           id: users(:active).uuid,
558           recursive: true,
559           format: :json,
560         }
561     owners = json_response['items'].map do |item|
562       item['owner_uuid']
563     end
564     assert_includes(owners, users(:active).uuid)
565     assert_includes(owners, groups(:aproject).uuid)
566     assert_includes(owners, groups(:asubproject).uuid)
567   end
568
569   [:afiltergroup, :private_role].each do |grp|
570     test "delete non-project group #{grp}" do
571       authorize_with :admin
572       assert_not_nil Group.find_by_uuid(groups(grp).uuid)
573       assert !Group.find_by_uuid(groups(grp).uuid).is_trashed
574       post :destroy, params: {
575             id: groups(grp).uuid,
576             format: :json,
577           }
578       assert_response :success
579       # Should not be trashed
580       assert_nil Group.find_by_uuid(groups(grp).uuid)
581     end
582   end
583
584   [
585     [false, :inactive, :private_role, false],
586     [false, :spectator, :private_role, false],
587     [false, :admin, :private_role, true],
588     [true, :inactive, :private_role, false],
589     [true, :spectator, :private_role, true],
590     [true, :admin, :private_role, true],
591     # project (non-role) groups are invisible even when RoleGroupsVisibleToAll is true
592     [true, :inactive, :private, false],
593     [true, :spectator, :private, false],
594     [true, :admin, :private, true],
595   ].each do |visibleToAll, userFixture, groupFixture, visible|
596     test "with RoleGroupsVisibleToAll=#{visibleToAll}, #{groupFixture} group is #{visible ? '' : 'in'}visible to #{userFixture} user" do
597       Rails.configuration.Users.RoleGroupsVisibleToAll = visibleToAll
598       authorize_with userFixture
599       get :show, params: {id: groups(groupFixture).uuid, format: :json}
600       if visible
601         assert_response :success
602       else
603         assert_response 404
604       end
605     end
606   end
607
608   ### trashed project tests ###
609
610   #
611   # The structure is
612   #
613   # trashed_project         (zzzzz-j7d0g-trashedproject1)
614   #   trashed_subproject    (zzzzz-j7d0g-trashedproject2)
615   #   trashed_subproject3   (zzzzz-j7d0g-trashedproject3)
616   #   zzzzz-xvhdp-cr5trashedcontr
617
618   [:active,
619    :admin].each do |auth|
620     # project: to query,    to untrash,    is visible, parent contents listing success
621     [
622      [:trashed_project,     [],                 false, true],
623      [:trashed_project,     [:trashed_project], true,  true],
624      [:trashed_subproject,  [],                 false, false],
625      [:trashed_subproject,  [:trashed_project], true,  true],
626      [:trashed_subproject3, [:trashed_project], false, true],
627      [:trashed_subproject3, [:trashed_subproject3], false, false],
628      [:trashed_subproject3, [:trashed_project, :trashed_subproject3], true, true],
629     ].each do |project, untrash, visible, success|
630
631       test "contents listing #{project} #{untrash} as #{auth}" do
632         authorize_with auth
633         untrash.each do |pr|
634           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
635         end
636         get :contents, params: {
637               id: groups(project).owner_uuid,
638               format: :json
639             }
640         if success
641           assert_response :success
642           item_uuids = json_response['items'].map do |item|
643             item['uuid']
644           end
645           if visible
646             assert_includes(item_uuids, groups(project).uuid)
647           else
648             assert_not_includes(item_uuids, groups(project).uuid)
649           end
650         else
651           assert_response 404
652         end
653       end
654
655       test "contents of #{project} #{untrash} as #{auth}" do
656         authorize_with auth
657         untrash.each do |pr|
658           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
659         end
660         get :contents, params: {
661               id: groups(project).uuid,
662               format: :json
663             }
664         if visible
665           assert_response :success
666         else
667           assert_response 404
668         end
669       end
670
671       test "index #{project} #{untrash} as #{auth}" do
672         authorize_with auth
673         untrash.each do |pr|
674           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
675         end
676         get :index, params: {
677               format: :json,
678             }
679         assert_response :success
680         item_uuids = json_response['items'].map do |item|
681           item['uuid']
682         end
683         if visible
684           assert_includes(item_uuids, groups(project).uuid)
685         else
686           assert_not_includes(item_uuids, groups(project).uuid)
687         end
688       end
689
690       test "show #{project} #{untrash} as #{auth}" do
691         authorize_with auth
692         untrash.each do |pr|
693           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
694         end
695         get :show, params: {
696               id: groups(project).uuid,
697               format: :json
698             }
699         if visible
700           assert_response :success
701         else
702           assert_response 404
703         end
704       end
705
706       test "show include_trash=false #{project} #{untrash} as #{auth}" do
707         authorize_with auth
708         untrash.each do |pr|
709           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
710         end
711         get :show, params: {
712               id: groups(project).uuid,
713               format: :json,
714               include_trash: false
715             }
716         if visible
717           assert_response :success
718         else
719           assert_response 404
720         end
721       end
722
723       test "show include_trash #{project} #{untrash} as #{auth}" do
724         authorize_with auth
725         untrash.each do |pr|
726           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
727         end
728         get :show, params: {
729               id: groups(project).uuid,
730               format: :json,
731               include_trash: true
732             }
733         assert_response :success
734       end
735
736       test "index include_trash #{project} #{untrash} as #{auth}" do
737         authorize_with auth
738         untrash.each do |pr|
739           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
740         end
741         get :index, params: {
742               format: :json,
743               include_trash: true
744             }
745         assert_response :success
746         item_uuids = json_response['items'].map do |item|
747           item['uuid']
748         end
749         assert_includes(item_uuids, groups(project).uuid)
750       end
751     end
752
753     test "delete project #{auth}" do
754       authorize_with auth
755       [:trashed_project].each do |pr|
756         Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
757       end
758       assert !Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
759       post :destroy, params: {
760             id: groups(:trashed_project).uuid,
761             format: :json,
762           }
763       assert_response :success
764       assert Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
765     end
766
767     test "untrash project #{auth}" do
768       authorize_with auth
769       assert Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
770       post :untrash, params: {
771             id: groups(:trashed_project).uuid,
772             format: :json,
773           }
774       assert_response :success
775       assert !Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
776     end
777
778     test "untrash project with name conflict #{auth}" do
779       authorize_with auth
780       [:trashed_project].each do |pr|
781         Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
782       end
783       gc = Group.create!({owner_uuid: "zzzzz-j7d0g-trashedproject1",
784                          name: "trashed subproject 3",
785                          group_class: "project"})
786       post :untrash, params: {
787             id: groups(:trashed_subproject3).uuid,
788             format: :json,
789             ensure_unique_name: true
790            }
791       assert_response :success
792       assert_match /^trashed subproject 3 \(\d{4}-\d\d-\d\d.*?Z\)$/, json_response['name']
793     end
794
795     test "move trashed subproject to new owner #{auth}" do
796       authorize_with auth
797       assert_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
798       put :update, params: {
799             id: groups(:trashed_subproject).uuid,
800             group: {
801               owner_uuid: users(:active).uuid
802             },
803             include_trash: true,
804             format: :json,
805           }
806       assert_response :success
807       assert_not_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
808     end
809   end
810
811   # the group class overrides the destroy method. Make sure that the destroyed
812   # object is returned
813   [
814     {group_class: "project"},
815     {group_class: "role"},
816     {group_class: "filter", properties: {"filters":[]}},
817   ].each do |params|
818     test "destroy group #{params} returns object" do
819       authorize_with :active
820
821       group = Group.create!(params)
822
823       post :destroy, params: {
824             id: group.uuid,
825             format: :json,
826           }
827       assert_response :success
828       assert_not_nil json_response
829       assert_equal group.uuid, json_response["uuid"]
830     end
831   end
832
833   test 'get shared owned by another user' do
834     authorize_with :user_bar_in_sharing_group
835
836     act_as_system_user do
837       Link.create!(
838         tail_uuid: users(:user_bar_in_sharing_group).uuid,
839         link_class: 'permission',
840         name: 'can_read',
841         head_uuid: groups(:project_owned_by_foo).uuid)
842     end
843
844     get :shared, params: {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
845
846     assert_equal 1, json_response['items'].length
847     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
848
849     assert_equal 1, json_response['included'].length
850     assert_equal json_response['included'][0]["uuid"], users(:user_foo_in_sharing_group).uuid
851   end
852
853   test 'get shared, owned by unreadable project' do
854     authorize_with :user_bar_in_sharing_group
855
856     act_as_system_user do
857       Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:aproject).uuid)
858       Link.create!(
859         tail_uuid: users(:user_bar_in_sharing_group).uuid,
860         link_class: 'permission',
861         name: 'can_read',
862         head_uuid: groups(:project_owned_by_foo).uuid)
863     end
864
865     get :shared, params: {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
866
867     assert_equal 1, json_response['items'].length
868     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
869
870     assert_equal 0, json_response['included'].length
871   end
872
873   test 'get shared, add permission link' do
874     authorize_with :user_bar_in_sharing_group
875
876     act_as_system_user do
877       Link.create!(tail_uuid: groups(:group_for_sharing_tests).uuid,
878                    head_uuid: groups(:project_owned_by_foo).uuid,
879                    link_class: 'permission',
880                    name: 'can_manage')
881     end
882
883     get :shared, params: {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
884
885     assert_equal 1, json_response['items'].length
886     assert_equal groups(:project_owned_by_foo).uuid, json_response['items'][0]["uuid"]
887
888     assert_equal 1, json_response['included'].length
889     assert_equal users(:user_foo_in_sharing_group).uuid, json_response['included'][0]["uuid"]
890   end
891
892   ### contents with exclude_home_project
893
894   test 'contents, exclude home owned by another user' do
895     authorize_with :user_bar_in_sharing_group
896
897     act_as_system_user do
898       Link.create!(
899         tail_uuid: users(:user_bar_in_sharing_group).uuid,
900         link_class: 'permission',
901         name: 'can_read',
902         head_uuid: groups(:project_owned_by_foo).uuid)
903       Link.create!(
904         tail_uuid: users(:user_bar_in_sharing_group).uuid,
905         link_class: 'permission',
906         name: 'can_read',
907         head_uuid: collections(:collection_owned_by_foo).uuid)
908     end
909
910     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
911
912     assert_equal 2, json_response['items'].length
913     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
914     assert_equal json_response['items'][1]["uuid"], collections(:collection_owned_by_foo).uuid
915
916     assert_equal 1, json_response['included'].length
917     assert_equal json_response['included'][0]["uuid"], users(:user_foo_in_sharing_group).uuid
918   end
919
920   test 'contents, exclude home, owned by unreadable project' do
921     authorize_with :user_bar_in_sharing_group
922
923     act_as_system_user do
924       Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:aproject).uuid)
925       Link.create!(
926         tail_uuid: users(:user_bar_in_sharing_group).uuid,
927         link_class: 'permission',
928         name: 'can_read',
929         head_uuid: groups(:project_owned_by_foo).uuid)
930     end
931
932     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
933
934     assert_equal 1, json_response['items'].length
935     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
936
937     assert_equal 0, json_response['included'].length
938   end
939
940   test 'contents, exclude home, add permission link' do
941     authorize_with :user_bar_in_sharing_group
942
943     act_as_system_user do
944       Link.create!(tail_uuid: groups(:group_for_sharing_tests).uuid,
945                    head_uuid: groups(:project_owned_by_foo).uuid,
946                    link_class: 'permission',
947                    name: 'can_manage')
948     end
949
950     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
951
952     assert_equal 1, json_response['items'].length
953     assert_equal groups(:project_owned_by_foo).uuid, json_response['items'][0]["uuid"]
954
955     assert_equal 1, json_response['included'].length
956     assert_equal users(:user_foo_in_sharing_group).uuid, json_response['included'][0]["uuid"]
957   end
958
959   test 'contents, exclude home, with parent specified' do
960     authorize_with :active
961
962     get :contents, params: {id: groups(:aproject).uuid, :include => "owner_uuid", :exclude_home_project => true}
963
964     assert_response 422
965   end
966
967   test "include_trash does not return trash inside frozen project" do
968     authorize_with :active
969     trashtime = Time.now - 1.second
970     outerproj = Group.create!(group_class: 'project')
971     innerproj = Group.create!(group_class: 'project', owner_uuid: outerproj.uuid)
972     innercoll = Collection.create!(name: 'inner-not-trashed', owner_uuid: innerproj.uuid)
973     innertrash = Collection.create!(name: 'inner-trashed', owner_uuid: innerproj.uuid, trash_at: trashtime)
974     innertrashproj = Group.create!(group_class: 'project', name: 'inner-trashed-proj', owner_uuid: innerproj.uuid, trash_at: trashtime)
975     outertrash = Collection.create!(name: 'outer-trashed', owner_uuid: outerproj.uuid, trash_at: trashtime)
976     innerproj.update_attributes!(frozen_by_uuid: users(:active).uuid)
977     get :contents, params: {id: outerproj.uuid, include_trash: true, recursive: true}
978     assert_response :success
979     uuids = json_response['items'].collect { |item| item['uuid'] }
980     assert_includes uuids, outertrash.uuid
981     assert_includes uuids, innerproj.uuid
982     assert_includes uuids, innercoll.uuid
983     refute_includes uuids, innertrash.uuid
984     refute_includes uuids, innertrashproj.uuid
985   end
986 end