20336: Allow filtering on set of integers.
[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 additional filter matching nothing' do
334     authorize_with :active
335     get :contents, params: {
336       id: groups(:aproject).uuid,
337       filters: [['uuid', 'in', ['foo_not_a_uuid','bar_not_a_uuid']]],
338       format: :json,
339     }
340     assert_response :success
341     assert_equal [], json_response['items']
342     assert_equal 0, json_response['items_available']
343   end
344
345   %w(offset limit).each do |arg|
346     ['foo', '', '1234five', '0x10', '-8'].each do |val|
347       test "Raise error on bogus #{arg} parameter #{val.inspect}" do
348         authorize_with :active
349         get :contents, params: {
350           :id => groups(:aproject).uuid,
351           :format => :json,
352           arg => val,
353         }
354         assert_response 422
355       end
356     end
357   end
358
359   test "Collection contents don't include manifest_text or unsigned_manifest_text" do
360     authorize_with :active
361     get :contents, params: {
362       id: groups(:aproject).uuid,
363       filters: [["uuid", "is_a", "arvados#collection"]],
364       format: :json,
365     }
366     assert_response :success
367     refute(json_response["items"].any? { |c| not c["portable_data_hash"] },
368            "response included an item without a portable data hash")
369     refute(json_response["items"].any? { |c| c.include?("manifest_text") },
370            "response included an item with manifest_text")
371     refute(json_response["items"].any? { |c| c.include?("unsigned_manifest_text") },
372            "response included an item with unsigned_manifest_text")
373   end
374
375   test 'get writable_by list for owned group' do
376     authorize_with :active
377     get :show, params: {
378       id: groups(:aproject).uuid,
379       format: :json
380     }
381     assert_response :success
382     assert_not_nil(json_response['writable_by'],
383                    "Should receive uuid list in 'writable_by' field")
384     assert_includes(json_response['writable_by'], users(:active).uuid,
385                     "owner should be included in writable_by list")
386   end
387
388   test 'no writable_by list for group with read-only access' do
389     authorize_with :rominiadmin
390     get :show, params: {
391       id: groups(:testusergroup_admins).uuid,
392       format: :json
393     }
394     assert_response :success
395     assert_equal([json_response['owner_uuid']],
396                  json_response['writable_by'],
397                  "Should only see owner_uuid in 'writable_by' field")
398   end
399
400   test 'get writable_by list by admin user' do
401     authorize_with :admin
402     get :show, params: {
403       id: groups(:testusergroup_admins).uuid,
404       format: :json
405     }
406     assert_response :success
407     assert_not_nil(json_response['writable_by'],
408                    "Should receive uuid list in 'writable_by' field")
409     assert_includes(json_response['writable_by'],
410                     users(:admin).uuid,
411                     "Current user should be included in 'writable_by' field")
412   end
413
414   test 'creating subproject with duplicate name fails' do
415     authorize_with :active
416     post :create, params: {
417       group: {
418         name: 'A Project',
419         owner_uuid: users(:active).uuid,
420         group_class: 'project',
421       },
422     }
423     assert_response 422
424     response_errors = json_response['errors']
425     assert_not_nil response_errors, 'Expected error in response'
426     assert(response_errors.first.include?('duplicate key'),
427            "Expected 'duplicate key' error in #{response_errors.first}")
428   end
429
430   test 'creating duplicate named subproject succeeds with ensure_unique_name' do
431     authorize_with :active
432     post :create, params: {
433       group: {
434         name: 'A Project',
435         owner_uuid: users(:active).uuid,
436         group_class: 'project',
437       },
438       ensure_unique_name: true
439     }
440     assert_response :success
441     new_project = json_response
442     assert_not_equal(new_project['uuid'],
443                      groups(:aproject).uuid,
444                      "create returned same uuid as existing project")
445     assert_match(/^A Project \(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z\)$/,
446                  new_project['name'])
447   end
448
449   [
450     [['owner_uuid', '!=', 'zzzzz-tpzed-xurymjxw79nv3jz'], 200,
451         'zzzzz-d1hrv-subprojpipeline', 'zzzzz-d1hrv-1xfj6xkicf2muk2'],
452     [["pipeline_instances.state", "not in", ["Complete", "Failed"]], 200,
453         'zzzzz-d1hrv-1xfj6xkicf2muk2', 'zzzzz-d1hrv-i3e77t9z5y8j9cc'],
454     [['container_requests.requesting_container_uuid', '=', nil], 200,
455         'zzzzz-xvhdp-cr4queuedcontnr', 'zzzzz-xvhdp-cr4requestercn2'],
456     [['container_requests.no_such_column', '=', nil], 422],
457     [['container_requests.', '=', nil], 422],
458     [['.requesting_container_uuid', '=', nil], 422],
459     [['no_such_table.uuid', '!=', 'zzzzz-tpzed-xurymjxw79nv3jz'], 422],
460   ].each do |filter, expect_code, expect_uuid, not_expect_uuid|
461     test "get contents with '#{filter}' filter" do
462       authorize_with :active
463       get :contents, params: {filters: [filter], format: :json}
464       assert_response expect_code
465       if expect_code == 200
466         assert_not_empty json_response['items']
467         item_uuids = json_response['items'].collect {|item| item['uuid']}
468         assert_includes(item_uuids, expect_uuid)
469         assert_not_includes(item_uuids, not_expect_uuid)
470       end
471     end
472   end
473
474   test 'get contents with jobs and pipeline instances disabled' do
475     Rails.configuration.API.DisabledAPIs = ConfigLoader.to_OrderedOptions(
476       {'jobs.index'=>{}, 'pipeline_instances.index'=>{}})
477
478     authorize_with :active
479     get :contents, params: {
480       id: groups(:aproject).uuid,
481       format: :json,
482     }
483     check_project_contents_response %w'arvados#pipelineInstance arvados#job'
484   end
485
486   test 'get contents with low max_index_database_read' do
487     # Some result will certainly have at least 12 bytes in a
488     # restricted column
489     Rails.configuration.API.MaxIndexDatabaseRead = 12
490     authorize_with :active
491     get :contents, params: {
492           id: groups(:aproject).uuid,
493           format: :json,
494         }
495     assert_response :success
496     assert_not_empty(json_response['items'])
497     assert_operator(json_response['items'].count,
498                     :<, json_response['items_available'])
499   end
500
501   test 'get contents, recursive=true' do
502     authorize_with :active
503     params = {
504       id: groups(:aproject).uuid,
505       recursive: true,
506       format: :json,
507     }
508     get :contents, params: params
509     owners = json_response['items'].map do |item|
510       item['owner_uuid']
511     end
512     assert_includes(owners, groups(:aproject).uuid)
513     assert_includes(owners, groups(:asubproject).uuid)
514   end
515
516   [false, nil].each do |recursive|
517     test "get contents, recursive=#{recursive.inspect}" do
518       authorize_with :active
519       params = {
520         id: groups(:aproject).uuid,
521         format: :json,
522       }
523       params[:recursive] = false if recursive == false
524       get :contents, params: params
525       owners = json_response['items'].map do |item|
526         item['owner_uuid']
527       end
528       assert_includes(owners, groups(:aproject).uuid)
529       refute_includes(owners, groups(:asubproject).uuid)
530     end
531   end
532
533   test 'get home project contents, recursive=true' do
534     authorize_with :active
535     get :contents, params: {
536           id: users(:active).uuid,
537           recursive: true,
538           format: :json,
539         }
540     owners = json_response['items'].map do |item|
541       item['owner_uuid']
542     end
543     assert_includes(owners, users(:active).uuid)
544     assert_includes(owners, groups(:aproject).uuid)
545     assert_includes(owners, groups(:asubproject).uuid)
546   end
547
548   [:afiltergroup, :private_role].each do |grp|
549     test "delete non-project group #{grp}" do
550       authorize_with :admin
551       assert_not_nil Group.find_by_uuid(groups(grp).uuid)
552       assert !Group.find_by_uuid(groups(grp).uuid).is_trashed
553       post :destroy, params: {
554             id: groups(grp).uuid,
555             format: :json,
556           }
557       assert_response :success
558       # Should not be trashed
559       assert_nil Group.find_by_uuid(groups(grp).uuid)
560     end
561   end
562
563   [
564     [false, :inactive, :private_role, false],
565     [false, :spectator, :private_role, false],
566     [false, :admin, :private_role, true],
567     [true, :inactive, :private_role, false],
568     [true, :spectator, :private_role, true],
569     [true, :admin, :private_role, true],
570     # project (non-role) groups are invisible even when RoleGroupsVisibleToAll is true
571     [true, :inactive, :private, false],
572     [true, :spectator, :private, false],
573     [true, :admin, :private, true],
574   ].each do |visibleToAll, userFixture, groupFixture, visible|
575     test "with RoleGroupsVisibleToAll=#{visibleToAll}, #{groupFixture} group is #{visible ? '' : 'in'}visible to #{userFixture} user" do
576       Rails.configuration.Users.RoleGroupsVisibleToAll = visibleToAll
577       authorize_with userFixture
578       get :show, params: {id: groups(groupFixture).uuid, format: :json}
579       if visible
580         assert_response :success
581       else
582         assert_response 404
583       end
584     end
585   end
586
587   ### trashed project tests ###
588
589   #
590   # The structure is
591   #
592   # trashed_project         (zzzzz-j7d0g-trashedproject1)
593   #   trashed_subproject    (zzzzz-j7d0g-trashedproject2)
594   #   trashed_subproject3   (zzzzz-j7d0g-trashedproject3)
595   #   zzzzz-xvhdp-cr5trashedcontr
596
597   [:active,
598    :admin].each do |auth|
599     # project: to query,    to untrash,    is visible, parent contents listing success
600     [
601      [:trashed_project,     [],                 false, true],
602      [:trashed_project,     [:trashed_project], true,  true],
603      [:trashed_subproject,  [],                 false, false],
604      [:trashed_subproject,  [:trashed_project], true,  true],
605      [:trashed_subproject3, [:trashed_project], false, true],
606      [:trashed_subproject3, [:trashed_subproject3], false, false],
607      [:trashed_subproject3, [:trashed_project, :trashed_subproject3], true, true],
608     ].each do |project, untrash, visible, success|
609
610       test "contents listing #{project} #{untrash} as #{auth}" do
611         authorize_with auth
612         untrash.each do |pr|
613           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
614         end
615         get :contents, params: {
616               id: groups(project).owner_uuid,
617               format: :json
618             }
619         if success
620           assert_response :success
621           item_uuids = json_response['items'].map do |item|
622             item['uuid']
623           end
624           if visible
625             assert_includes(item_uuids, groups(project).uuid)
626           else
627             assert_not_includes(item_uuids, groups(project).uuid)
628           end
629         else
630           assert_response 404
631         end
632       end
633
634       test "contents of #{project} #{untrash} as #{auth}" do
635         authorize_with auth
636         untrash.each do |pr|
637           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
638         end
639         get :contents, params: {
640               id: groups(project).uuid,
641               format: :json
642             }
643         if visible
644           assert_response :success
645         else
646           assert_response 404
647         end
648       end
649
650       test "index #{project} #{untrash} as #{auth}" do
651         authorize_with auth
652         untrash.each do |pr|
653           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
654         end
655         get :index, params: {
656               format: :json,
657             }
658         assert_response :success
659         item_uuids = json_response['items'].map do |item|
660           item['uuid']
661         end
662         if visible
663           assert_includes(item_uuids, groups(project).uuid)
664         else
665           assert_not_includes(item_uuids, groups(project).uuid)
666         end
667       end
668
669       test "show #{project} #{untrash} as #{auth}" do
670         authorize_with auth
671         untrash.each do |pr|
672           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
673         end
674         get :show, params: {
675               id: groups(project).uuid,
676               format: :json
677             }
678         if visible
679           assert_response :success
680         else
681           assert_response 404
682         end
683       end
684
685       test "show include_trash=false #{project} #{untrash} as #{auth}" do
686         authorize_with auth
687         untrash.each do |pr|
688           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
689         end
690         get :show, params: {
691               id: groups(project).uuid,
692               format: :json,
693               include_trash: false
694             }
695         if visible
696           assert_response :success
697         else
698           assert_response 404
699         end
700       end
701
702       test "show include_trash #{project} #{untrash} as #{auth}" do
703         authorize_with auth
704         untrash.each do |pr|
705           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
706         end
707         get :show, params: {
708               id: groups(project).uuid,
709               format: :json,
710               include_trash: true
711             }
712         assert_response :success
713       end
714
715       test "index include_trash #{project} #{untrash} as #{auth}" do
716         authorize_with auth
717         untrash.each do |pr|
718           Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
719         end
720         get :index, params: {
721               format: :json,
722               include_trash: true
723             }
724         assert_response :success
725         item_uuids = json_response['items'].map do |item|
726           item['uuid']
727         end
728         assert_includes(item_uuids, groups(project).uuid)
729       end
730     end
731
732     test "delete project #{auth}" do
733       authorize_with auth
734       [:trashed_project].each do |pr|
735         Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
736       end
737       assert !Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
738       post :destroy, params: {
739             id: groups(:trashed_project).uuid,
740             format: :json,
741           }
742       assert_response :success
743       assert Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
744     end
745
746     test "untrash project #{auth}" do
747       authorize_with auth
748       assert Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
749       post :untrash, params: {
750             id: groups(:trashed_project).uuid,
751             format: :json,
752           }
753       assert_response :success
754       assert !Group.find_by_uuid(groups(:trashed_project).uuid).is_trashed
755     end
756
757     test "untrash project with name conflict #{auth}" do
758       authorize_with auth
759       [:trashed_project].each do |pr|
760         Group.find_by_uuid(groups(pr).uuid).update! is_trashed: false
761       end
762       gc = Group.create!({owner_uuid: "zzzzz-j7d0g-trashedproject1",
763                          name: "trashed subproject 3",
764                          group_class: "project"})
765       post :untrash, params: {
766             id: groups(:trashed_subproject3).uuid,
767             format: :json,
768             ensure_unique_name: true
769            }
770       assert_response :success
771       assert_match /^trashed subproject 3 \(\d{4}-\d\d-\d\d.*?Z\)$/, json_response['name']
772     end
773
774     test "move trashed subproject to new owner #{auth}" do
775       authorize_with auth
776       assert_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
777       put :update, params: {
778             id: groups(:trashed_subproject).uuid,
779             group: {
780               owner_uuid: users(:active).uuid
781             },
782             include_trash: true,
783             format: :json,
784           }
785       assert_response :success
786       assert_not_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
787     end
788   end
789
790   # the group class overrides the destroy method. Make sure that the destroyed
791   # object is returned
792   [
793     {group_class: "project"},
794     {group_class: "role"},
795     {group_class: "filter", properties: {"filters":[]}},
796   ].each do |params|
797     test "destroy group #{params} returns object" do
798       authorize_with :active
799
800       group = Group.create!(params)
801
802       post :destroy, params: {
803             id: group.uuid,
804             format: :json,
805           }
806       assert_response :success
807       assert_not_nil json_response
808       assert_equal group.uuid, json_response["uuid"]
809     end
810   end
811
812   test 'get shared owned by another user' do
813     authorize_with :user_bar_in_sharing_group
814
815     act_as_system_user do
816       Link.create!(
817         tail_uuid: users(:user_bar_in_sharing_group).uuid,
818         link_class: 'permission',
819         name: 'can_read',
820         head_uuid: groups(:project_owned_by_foo).uuid)
821     end
822
823     get :shared, params: {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
824
825     assert_equal 1, json_response['items'].length
826     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
827
828     assert_equal 1, json_response['included'].length
829     assert_equal json_response['included'][0]["uuid"], users(:user_foo_in_sharing_group).uuid
830   end
831
832   test 'get shared, owned by unreadable project' do
833     authorize_with :user_bar_in_sharing_group
834
835     act_as_system_user do
836       Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:aproject).uuid)
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 0, json_response['included'].length
850   end
851
852   test 'get shared, add permission link' do
853     authorize_with :user_bar_in_sharing_group
854
855     act_as_system_user do
856       Link.create!(tail_uuid: groups(:group_for_sharing_tests).uuid,
857                    head_uuid: groups(:project_owned_by_foo).uuid,
858                    link_class: 'permission',
859                    name: 'can_manage')
860     end
861
862     get :shared, params: {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
863
864     assert_equal 1, json_response['items'].length
865     assert_equal groups(:project_owned_by_foo).uuid, json_response['items'][0]["uuid"]
866
867     assert_equal 1, json_response['included'].length
868     assert_equal users(:user_foo_in_sharing_group).uuid, json_response['included'][0]["uuid"]
869   end
870
871   ### contents with exclude_home_project
872
873   test 'contents, exclude home owned by another user' do
874     authorize_with :user_bar_in_sharing_group
875
876     act_as_system_user do
877       Link.create!(
878         tail_uuid: users(:user_bar_in_sharing_group).uuid,
879         link_class: 'permission',
880         name: 'can_read',
881         head_uuid: groups(:project_owned_by_foo).uuid)
882       Link.create!(
883         tail_uuid: users(:user_bar_in_sharing_group).uuid,
884         link_class: 'permission',
885         name: 'can_read',
886         head_uuid: collections(:collection_owned_by_foo).uuid)
887     end
888
889     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
890
891     assert_equal 2, json_response['items'].length
892     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
893     assert_equal json_response['items'][1]["uuid"], collections(:collection_owned_by_foo).uuid
894
895     assert_equal 1, json_response['included'].length
896     assert_equal json_response['included'][0]["uuid"], users(:user_foo_in_sharing_group).uuid
897   end
898
899   test 'contents, exclude home, owned by unreadable project' do
900     authorize_with :user_bar_in_sharing_group
901
902     act_as_system_user do
903       Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:aproject).uuid)
904       Link.create!(
905         tail_uuid: users(:user_bar_in_sharing_group).uuid,
906         link_class: 'permission',
907         name: 'can_read',
908         head_uuid: groups(:project_owned_by_foo).uuid)
909     end
910
911     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
912
913     assert_equal 1, json_response['items'].length
914     assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
915
916     assert_equal 0, json_response['included'].length
917   end
918
919   test 'contents, exclude home, add permission link' do
920     authorize_with :user_bar_in_sharing_group
921
922     act_as_system_user do
923       Link.create!(tail_uuid: groups(:group_for_sharing_tests).uuid,
924                    head_uuid: groups(:project_owned_by_foo).uuid,
925                    link_class: 'permission',
926                    name: 'can_manage')
927     end
928
929     get :contents, params: {:include => "owner_uuid", :exclude_home_project => true}
930
931     assert_equal 1, json_response['items'].length
932     assert_equal groups(:project_owned_by_foo).uuid, json_response['items'][0]["uuid"]
933
934     assert_equal 1, json_response['included'].length
935     assert_equal users(:user_foo_in_sharing_group).uuid, json_response['included'][0]["uuid"]
936   end
937
938   test 'contents, exclude home, with parent specified' do
939     authorize_with :active
940
941     get :contents, params: {id: groups(:aproject).uuid, :include => "owner_uuid", :exclude_home_project => true}
942
943     assert_response 422
944   end
945
946   test "include_trash does not return trash inside frozen project" do
947     authorize_with :active
948     trashtime = Time.now - 1.second
949     outerproj = Group.create!(group_class: 'project')
950     innerproj = Group.create!(group_class: 'project', owner_uuid: outerproj.uuid)
951     innercoll = Collection.create!(name: 'inner-not-trashed', owner_uuid: innerproj.uuid)
952     innertrash = Collection.create!(name: 'inner-trashed', owner_uuid: innerproj.uuid, trash_at: trashtime)
953     innertrashproj = Group.create!(group_class: 'project', name: 'inner-trashed-proj', owner_uuid: innerproj.uuid, trash_at: trashtime)
954     outertrash = Collection.create!(name: 'outer-trashed', owner_uuid: outerproj.uuid, trash_at: trashtime)
955     innerproj.update_attributes!(frozen_by_uuid: users(:active).uuid)
956     get :contents, params: {id: outerproj.uuid, include_trash: true, recursive: true}
957     assert_response :success
958     uuids = json_response['items'].collect { |item| item['uuid'] }
959     assert_includes uuids, outertrash.uuid
960     assert_includes uuids, innerproj.uuid
961     assert_includes uuids, innercoll.uuid
962     refute_includes uuids, innertrash.uuid
963     refute_includes uuids, innertrashproj.uuid
964   end
965 end