2 require 'helpers/share_object_helper'
4 class ProjectsControllerTest < ActionController::TestCase
5 include ShareObjectHelper
7 test "invited user is asked to sign user agreements on front page" do
8 get :index, {}, session_for(:inactive)
9 assert_response :redirect
10 assert_match(/^#{Regexp.escape(user_agreements_url)}\b/,
11 @response.redirect_url,
12 "Inactive user was not redirected to user_agreements page")
15 test "uninvited user is asked to wait for activation" do
16 get :index, {}, session_for(:inactive_uninvited)
17 assert_response :redirect
18 assert_match(/^#{Regexp.escape(inactive_users_url)}\b/,
19 @response.redirect_url,
20 "Uninvited user was not redirected to inactive user page")
24 [:project_viewer, false]].each do |which_user, should_show|
25 test "create subproject button #{'not ' unless should_show} shown to #{which_user}" do
26 readonly_project_uuid = api_fixture('groups')['aproject']['uuid']
28 id: readonly_project_uuid
29 }, session_for(which_user)
30 buttons = css_select('[data-method=post]').select do |el|
31 el.attributes['data-remote-href'].match /project.*owner_uuid.*#{readonly_project_uuid}/
34 assert_not_empty(buttons, "did not offer to create a subproject")
36 assert_empty(buttons.collect(&:to_s),
37 "offered to create a subproject in a non-writable project")
42 test "sharing a project with a user and group" do
43 uuid_list = [api_fixture("groups")["future_project_viewing_group"]["uuid"],
44 api_fixture("users")["future_project_user"]["uuid"]]
46 id: api_fixture("groups")["asubproject"]["uuid"],
50 assert_response :success
51 assert_equal(uuid_list, json_response["success"])
54 test "user with project read permission can't add permissions" do
55 share_uuid = api_fixture("users")["spectator"]["uuid"]
57 id: api_fixture("groups")["aproject"]["uuid"],
60 session_for(:project_viewer))
62 assert(json_response["errors"].andand.
63 any? { |msg| msg.start_with?("#{share_uuid}: ") },
64 "JSON response missing properly formatted sharing error")
67 test "admin can_manage aproject" do
68 assert user_can_manage(:admin, api_fixture("groups")["aproject"])
71 test "owner can_manage aproject" do
72 assert user_can_manage(:active, api_fixture("groups")["aproject"])
75 test "owner can_manage asubproject" do
76 assert user_can_manage(:active, api_fixture("groups")["asubproject"])
79 test "viewer can't manage aproject" do
80 refute user_can_manage(:project_viewer, api_fixture("groups")["aproject"])
83 test "viewer can't manage asubproject" do
84 refute user_can_manage(:project_viewer, api_fixture("groups")["asubproject"])
87 test "subproject_admin can_manage asubproject" do
88 assert user_can_manage(:subproject_admin, api_fixture("groups")["asubproject"])
91 test "detect ownership loop in project breadcrumbs" do
92 # This test has an arbitrary time limit -- otherwise we'd just sit
93 # here forever instead of reporting that the loop was not
94 # detected. The test passes quickly, but fails slowly.
95 Timeout::timeout 10 do
97 { id: api_fixture("groups")["project_owns_itself"]["uuid"] },
100 assert_response :success
103 test "project admin can remove collections from the project" do
104 # Deleting an object that supports 'expires_at' should make it
105 # completely inaccessible to API queries, not simply moved out of the project.
106 coll_key = "collection_to_remove_from_subproject"
107 coll_uuid = api_fixture("collections")[coll_key]["uuid"]
109 { id: api_fixture("groups")["asubproject"]["uuid"],
110 item_uuid: coll_uuid,
112 session_for(:subproject_admin))
113 assert_response :success
114 assert_match(/\b#{coll_uuid}\b/, @response.body,
115 "removed object not named in response")
117 use_token :subproject_admin
118 assert_raise ArvadosApiClient::NotFoundException do
119 Collection.find(coll_uuid)
123 test "project admin can remove items from project other than collections" do
124 # An object which does not have an expired_at field (e.g. Specimen)
125 # should be implicitly moved to the user's Home project when removed.
126 specimen_uuid = api_fixture('specimens', 'in_asubproject')['uuid']
128 { id: api_fixture('groups', 'asubproject')['uuid'],
129 item_uuid: specimen_uuid,
131 session_for(:subproject_admin))
132 assert_response :success
133 assert_match(/\b#{specimen_uuid}\b/, @response.body,
134 "removed object not named in response")
136 use_token :subproject_admin
137 new_specimen = Specimen.find(specimen_uuid)
138 assert_equal api_fixture('users', 'subproject_admin')['uuid'], new_specimen.owner_uuid
141 # An object which does not offer an expired_at field but has a xx_owner_uuid_name_unique constraint
142 # will be renamed when removed and another object with the same name exists in user's home project.
144 ['groups', 'subproject_in_asubproject_with_same_name_as_one_in_active_user_home'],
145 ['pipeline_templates', 'template_in_asubproject_with_same_name_as_one_in_active_user_home'],
146 ].each do |dm, fixture|
147 test "removing #{dm} from a subproject results in renaming it when there is another such object with same name in home project" do
148 object = api_fixture(dm, fixture)
150 { id: api_fixture('groups', 'asubproject')['uuid'],
151 item_uuid: object['uuid'],
153 session_for(:active))
154 assert_response :success
155 assert_match(/\b#{object['uuid']}\b/, @response.body,
156 "removed object not named in response")
159 found = Group.find(object['uuid'])
161 found = PipelineTemplate.find(object['uuid'])
163 assert_equal api_fixture('users', 'active')['uuid'], found.owner_uuid
164 assert_equal true, found.name.include?(object['name'] + ' removed from ')
168 test 'projects#show tab infinite scroll partial obeys limit' do
169 get_contents_rows(limit: 1, filters: [['uuid','is_a',['arvados#job']]])
170 assert_response :success
171 assert_equal(1, json_response['content'].scan('<tr').count,
172 "Did not get exactly one row")
175 ['', ' asc', ' desc'].each do |direction|
176 test "projects#show tab partial orders correctly by #{direction}" do
177 _test_tab_content_order direction
181 def _test_tab_content_order direction
182 get_contents_rows(limit: 100,
183 order: "created_at#{direction}",
184 filters: [['uuid','is_a',['arvados#job',
185 'arvados#pipelineInstance']]])
186 assert_response :success
187 not_grouped_by_kind = nil
191 json_response['content'].scan /<tr[^>]+>/ do |tr_tag|
193 tr_tag.scan(/\ data-object-created-at=\"(.*?)\"/).each do |t,|
195 correct_operator = / desc$/ =~ direction ? :>= : :<=
196 assert_operator(last_timestamp, correct_operator, t,
197 "Rows are not sorted by created_at#{direction}")
200 found_timestamps += 1
202 assert_equal(1, found_timestamps,
203 "Content row did not have exactly one timestamp")
205 # Confirm that the test for timestamp ordering couldn't have
206 # passed merely because the test fixtures have convenient
207 # timestamps (e.g., there is only one pipeline and one job in
208 # the project being tested, or there are no pipelines at all in
209 # the project being tested):
210 tr_tag.scan /\ data-kind=\"(.*?)\"/ do |kind|
211 if last_kind and last_kind != kind and found_kind[kind]
212 # We saw this kind before, then a different kind, then
213 # this kind again. That means objects are not grouped by
215 not_grouped_by_kind = true
217 found_kind[kind] ||= 0
218 found_kind[kind] += 1
222 assert_equal(true, not_grouped_by_kind,
223 "Could not confirm that results are not grouped by kind")
226 def get_contents_rows params
228 id: api_fixture('users')['active']['uuid'],
229 partial: :contents_rows,
232 encoded_params = Hash[params.map { |k,v|
233 [k, (v.is_a?(Array) || v.is_a?(Hash)) ? v.to_json : v]
235 get :show, encoded_params, session_for(:active)
238 test "visit non-public project as anonymous when anonymous browsing is enabled and expect page not found" do
239 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
240 get(:show, {id: api_fixture('groups')['aproject']['uuid']})
242 assert_match(/log ?in/i, @response.body)
245 test "visit home page as anonymous when anonymous browsing is enabled and expect login" do
246 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
248 assert_response :redirect
249 assert_match /\/users\/welcome/, @response.redirect_url
256 test "visit public projects page when anon config is enabled, as user #{user}, and expect page" do
257 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
260 get :public, {}, session_for(user)
265 assert_response :success
266 assert_not_nil assigns(:objects)
267 project_names = assigns(:objects).collect(&:name)
268 assert_includes project_names, 'Unrestricted public data'
269 assert_not_includes project_names, 'A Project'
270 refute_empty css_select('[href="/projects/public"]')
274 test "visit public projects page when anon config is not enabled as active user and expect 404" do
275 get :public, {}, session_for(:active)
279 test "visit public projects page when anon config is enabled but public projects page is disabled as active user and expect 404" do
280 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
281 Rails.configuration.enable_public_projects_page = false
282 get :public, {}, session_for(:active)
286 test "visit public projects page when anon config is not enabled as anonymous and expect login page" do
288 assert_response :redirect
289 assert_match /\/users\/welcome/, @response.redirect_url
290 assert_empty css_select('[href="/projects/public"]')
293 test "visit public projects page when anon config is enabled and public projects page is disabled and expect login page" do
294 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
295 Rails.configuration.enable_public_projects_page = false
297 assert_response :redirect
298 assert_match /\/users\/welcome/, @response.redirect_url
299 assert_empty css_select('[href="/projects/public"]')
302 test "visit public projects page when anon config is not enabled and public projects page is enabled and expect login page" do
303 Rails.configuration.enable_public_projects_page = true
305 assert_response :redirect
306 assert_match /\/users\/welcome/, @response.redirect_url
307 assert_empty css_select('[href="/projects/public"]')
310 test "find a project and edit its description" do
311 project = api_fixture('groups')['aproject']
313 found = Group.find(project['uuid'])
314 found.description = 'test description update'
316 get(:show, {id: project['uuid']}, session_for(:active))
317 assert_includes @response.body, 'test description update'
320 test "find a project and edit description to textile description" do
321 project = api_fixture('groups')['aproject']
323 found = Group.find(project['uuid'])
324 found.description = '*test bold description for textile formatting*'
326 get(:show, {id: project['uuid']}, session_for(:active))
327 assert_includes @response.body, '<strong>test bold description for textile formatting</strong>'
330 test "find a project and edit description to html description" do
331 project = api_fixture('groups')['aproject']
333 found = Group.find(project['uuid'])
334 found.description = 'Textile description with link to home page <a href="/">take me home</a>.'
336 get(:show, {id: project['uuid']}, session_for(:active))
337 assert_includes @response.body, 'Textile description with link to home page <a href="/">take me home</a>.'
340 test "find a project and edit description to textile description with link to object" do
341 project = api_fixture('groups')['aproject']
343 found = Group.find(project['uuid'])
345 # uses 'Link to object' as a hyperlink for the object
346 found.description = '"Link to object":' + api_fixture('groups')['asubproject']['uuid']
348 get(:show, {id: project['uuid']}, session_for(:active))
350 # check that input was converted to textile, not staying as inputted
351 refute_includes @response.body,'"Link to object"'
352 refute_empty css_select('[href="/groups/zzzzz-j7d0g-axqo7eu9pwvna1x"]')
355 test "project viewer can't see project sharing tab" do
356 project = api_fixture('groups')['aproject']
357 get(:show, {id: project['uuid']}, session_for(:project_viewer))
358 refute_includes @response.body, '<div id="Sharing"'
359 assert_includes @response.body, '<div id="Data_collections"'
366 test "#{username} can see project sharing tab" do
367 project = api_fixture('groups')['aproject']
368 get(:show, {id: project['uuid']}, session_for(username))
369 assert_includes @response.body, '<div id="Sharing"'
370 assert_includes @response.body, '<div id="Data_collections"'
377 ['project_viewer',false],
378 ].each do |user, can_move|
379 test "#{user} can move subproject from project #{can_move}" do
380 get(:show, {id: api_fixture('groups')['aproject']['uuid']}, session_for(user))
382 assert_includes @response.body, 'Move project...'
384 refute_includes @response.body, 'Move project...'
391 ["pipelines", "/pipeline_instances"],
392 ["collections", "/collections"],
393 ].each do |target,path|
394 test "test dashboard button all #{target}" do
395 get :index, {}, session_for(:active)
396 assert_includes @response.body, "href=\"#{path}\""
397 assert_includes @response.body, "All #{target}"
401 test "visit a public project and verify the public projects page link exists" do
402 Rails.configuration.anonymous_user_token = api_fixture('api_client_authorizations')['anonymous']['api_token']
403 uuid = api_fixture('groups')['anonymously_accessible_project']['uuid']
404 get :show, {id: uuid}
405 project = assigns(:object)
406 assert_equal uuid, project['uuid']
407 refute_empty css_select("[href=\"/projects/#{project['uuid']}\"]")
408 assert_includes @response.body, "<a href=\"/projects/public\">Public Projects</a>"
411 test 'all_projects unaffected by params after use by ProjectsController (#6640)' do
412 @controller = ProjectsController.new
413 project_uuid = api_fixture('groups')['aproject']['uuid']
415 filters: [['uuid', '<', project_uuid]].to_json,
418 }, session_for(:active)
419 assert_select "#projects-menu + ul li.divider ~ li a[href=/projects/#{project_uuid}]"
423 ["active", 5, ["aproject", "asubproject"], "anonymously_accessible_project"],
424 ["user1_with_load", 2, ["project_with_10_collections"], "project_with_2_pipelines_and_60_jobs"],
425 ["admin", 5, ["anonymously_accessible_project", "subproject_in_anonymous_accessible_project"], "aproject"],
426 ].each do |user, page_size, tree_segment, unexpected|
427 test "build my projects tree for #{user} user and verify #{unexpected} is omitted" do
429 ctrl = ProjectsController.new
431 current_user = User.find(api_fixture('users')[user]['uuid'])
433 my_tree = ctrl.send :my_wanted_projects_tree, current_user, page_size
435 tree_segment_at_depth_1 = api_fixture('groups')[tree_segment[0]]
436 tree_segment_at_depth_2 = api_fixture('groups')[tree_segment[1]] if tree_segment[1]
439 my_tree[0].each do |x|
440 tree_nodes[x[:object]['uuid']] = x[:depth]
443 assert_equal(1, tree_nodes[tree_segment_at_depth_1['uuid']])
444 assert_equal(2, tree_nodes[tree_segment_at_depth_2['uuid']]) if tree_segment[1]
446 unexpected_project = api_fixture('groups')[unexpected]
447 assert_nil(tree_nodes[unexpected_project['uuid']])