3153: Add integration tests.
[arvados.git] / services / api / test / functional / arvados / v1 / links_controller_test.rb
1 require 'test_helper'
2
3 class Arvados::V1::LinksControllerTest < ActionController::TestCase
4
5   test "no symbol keys in serialized hash" do
6     link = {
7       properties: {username: 'testusername'},
8       link_class: 'test',
9       name: 'encoding',
10       tail_uuid: users(:admin).uuid,
11       head_uuid: virtual_machines(:testvm).uuid
12     }
13     authorize_with :admin
14     [link, link.to_json].each do |formatted_link|
15       post :create, link: formatted_link
16       assert_response :success
17       assert_not_nil assigns(:object)
18       assert_equal 'testusername', assigns(:object).properties['username']
19       assert_equal false, assigns(:object).properties.has_key?(:username)
20     end
21   end
22
23   %w(created_at modified_at).each do |attr|
24     {nil: nil, bogus: 2.days.ago}.each do |bogustype, bogusvalue|
25       test "cannot set #{bogustype} #{attr} in create" do
26         authorize_with :active
27         post :create, {
28           link: {
29             properties: {},
30             link_class: 'test',
31             name: 'test',
32           }.merge(attr => bogusvalue)
33         }
34         assert_response :success
35         resp = JSON.parse @response.body
36         assert_in_delta Time.now, Time.parse(resp[attr]), 3.0
37       end
38       test "cannot set #{bogustype} #{attr} in update" do
39         really_created_at = links(:test_timestamps).created_at
40         authorize_with :active
41         put :update, {
42           id: links(:test_timestamps).uuid,
43           link: {
44             :properties => {test: 'test'},
45             attr => bogusvalue
46           }
47         }
48         assert_response :success
49         resp = JSON.parse @response.body
50         case attr
51         when 'created_at'
52           assert_in_delta really_created_at, Time.parse(resp[attr]), 0.001
53         else
54           assert_in_delta Time.now, Time.parse(resp[attr]), 3.0
55         end
56       end
57     end
58   end
59
60   test "head must exist" do
61     link = {
62       link_class: 'test',
63       name: 'stuff',
64       tail_uuid: users(:active).uuid,
65       head_uuid: 'zzzzz-tpzed-xyzxyzxerrrorxx'
66     }
67     authorize_with :admin
68     post :create, link: link
69     assert_response 422
70   end
71
72   test "tail must exist" do
73     link = {
74       link_class: 'test',
75       name: 'stuff',
76       head_uuid: users(:active).uuid,
77       tail_uuid: 'zzzzz-tpzed-xyzxyzxerrrorxx'
78     }
79     authorize_with :admin
80     post :create, link: link
81     assert_response 422
82   end
83
84   test "head and tail exist, head_kind and tail_kind are returned" do
85     link = {
86       link_class: 'test',
87       name: 'stuff',
88       head_uuid: users(:active).uuid,
89       tail_uuid: users(:spectator).uuid,
90     }
91     authorize_with :admin
92     post :create, link: link
93     assert_response :success
94     l = JSON.parse(@response.body)
95     assert 'arvados#user', l['head_kind']
96     assert 'arvados#user', l['tail_kind']
97   end
98
99   test "can supply head_kind and tail_kind without error" do
100     link = {
101       link_class: 'test',
102       name: 'stuff',
103       head_uuid: users(:active).uuid,
104       tail_uuid: users(:spectator).uuid,
105       head_kind: "arvados#user",
106       tail_kind: "arvados#user",
107     }
108     authorize_with :admin
109     post :create, link: link
110     assert_response :success
111     l = JSON.parse(@response.body)
112     assert 'arvados#user', l['head_kind']
113     assert 'arvados#user', l['tail_kind']
114   end
115
116   test "tail must be visible by user" do
117     link = {
118       link_class: 'test',
119       name: 'stuff',
120       head_uuid: users(:active).uuid,
121       tail_uuid: virtual_machines(:testvm2).uuid
122     }
123     authorize_with :active
124     post :create, link: link
125     assert_response 422
126   end
127
128   test "filter links with 'is_a' operator" do
129     authorize_with :admin
130     get :index, {
131       filters: [ ['tail_uuid', 'is_a', 'arvados#user'] ]
132     }
133     assert_response :success
134     found = assigns(:objects)
135     assert_not_equal 0, found.count
136     assert_equal found.count, (found.select { |f| f.tail_uuid.match /[a-z0-9]{5}-tpzed-[a-z0-9]{15}/}).count
137   end
138
139   test "filter links with 'is_a' operator with more than one" do
140     authorize_with :admin
141     get :index, {
142       filters: [ ['tail_uuid', 'is_a', ['arvados#user', 'arvados#group'] ] ],
143     }
144     assert_response :success
145     found = assigns(:objects)
146     assert_not_equal 0, found.count
147     assert_equal found.count, (found.select { |f| f.tail_uuid.match /[a-z0-9]{5}-(tpzed|j7d0g)-[a-z0-9]{15}/}).count
148   end
149
150   test "filter links with 'is_a' operator with bogus type" do
151     authorize_with :admin
152     get :index, {
153       filters: [ ['tail_uuid', 'is_a', ['arvados#bogus'] ] ],
154     }
155     assert_response :success
156     found = assigns(:objects)
157     assert_equal 0, found.count
158   end
159
160   test "filter links with 'is_a' operator with collection" do
161     authorize_with :admin
162     get :index, {
163       filters: [ ['head_uuid', 'is_a', ['arvados#collection'] ] ],
164     }
165     assert_response :success
166     found = assigns(:objects)
167     assert_not_equal 0, found.count
168     assert_equal found.count, (found.select { |f| f.head_uuid.match /[a-f0-9]{32}\+\d+/}).count
169   end
170
171   test "test can still use where tail_kind" do
172     authorize_with :admin
173     get :index, {
174       where: { tail_kind: 'arvados#user' }
175     }
176     assert_response :success
177     found = assigns(:objects)
178     assert_not_equal 0, found.count
179     assert_equal found.count, (found.select { |f| f.tail_uuid.match /[a-z0-9]{5}-tpzed-[a-z0-9]{15}/}).count
180   end
181
182   test "test can still use where head_kind" do
183     authorize_with :admin
184     get :index, {
185       where: { head_kind: 'arvados#user' }
186     }
187     assert_response :success
188     found = assigns(:objects)
189     assert_not_equal 0, found.count
190     assert_equal found.count, (found.select { |f| f.head_uuid.match /[a-z0-9]{5}-tpzed-[a-z0-9]{15}/}).count
191   end
192
193   test "test can still use filter tail_kind" do
194     authorize_with :admin
195     get :index, {
196       filters: [ ['tail_kind', '=', 'arvados#user'] ]
197     }
198     assert_response :success
199     found = assigns(:objects)
200     assert_not_equal 0, found.count
201     assert_equal found.count, (found.select { |f| f.tail_uuid.match /[a-z0-9]{5}-tpzed-[a-z0-9]{15}/}).count
202   end
203
204   test "test can still use filter head_kind" do
205     authorize_with :admin
206     get :index, {
207       filters: [ ['head_kind', '=', 'arvados#user'] ]
208     }
209     assert_response :success
210     found = assigns(:objects)
211     assert_not_equal 0, found.count
212     assert_equal found.count, (found.select { |f| f.head_uuid.match /[a-z0-9]{5}-tpzed-[a-z0-9]{15}/}).count
213   end
214
215   test "head_kind matches head_uuid" do
216     link = {
217       link_class: 'test',
218       name: 'stuff',
219       head_uuid: groups(:public).uuid,
220       head_kind: "arvados#user",
221       tail_uuid: users(:spectator).uuid,
222       tail_kind: "arvados#user",
223     }
224     authorize_with :admin
225     post :create, link: link
226     assert_response 422
227   end
228
229   test "tail_kind matches tail_uuid" do
230     link = {
231       link_class: 'test',
232       name: 'stuff',
233       head_uuid: users(:active).uuid,
234       head_kind: "arvados#user",
235       tail_uuid: groups(:public).uuid,
236       tail_kind: "arvados#user",
237     }
238     authorize_with :admin
239     post :create, link: link
240     assert_response 422
241   end
242
243   test "test with virtual_machine" do
244     link = {
245       tail_kind: "arvados#user",
246       tail_uuid: users(:active).uuid,
247       head_kind: "arvados#virtual_machine",
248       head_uuid: virtual_machines(:testvm).uuid,
249       link_class: "permission",
250       name: "can_login",
251       properties: {username: "repo_and_user_name"}
252     }
253     authorize_with :admin
254     post :create, link: link
255     assert_response 422
256   end
257
258   test "test with virtualMachine" do
259     link = {
260       tail_kind: "arvados#user",
261       tail_uuid: users(:active).uuid,
262       head_kind: "arvados#virtualMachine",
263       head_uuid: virtual_machines(:testvm).uuid,
264       link_class: "permission",
265       name: "can_login",
266       properties: {username: "repo_and_user_name"}
267     }
268     authorize_with :admin
269     post :create, link: link
270     assert_response :success
271   end
272
273   test "refuse duplicate name" do
274     the_name = links(:job_name_in_aproject).name
275     the_project = links(:job_name_in_aproject).tail_uuid
276     authorize_with :active
277     post :create, link: {
278       tail_uuid: the_project,
279       head_uuid: specimens(:owned_by_active_user).uuid,
280       link_class: 'name',
281       name: the_name,
282       properties: {this_s: "a duplicate name"}
283     }
284     assert_response 422
285   end
286
287   test "project owner can show a project permission" do
288     uuid = links(:project_viewer_can_read_project).uuid
289     authorize_with :active
290     get :show, id: uuid
291     assert_response :success
292     assert_equal(uuid, assigns(:object).andand.uuid)
293   end
294
295   test "admin can show a project permission" do
296     uuid = links(:project_viewer_can_read_project).uuid
297     authorize_with :admin
298     get :show, id: uuid
299     assert_response :success
300     assert_equal(uuid, assigns(:object).andand.uuid)
301   end
302
303   test "project viewer can't show others' project permissions" do
304     authorize_with :project_viewer
305     get :show, id: links(:admin_can_write_aproject).uuid
306     assert_response 404
307   end
308
309   test "requesting a nonexistent link returns 404" do
310     authorize_with :active
311     get :show, id: 'zzzzz-zzzzz-zzzzzzzzzzzzzzz'
312     assert_response 404
313   end
314
315   test "project owner can index project permissions" do
316     skip "Test tickles known bug"
317     # readable_by only lets users see permission links that relate to them
318     # directly.  It does not allow users to see permission links for groups
319     # they manage.
320     # We'd like to fix this general issue, but we haven't settled on a general
321     # way to do it that doesn't involve making readable_by ridiculously hairy.
322     # This test demonstrates the desired behavior once we're ready to tackle
323     # it.  In the meantime, clients should use /permissions to get this
324     # information.
325     authorize_with :active
326     get :index, filters: [['link_class', '=', 'permission'],
327                           ['head_uuid', '=', groups(:aproject).uuid]]
328     assert_response :success
329     assert_not_nil assigns(:objects)
330     assert_includes(assigns(:objects).map(&:uuid),
331                     links(:project_viewer_can_read_project).uuid)
332   end
333
334   test "admin can index project permissions" do
335     authorize_with :admin
336     get :index, filters: [['link_class', '=', 'permission'],
337                           ['head_uuid', '=', groups(:aproject).uuid]]
338     assert_response :success
339     assert_not_nil assigns(:objects)
340     assert_includes(assigns(:objects).map(&:uuid),
341                     links(:project_viewer_can_read_project).uuid)
342   end
343
344   test "project viewer can't index others' project permissions" do
345     authorize_with :project_viewer
346     get :index, filters: [['link_class', '=', 'permission'],
347                           ['head_uuid', '=', groups(:aproject).uuid],
348                           ['tail_uuid', '!=', users(:project_viewer).uuid]]
349     assert_response :success
350     assert_not_nil assigns(:objects)
351     assert_empty assigns(:objects)
352   end
353 end