Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorSean McGivern <sean@mcgivern.me.uk>2017-05-06 08:45:56 +0300
committerSean McGivern <sean@mcgivern.me.uk>2017-05-06 08:45:56 +0300
commit56fb7823f9740d4595d90dcb224180c950a27048 (patch)
treeebf2f5498f164477cfe594ea6b820202087532a9 /spec
parent6ce1df41e175c7d62ca760b1e66cf1bf86150284 (diff)
parentb0ee22609a89572d6e3f98eebccf9fb2335dd939 (diff)
Merge branch '17361-redirect-renamed-paths' into 'master'
Resolve "Redirect to new project link after a rename" Closes #17361 and #30317 See merge request !11136
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/application_controller_spec.rb5
-rw-r--r--spec/controllers/groups_controller_spec.rb96
-rw-r--r--spec/controllers/projects_controller_spec.rb87
-rw-r--r--spec/controllers/users_controller_spec.rb211
-rw-r--r--spec/features/groups/group_settings_spec.rb80
-rw-r--r--spec/features/profiles/account_spec.rb59
-rw-r--r--spec/features/projects/features_visibility_spec.rb35
-rw-r--r--spec/features/projects/project_settings_spec.rb152
-rw-r--r--spec/lib/constraints/group_url_constrainer_spec.rb32
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb21
-rw-r--r--spec/lib/constraints/user_url_constrainer_spec.rb21
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/models/concerns/routable_spec.rb52
-rw-r--r--spec/models/redirect_route_spec.rb27
-rw-r--r--spec/models/route_spec.rb114
-rw-r--r--spec/models/user_spec.rb59
-rw-r--r--spec/routing/project_routing_spec.rb6
17 files changed, 973 insertions, 85 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 1bf0533ca24..d40aae04fc3 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -106,10 +106,9 @@ describe ApplicationController do
controller.send(:route_not_found)
end
- it 'does redirect to login page if not authenticated' do
+ it 'does redirect to login page via authenticate_user! if not authenticated' do
allow(controller).to receive(:current_user).and_return(nil)
- expect(controller).to receive(:redirect_to)
- expect(controller).to receive(:new_user_session_path)
+ expect(controller).to receive(:authenticate_user!)
controller.send(:route_not_found)
end
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index cad82a34fb0..073b87a1cb4 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -49,6 +49,26 @@ describe GroupsController do
expect(assigns(:issues)).to eq [issue_2, issue_1]
end
end
+
+ context 'when requesting the canonical path with different casing' do
+ it 'redirects to the correct casing' do
+ get :issues, id: group.to_param.upcase
+
+ expect(response).to redirect_to(issues_group_path(group.to_param))
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
+
+ it 'redirects to the canonical path' do
+ get :issues, id: redirect_route.path
+
+ expect(response).to redirect_to(issues_group_path(group.to_param))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe 'GET #merge_requests' do
@@ -74,6 +94,26 @@ describe GroupsController do
expect(assigns(:merge_requests)).to eq [merge_request_2, merge_request_1]
end
end
+
+ context 'when requesting the canonical path with different casing' do
+ it 'redirects to the correct casing' do
+ get :merge_requests, id: group.to_param.upcase
+
+ expect(response).to redirect_to(merge_requests_group_path(group.to_param))
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
+
+ it 'redirects to the canonical path' do
+ get :merge_requests, id: redirect_route.path
+
+ expect(response).to redirect_to(merge_requests_group_path(group.to_param))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe 'DELETE #destroy' do
@@ -81,7 +121,7 @@ describe GroupsController do
it 'returns 404' do
sign_in(create(:user))
- delete :destroy, id: group.path
+ delete :destroy, id: group.to_param
expect(response.status).to eq(404)
end
@@ -94,15 +134,39 @@ describe GroupsController do
it 'schedules a group destroy' do
Sidekiq::Testing.fake! do
- expect { delete :destroy, id: group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
+ expect { delete :destroy, id: group.to_param }.to change(GroupDestroyWorker.jobs, :size).by(1)
end
end
it 'redirects to the root path' do
- delete :destroy, id: group.path
+ delete :destroy, id: group.to_param
expect(response).to redirect_to(root_path)
end
+
+ context 'when requesting the canonical path with different casing' do
+ it 'does not 404' do
+ delete :destroy, id: group.to_param.upcase
+
+ expect(response).not_to have_http_status(404)
+ end
+
+ it 'does not redirect to the correct casing' do
+ delete :destroy, id: group.to_param.upcase
+
+ expect(response).not_to redirect_to(group_path(group.to_param))
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
+
+ it 'returns not found' do
+ delete :destroy, id: redirect_route.path
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
end
@@ -111,7 +175,7 @@ describe GroupsController do
sign_in(user)
end
- it 'updates the path succesfully' do
+ it 'updates the path successfully' do
post :update, id: group.to_param, group: { path: 'new_path' }
expect(response).to have_http_status(302)
@@ -125,5 +189,29 @@ describe GroupsController do
expect(assigns(:group).errors).not_to be_empty
expect(assigns(:group).path).not_to eq('new_path')
end
+
+ context 'when requesting the canonical path with different casing' do
+ it 'does not 404' do
+ post :update, id: group.to_param.upcase, group: { path: 'new_path' }
+
+ expect(response).not_to have_http_status(404)
+ end
+
+ it 'does not redirect to the correct casing' do
+ post :update, id: group.to_param.upcase, group: { path: 'new_path' }
+
+ expect(response).not_to redirect_to(group_path(group.to_param))
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
+
+ it 'returns not found' do
+ post :update, id: redirect_route.path, group: { path: 'new_path' }
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index eafc2154568..e46ef447df2 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -185,6 +185,7 @@ describe ProjectsController do
expect(assigns(:project)).to eq(public_project)
expect(response).to redirect_to("/#{public_project.full_path}")
+ expect(controller).not_to set_flash[:notice]
end
end
end
@@ -218,19 +219,33 @@ describe ProjectsController do
expect(response).to redirect_to(namespace_project_path)
end
end
+
+ context 'when requesting a redirected path' do
+ let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
+
+ it 'redirects to the canonical path' do
+ get :show, namespace_id: 'foo', id: 'bar'
+
+ expect(response).to redirect_to(public_project)
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe "#update" do
render_views
let(:admin) { create(:admin) }
+ let(:project) { create(:project, :repository) }
+ let(:new_path) { 'renamed_path' }
+ let(:project_params) { { path: new_path } }
+
+ before do
+ sign_in(admin)
+ end
it "sets the repository to the right path after a rename" do
- project = create(:project, :repository)
- new_path = 'renamed_path'
- project_params = { path: new_path }
controller.instance_variable_set(:@project, project)
- sign_in(admin)
put :update,
namespace_id: project.namespace,
@@ -241,6 +256,34 @@ describe ProjectsController do
expect(assigns(:repository).path).to eq(project.repository.path)
expect(response).to have_http_status(302)
end
+
+ context 'when requesting the canonical path' do
+ it "is case-insensitive" do
+ controller.instance_variable_set(:@project, project)
+
+ put :update,
+ namespace_id: 'FOo',
+ id: 'baR',
+ project: project_params
+
+ expect(project.repository.path).to include(new_path)
+ expect(assigns(:repository).path).to eq(project.repository.path)
+ expect(response).to have_http_status(302)
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") }
+
+ it 'returns not found' do
+ put :update,
+ namespace_id: 'foo',
+ id: 'bar',
+ project: project_params
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
describe "#destroy" do
@@ -276,6 +319,31 @@ describe ProjectsController do
expect(merge_request.reload.state).to eq('closed')
end
end
+
+ context 'when requesting the canonical path' do
+ it "is case-insensitive" do
+ controller.instance_variable_set(:@project, project)
+ sign_in(admin)
+
+ orig_id = project.id
+ delete :destroy, namespace_id: project.namespace, id: project.path.upcase
+
+ expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(dashboard_projects_path)
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") }
+
+ it 'returns not found' do
+ sign_in(admin)
+ delete :destroy, namespace_id: 'foo', id: 'bar'
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
describe 'PUT #new_issue_address' do
@@ -397,6 +465,17 @@ describe ProjectsController do
expect(parsed_body["Tags"]).to include("v1.0.0")
expect(parsed_body["Commits"]).to include("123456")
end
+
+ context 'when requesting a redirected path' do
+ let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
+
+ it 'redirects to the canonical path' do
+ get :refs, namespace_id: 'foo', id: 'bar'
+
+ expect(response).to redirect_to(refs_namespace_project_path(namespace_id: public_project.namespace, id: public_project))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe 'POST #preview_markdown' do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index bbe9aaf737f..74c5aa44ba9 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -4,15 +4,6 @@ describe UsersController do
let(:user) { create(:user) }
describe 'GET #show' do
- it 'is case-insensitive' do
- user = create(:user, username: 'CamelCaseUser')
- sign_in(user)
-
- get :show, username: user.username.downcase
-
- expect(response).to be_success
- end
-
context 'with rendered views' do
render_views
@@ -45,9 +36,9 @@ describe UsersController do
end
context 'when logged out' do
- it 'renders 404' do
+ it 'redirects to login page' do
get :show, username: user.username
- expect(response).to have_http_status(404)
+ expect(response).to redirect_to new_user_session_path
end
end
@@ -61,6 +52,58 @@ describe UsersController do
end
end
end
+
+ context 'when requesting the canonical path' do
+ let(:user) { create(:user, username: 'CamelCaseUser') }
+
+ before { sign_in(user) }
+
+ context 'with exactly matching casing' do
+ it 'responds with success' do
+ get :show, username: user.username
+
+ expect(response).to be_success
+ end
+ end
+
+ context 'with different casing' do
+ it 'redirects to the correct casing' do
+ get :show, username: user.username.downcase
+
+ expect(response).to redirect_to(user)
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') }
+
+ it 'redirects to the canonical path' do
+ get :show, username: redirect_route.path
+
+ expect(response).to redirect_to(user)
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
+
+ context 'when a user by that username does not exist' do
+ context 'when logged out' do
+ it 'redirects to login page' do
+ get :show, username: 'nonexistent'
+ expect(response).to redirect_to new_user_session_path
+ end
+ end
+
+ context 'when logged in' do
+ before { sign_in(user) }
+
+ it 'renders 404' do
+ get :show, username: 'nonexistent'
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
end
describe 'GET #calendar' do
@@ -88,11 +131,45 @@ describe UsersController do
expect(assigns(:contributions_calendar).projects.count).to eq(2)
end
end
+
+ context 'when requesting the canonical path' do
+ let(:user) { create(:user, username: 'CamelCaseUser') }
+
+ before { sign_in(user) }
+
+ context 'with exactly matching casing' do
+ it 'responds with success' do
+ get :calendar, username: user.username
+
+ expect(response).to be_success
+ end
+ end
+
+ context 'with different casing' do
+ it 'redirects to the correct casing' do
+ get :calendar, username: user.username.downcase
+
+ expect(response).to redirect_to(user_calendar_path(user))
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') }
+
+ it 'redirects to the canonical path' do
+ get :calendar, username: redirect_route.path
+
+ expect(response).to redirect_to(user_calendar_path(user))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe 'GET #calendar_activities' do
let!(:project) { create(:empty_project) }
- let!(:user) { create(:user) }
+ let(:user) { create(:user) }
before do
allow_any_instance_of(User).to receive(:contributed_projects_ids).and_return([project.id])
@@ -110,6 +187,38 @@ describe UsersController do
get :calendar_activities, username: user.username
expect(response).to render_template('calendar_activities')
end
+
+ context 'when requesting the canonical path' do
+ let(:user) { create(:user, username: 'CamelCaseUser') }
+
+ context 'with exactly matching casing' do
+ it 'responds with success' do
+ get :calendar_activities, username: user.username
+
+ expect(response).to be_success
+ end
+ end
+
+ context 'with different casing' do
+ it 'redirects to the correct casing' do
+ get :calendar_activities, username: user.username.downcase
+
+ expect(response).to redirect_to(user_calendar_activities_path(user))
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') }
+
+ it 'redirects to the canonical path' do
+ get :calendar_activities, username: redirect_route.path
+
+ expect(response).to redirect_to(user_calendar_activities_path(user))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
end
describe 'GET #snippets' do
@@ -132,5 +241,83 @@ describe UsersController do
expect(JSON.parse(response.body)).to have_key('html')
end
end
+
+ context 'when requesting the canonical path' do
+ let(:user) { create(:user, username: 'CamelCaseUser') }
+
+ context 'with exactly matching casing' do
+ it 'responds with success' do
+ get :snippets, username: user.username
+
+ expect(response).to be_success
+ end
+ end
+
+ context 'with different casing' do
+ it 'redirects to the correct casing' do
+ get :snippets, username: user.username.downcase
+
+ expect(response).to redirect_to(user_snippets_path(user))
+ expect(controller).not_to set_flash[:notice]
+ end
+ end
+ end
+
+ context 'when requesting a redirected path' do
+ let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') }
+
+ it 'redirects to the canonical path' do
+ get :snippets, username: redirect_route.path
+
+ expect(response).to redirect_to(user_snippets_path(user))
+ expect(controller).to set_flash[:notice].to(/moved/)
+ end
+ end
+ end
+
+ describe 'GET #exists' do
+ before do
+ sign_in(user)
+ end
+
+ context 'when user exists' do
+ it 'returns JSON indicating the user exists' do
+ get :exists, username: user.username
+
+ expected_json = { exists: true }.to_json
+ expect(response.body).to eq(expected_json)
+ end
+
+ context 'when the casing is different' do
+ let(:user) { create(:user, username: 'CamelCaseUser') }
+
+ it 'returns JSON indicating the user exists' do
+ get :exists, username: user.username.downcase
+
+ expected_json = { exists: true }.to_json
+ expect(response.body).to eq(expected_json)
+ end
+ end
+ end
+
+ context 'when the user does not exist' do
+ it 'returns JSON indicating the user does not exist' do
+ get :exists, username: 'foo'
+
+ expected_json = { exists: false }.to_json
+ expect(response.body).to eq(expected_json)
+ end
+
+ context 'when a user changed their username' do
+ let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') }
+
+ it 'returns JSON indicating a user by that username does not exist' do
+ get :exists, username: 'old-username'
+
+ expected_json = { exists: false }.to_json
+ expect(response.body).to eq(expected_json)
+ end
+ end
+ end
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
new file mode 100644
index 00000000000..cc25db4ad60
--- /dev/null
+++ b/spec/features/groups/group_settings_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+feature 'Edit group settings', feature: true do
+ given(:user) { create(:user) }
+ given(:group) { create(:group, path: 'foo') }
+
+ background do
+ group.add_owner(user)
+ login_as(user)
+ end
+
+ describe 'when the group path is changed' do
+ let(:new_group_path) { 'bar' }
+ let(:old_group_full_path) { "/#{group.path}" }
+ let(:new_group_full_path) { "/#{new_group_path}" }
+
+ scenario 'the group is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_group_full_path
+ expect(current_path).to eq(new_group_full_path)
+ expect(find('h1.group-title')).to have_content(new_group_path)
+ end
+
+ scenario 'the old group path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_group_full_path
+ expect(current_path).to eq(new_group_full_path)
+ expect(find('h1.group-title')).to have_content(new_group_path)
+ end
+
+ context 'with a subgroup' do
+ given!(:subgroup) { create(:group, parent: group, path: 'subgroup') }
+ given(:old_subgroup_full_path) { "/#{group.path}/#{subgroup.path}" }
+ given(:new_subgroup_full_path) { "/#{new_group_path}/#{subgroup.path}" }
+
+ scenario 'the subgroup is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_subgroup_full_path
+ expect(current_path).to eq(new_subgroup_full_path)
+ expect(find('h1.group-title')).to have_content(subgroup.path)
+ end
+
+ scenario 'the old subgroup path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_subgroup_full_path
+ expect(current_path).to eq(new_subgroup_full_path)
+ expect(find('h1.group-title')).to have_content(subgroup.path)
+ end
+ end
+
+ context 'with a project' do
+ given!(:project) { create(:project, group: group, path: 'project') }
+ given(:old_project_full_path) { "/#{group.path}/#{project.path}" }
+ given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ scenario 'the project is accessible via the new path' do
+ update_path(new_group_path)
+ visit new_project_full_path
+ expect(current_path).to eq(new_project_full_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+
+ scenario 'the old project path redirects to the new path' do
+ update_path(new_group_path)
+ visit old_project_full_path
+ expect(current_path).to eq(new_project_full_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+ end
+ end
+end
+
+def update_path(new_group_path)
+ visit edit_group_path(group)
+ fill_in 'group_path', with: new_group_path
+ click_button 'Save group'
+end
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
new file mode 100644
index 00000000000..05a7587f8d4
--- /dev/null
+++ b/spec/features/profiles/account_spec.rb
@@ -0,0 +1,59 @@
+require 'rails_helper'
+
+feature 'Profile > Account', feature: true do
+ given(:user) { create(:user, username: 'foo') }
+
+ before do
+ login_as(user)
+ end
+
+ describe 'Change username' do
+ given(:new_username) { 'bar' }
+ given(:new_user_path) { "/#{new_username}" }
+ given(:old_user_path) { "/#{user.username}" }
+
+ scenario 'the user is accessible via the new path' do
+ update_username(new_username)
+ visit new_user_path
+ expect(current_path).to eq(new_user_path)
+ expect(find('.user-info')).to have_content(new_username)
+ end
+
+ scenario 'the old user path redirects to the new path' do
+ update_username(new_username)
+ visit old_user_path
+ expect(current_path).to eq(new_user_path)
+ expect(find('.user-info')).to have_content(new_username)
+ end
+
+ context 'with a project' do
+ given!(:project) { create(:project, namespace: user.namespace, path: 'project') }
+ given(:new_project_path) { "/#{new_username}/#{project.path}" }
+ given(:old_project_path) { "/#{user.username}/#{project.path}" }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ scenario 'the project is accessible via the new path' do
+ update_username(new_username)
+ visit new_project_path
+ expect(current_path).to eq(new_project_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+
+ scenario 'the old project path redirects to the new path' do
+ update_username(new_username)
+ visit old_project_path
+ expect(current_path).to eq(new_project_path)
+ expect(find('h1.project-title')).to have_content(project.name)
+ end
+ end
+ end
+end
+
+def update_username(new_username)
+ allow(user.namespace).to receive(:move_dir)
+ visit profile_account_path
+ fill_in 'user_username', with: new_username
+ click_button 'Update username'
+end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index b080a8d500e..e1781cf320a 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -68,20 +68,23 @@ describe 'Edit Project Settings', feature: true do
end
describe 'project features visibility pages' do
- before do
- @tools =
- {
- builds: namespace_project_pipelines_path(project.namespace, project),
- issues: namespace_project_issues_path(project.namespace, project),
- wiki: namespace_project_wiki_path(project.namespace, project, :home),
- snippets: namespace_project_snippets_path(project.namespace, project),
- merge_requests: namespace_project_merge_requests_path(project.namespace, project),
- }
+ let(:tools) do
+ {
+ builds: namespace_project_pipelines_path(project.namespace, project),
+ issues: namespace_project_issues_path(project.namespace, project),
+ wiki: namespace_project_wiki_path(project.namespace, project, :home),
+ snippets: namespace_project_snippets_path(project.namespace, project),
+ merge_requests: namespace_project_merge_requests_path(project.namespace, project),
+ }
end
context 'normal user' do
+ before do
+ login_as(member)
+ end
+
it 'renders 200 if tool is enabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::ENABLED)
visit url
expect(page.status_code).to eq(200)
@@ -89,7 +92,7 @@ describe 'Edit Project Settings', feature: true do
end
it 'renders 404 if feature is disabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
@@ -99,21 +102,21 @@ describe 'Edit Project Settings', feature: true do
it 'renders 404 if feature is enabled only for team members' do
project.team.truncate
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(404)
end
end
- it 'renders 200 if users is member of group' do
+ it 'renders 200 if user is member of group' do
group = create(:group)
project.group = group
project.save
group.add_owner(member)
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
@@ -128,7 +131,7 @@ describe 'Edit Project Settings', feature: true do
end
it 'renders 404 if feature is disabled' do
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
@@ -138,7 +141,7 @@ describe 'Edit Project Settings', feature: true do
it 'renders 200 if feature is enabled only for team members' do
project.team.truncate
- @tools.each do |method_name, url|
+ tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb
index 5d0314d5c09..11dcab4d737 100644
--- a/spec/features/projects/project_settings_spec.rb
+++ b/spec/features/projects/project_settings_spec.rb
@@ -1,64 +1,158 @@
require 'spec_helper'
describe 'Edit Project Settings', feature: true do
+ include Select2Helper
+
let(:user) { create(:user) }
- let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
+ let(:project) { create(:empty_project, namespace: user.namespace, path: 'gitlab', name: 'sample') }
before do
login_as(user)
- project.team << [user, :master]
end
- describe 'Project settings', js: true do
+ describe 'Project settings section', js: true do
it 'shows errors for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
-
fill_in 'project_name_edit', with: 'foo&bar'
-
click_button 'Save changes'
-
expect(page).to have_field 'project_name_edit', with: 'foo&bar'
expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
expect(page).to have_button 'Save changes'
end
- scenario 'shows a successful notice when the project is updated' do
+ it 'shows a successful notice when the project is updated' do
visit edit_namespace_project_path(project.namespace, project)
-
fill_in 'project_name_edit', with: 'hello world'
-
click_button 'Save changes'
-
expect(page).to have_content "Project 'hello world' was successfully updated."
end
end
- describe 'Rename repository' do
- it 'shows errors for invalid project path/name' do
- visit edit_namespace_project_path(project.namespace, project)
-
- fill_in 'project_name', with: 'foo&bar'
- fill_in 'Path', with: 'foo&bar'
+ describe 'Rename repository section' do
+ context 'with invalid characters' do
+ it 'shows errors for invalid project path/name' do
+ rename_project(project, name: 'foo&bar', path: 'foo&bar')
+ expect(page).to have_field 'Project name', with: 'foo&bar'
+ expect(page).to have_field 'Path', with: 'foo&bar'
+ expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
+ expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
+ end
+ end
- click_button 'Rename project'
+ context 'when changing project name' do
+ it 'renames the repository' do
+ rename_project(project, name: 'bar')
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ context 'with emojis' do
+ it 'shows error for invalid project name' do
+ rename_project(project, name: '🚀 foo bar ☁️')
+ expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️'
+ expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'."
+ end
+ end
+ end
- expect(page).to have_field 'Project name', with: 'foo&bar'
- expect(page).to have_field 'Path', with: 'foo&bar'
- expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
- expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
+ context 'when changing project path' do
+ # Not using empty project because we need a repo to exist
+ let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
+
+ before(:context) { TestEnv.clean_test_path }
+ after(:example) { TestEnv.clean_test_path }
+
+ specify 'the project is accessible via the new path' do
+ rename_project(project, path: 'bar')
+ new_path = namespace_project_path(project.namespace, 'bar')
+ visit new_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ specify 'the project is accessible via a redirect from the old path' do
+ old_path = namespace_project_path(project.namespace, project)
+ rename_project(project, path: 'bar')
+ new_path = namespace_project_path(project.namespace, 'bar')
+ visit old_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
+
+ context 'and a new project is added with the same path' do
+ it 'overrides the redirect' do
+ old_path = namespace_project_path(project.namespace, project)
+ rename_project(project, path: 'bar')
+ new_project = create(:empty_project, namespace: user.namespace, path: 'gitlabhq', name: 'quz')
+ visit old_path
+ expect(current_path).to eq(old_path)
+ expect(find('h1.title')).to have_content(new_project.name)
+ end
+ end
end
end
- describe 'Rename repository name with emojis' do
- it 'shows error for invalid project name' do
- visit edit_namespace_project_path(project.namespace, project)
-
- fill_in 'project_name', with: '🚀 foo bar ☁️'
+ describe 'Transfer project section', js: true do
+ # Not using empty project because we need a repo to exist
+ let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
+ let!(:group) { create(:group) }
+
+ before(:context) { TestEnv.clean_test_path }
+ before(:example) { group.add_owner(user) }
+ after(:example) { TestEnv.clean_test_path }
+
+ specify 'the project is accessible via the new path' do
+ transfer_project(project, group)
+ new_path = namespace_project_path(group, project)
+ visit new_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
- click_button 'Rename project'
+ specify 'the project is accessible via a redirect from the old path' do
+ old_path = namespace_project_path(project.namespace, project)
+ transfer_project(project, group)
+ new_path = namespace_project_path(group, project)
+ visit old_path
+ expect(current_path).to eq(new_path)
+ expect(find('h1.title')).to have_content(project.name)
+ end
- expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️'
- expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'."
+ context 'and a new project is added with the same path' do
+ it 'overrides the redirect' do
+ old_path = namespace_project_path(project.namespace, project)
+ transfer_project(project, group)
+ new_project = create(:empty_project, namespace: user.namespace, path: 'gitlabhq', name: 'quz')
+ visit old_path
+ expect(current_path).to eq(old_path)
+ expect(find('h1.title')).to have_content(new_project.name)
+ end
end
end
end
+
+def rename_project(project, name: nil, path: nil)
+ visit edit_namespace_project_path(project.namespace, project)
+ fill_in('project_name', with: name) if name
+ fill_in('Path', with: path) if path
+ click_button('Rename project')
+ wait_for_edit_project_page_reload
+ project.reload
+end
+
+def transfer_project(project, namespace)
+ visit edit_namespace_project_path(project.namespace, project)
+ select2(namespace.id, from: '#new_namespace_id')
+ click_button('Transfer project')
+ confirm_transfer_modal
+ wait_for_edit_project_page_reload
+ project.reload
+end
+
+def confirm_transfer_modal
+ fill_in('confirm_name_input', with: project.path)
+ click_button 'Confirm'
+end
+
+def wait_for_edit_project_page_reload
+ expect(find('.project-edit-container')).to have_content('Rename repository')
+end
diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb
index f95adf3a84b..db680489a8d 100644
--- a/spec/lib/constraints/group_url_constrainer_spec.rb
+++ b/spec/lib/constraints/group_url_constrainer_spec.rb
@@ -29,9 +29,37 @@ describe GroupUrlConstrainer, lib: true do
it { expect(subject.matches?(request)).to be_falsey }
end
+
+ context 'when the request matches a redirect route' do
+ context 'for a root group' do
+ let!(:redirect_route) { group.redirect_routes.create!(path: 'gitlabb') }
+
+ context 'and is a GET request' do
+ let(:request) { build_request(redirect_route.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'and is NOT a GET request' do
+ let(:request) { build_request(redirect_route.path, 'POST') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
+
+ context 'for a nested group' do
+ let!(:nested_group) { create(:group, path: 'nested', parent: group) }
+ let!(:redirect_route) { nested_group.redirect_routes.create!(path: 'gitlabb/nested') }
+ let(:request) { build_request(redirect_route.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+ end
end
- def build_request(path)
- double(:request, params: { id: path })
+ def build_request(path, method = 'GET')
+ double(:request,
+ 'get?': (method == 'GET'),
+ params: { id: path })
end
end
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
index 4f25ad88960..b6884e37aa3 100644
--- a/spec/lib/constraints/project_url_constrainer_spec.rb
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -24,9 +24,26 @@ describe ProjectUrlConstrainer, lib: true do
it { expect(subject.matches?(request)).to be_falsey }
end
end
+
+ context 'when the request matches a redirect route' do
+ let(:old_project_path) { 'old_project_path' }
+ let!(:redirect_route) { project.redirect_routes.create!(path: "#{namespace.full_path}/#{old_project_path}") }
+
+ context 'and is a GET request' do
+ let(:request) { build_request(namespace.full_path, old_project_path) }
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'and is NOT a GET request' do
+ let(:request) { build_request(namespace.full_path, old_project_path, 'POST') }
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
end
- def build_request(namespace, project)
- double(:request, params: { namespace_id: namespace, id: project })
+ def build_request(namespace, project, method = 'GET')
+ double(:request,
+ 'get?': (method == 'GET'),
+ params: { namespace_id: namespace, id: project })
end
end
diff --git a/spec/lib/constraints/user_url_constrainer_spec.rb b/spec/lib/constraints/user_url_constrainer_spec.rb
index 207b6fe6c9e..ed69b830979 100644
--- a/spec/lib/constraints/user_url_constrainer_spec.rb
+++ b/spec/lib/constraints/user_url_constrainer_spec.rb
@@ -15,9 +15,26 @@ describe UserUrlConstrainer, lib: true do
it { expect(subject.matches?(request)).to be_falsey }
end
+
+ context 'when the request matches a redirect route' do
+ let(:old_project_path) { 'old_project_path' }
+ let!(:redirect_route) { user.namespace.redirect_routes.create!(path: 'foo') }
+
+ context 'and is a GET request' do
+ let(:request) { build_request(redirect_route.path) }
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'and is NOT a GET request' do
+ let(:request) { build_request(redirect_route.path, 'POST') }
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
end
- def build_request(username)
- double(:request, params: { username: username })
+ def build_request(username, method = 'GET')
+ double(:request,
+ 'get?': (method == 'GET'),
+ params: { username: username })
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index f7654f1aade..baa81870e81 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -229,6 +229,7 @@ project:
- authorized_users
- project_authorizations
- route
+- redirect_routes
- statistics
- container_repositories
- uploads
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index 221647d7a48..49a4132f763 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -9,6 +9,7 @@ describe Group, 'Routable' do
describe 'Associations' do
it { is_expected.to have_one(:route).dependent(:destroy) }
+ it { is_expected.to have_many(:redirect_routes).dependent(:destroy) }
end
describe 'Callbacks' do
@@ -35,10 +36,53 @@ describe Group, 'Routable' do
describe '.find_by_full_path' do
let!(:nested_group) { create(:group, parent: group) }
- it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
- it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
- it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
- it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
+ context 'without any redirect routes' do
+ it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
+ it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
+ it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
+ it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
+ end
+
+ context 'with redirect routes' do
+ let!(:group_redirect_route) { group.redirect_routes.create!(path: 'bar') }
+ let!(:nested_group_redirect_route) { nested_group.redirect_routes.create!(path: nested_group.path.sub('foo', 'bar')) }
+
+ context 'without follow_redirects option' do
+ context 'with the given path not matching any route' do
+ it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
+ end
+
+ context 'with the given path matching the canonical route' do
+ it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
+ it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
+ it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
+ end
+
+ context 'with the given path matching a redirect route' do
+ it { expect(described_class.find_by_full_path(group_redirect_route.path)).to eq(nil) }
+ it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase)).to eq(nil) }
+ it { expect(described_class.find_by_full_path(nested_group_redirect_route.path)).to eq(nil) }
+ end
+ end
+
+ context 'with follow_redirects option set to true' do
+ context 'with the given path not matching any route' do
+ it { expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil) }
+ end
+
+ context 'with the given path matching the canonical route' do
+ it { expect(described_class.find_by_full_path(group.to_param, follow_redirects: true)).to eq(group) }
+ it { expect(described_class.find_by_full_path(group.to_param.upcase, follow_redirects: true)).to eq(group) }
+ it { expect(described_class.find_by_full_path(nested_group.to_param, follow_redirects: true)).to eq(nested_group) }
+ end
+
+ context 'with the given path matching a redirect route' do
+ it { expect(described_class.find_by_full_path(group_redirect_route.path, follow_redirects: true)).to eq(group) }
+ it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase, follow_redirects: true)).to eq(group) }
+ it { expect(described_class.find_by_full_path(nested_group_redirect_route.path, follow_redirects: true)).to eq(nested_group) }
+ end
+ end
+ end
end
describe '.where_full_path_in' do
diff --git a/spec/models/redirect_route_spec.rb b/spec/models/redirect_route_spec.rb
new file mode 100644
index 00000000000..71827421dd7
--- /dev/null
+++ b/spec/models/redirect_route_spec.rb
@@ -0,0 +1,27 @@
+require 'rails_helper'
+
+describe RedirectRoute, models: true do
+ let(:group) { create(:group) }
+ let!(:redirect_route) { group.redirect_routes.create(path: 'gitlabb') }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:source) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:source) }
+ it { is_expected.to validate_presence_of(:path) }
+ it { is_expected.to validate_uniqueness_of(:path) }
+ end
+
+ describe '.matching_path_and_descendants' do
+ let!(:redirect2) { group.redirect_routes.create(path: 'gitlabb/test') }
+ let!(:redirect3) { group.redirect_routes.create(path: 'gitlabb/test/foo') }
+ let!(:redirect4) { group.redirect_routes.create(path: 'gitlabb/test/foo/bar') }
+ let!(:redirect5) { group.redirect_routes.create(path: 'gitlabb/test/baz') }
+
+ it 'returns correct routes' do
+ expect(RedirectRoute.matching_path_and_descendants('gitlabb/test')).to match_array([redirect2, redirect3, redirect4, redirect5])
+ end
+ end
+end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 171a51fcc5b..c1fe1b06c52 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -1,19 +1,43 @@
require 'spec_helper'
describe Route, models: true do
- let!(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
- let!(:route) { group.route }
+ let(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
+ let(:route) { group.route }
describe 'relationships' do
it { is_expected.to belong_to(:source) }
end
describe 'validations' do
+ before { route }
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path) }
end
+ describe 'callbacks' do
+ context 'after update' do
+ it 'calls #create_redirect_for_old_path' do
+ expect(route).to receive(:create_redirect_for_old_path)
+ route.update_attributes(path: 'foo')
+ end
+
+ it 'calls #delete_conflicting_redirects' do
+ expect(route).to receive(:delete_conflicting_redirects)
+ route.update_attributes(path: 'foo')
+ end
+ end
+
+ context 'after create' do
+ it 'calls #delete_conflicting_redirects' do
+ route.destroy
+ new_route = Route.new(source: group, path: group.path)
+ expect(new_route).to receive(:delete_conflicting_redirects)
+ new_route.save!
+ end
+ end
+ end
+
describe '.inside_path' do
let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
@@ -37,7 +61,7 @@ describe Route, models: true do
context 'when route name is set' do
before { route.update_attributes(path: 'bar') }
- it "updates children routes with new path" do
+ it 'updates children routes with new path' do
expect(described_class.exists?(path: 'bar')).to be_truthy
expect(described_class.exists?(path: 'bar/test')).to be_truthy
expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy
@@ -56,10 +80,24 @@ describe Route, models: true do
expect(route.update_attributes(path: 'bar')).to be_truthy
end
end
+
+ context 'when conflicting redirects exist' do
+ let!(:conflicting_redirect1) { route.create_redirect('bar/test') }
+ let!(:conflicting_redirect2) { route.create_redirect('bar/test/foo') }
+ let!(:conflicting_redirect3) { route.create_redirect('gitlab-org') }
+
+ it 'deletes the conflicting redirects' do
+ route.update_attributes(path: 'bar')
+
+ expect(RedirectRoute.exists?(path: 'bar/test')).to be_falsey
+ expect(RedirectRoute.exists?(path: 'bar/test/foo')).to be_falsey
+ expect(RedirectRoute.exists?(path: 'gitlab-org')).to be_truthy
+ end
+ end
end
context 'name update' do
- it "updates children routes with new path" do
+ it 'updates children routes with new path' do
route.update_attributes(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
@@ -77,4 +115,72 @@ describe Route, models: true do
end
end
end
+
+ describe '#create_redirect_for_old_path' do
+ context 'if the path changed' do
+ it 'creates a RedirectRoute for the old path' do
+ redirect_scope = route.source.redirect_routes.where(path: 'git_lab')
+ expect(redirect_scope.exists?).to be_falsey
+ route.path = 'new-path'
+ route.save!
+ expect(redirect_scope.exists?).to be_truthy
+ end
+ end
+ end
+
+ describe '#create_redirect' do
+ it 'creates a RedirectRoute with the same source' do
+ redirect_route = route.create_redirect('foo')
+ expect(redirect_route).to be_a(RedirectRoute)
+ expect(redirect_route).to be_persisted
+ expect(redirect_route.source).to eq(route.source)
+ expect(redirect_route.path).to eq('foo')
+ end
+ end
+
+ describe '#delete_conflicting_redirects' do
+ context 'when a redirect route with the same path exists' do
+ let!(:redirect1) { route.create_redirect(route.path) }
+
+ it 'deletes the redirect' do
+ route.delete_conflicting_redirects
+ expect(route.conflicting_redirects).to be_empty
+ end
+
+ context 'when redirect routes with paths descending from the route path exists' do
+ let!(:redirect2) { route.create_redirect("#{route.path}/foo") }
+ let!(:redirect3) { route.create_redirect("#{route.path}/foo/bar") }
+ let!(:redirect4) { route.create_redirect("#{route.path}/baz/quz") }
+ let!(:other_redirect) { route.create_redirect("other") }
+
+ it 'deletes all redirects with paths that descend from the route path' do
+ route.delete_conflicting_redirects
+ expect(route.conflicting_redirects).to be_empty
+ end
+ end
+ end
+ end
+
+ describe '#conflicting_redirects' do
+ context 'when a redirect route with the same path exists' do
+ let!(:redirect1) { route.create_redirect(route.path) }
+
+ it 'returns the redirect route' do
+ expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
+ expect(route.conflicting_redirects).to match_array([redirect1])
+ end
+
+ context 'when redirect routes with paths descending from the route path exists' do
+ let!(:redirect2) { route.create_redirect("#{route.path}/foo") }
+ let!(:redirect3) { route.create_redirect("#{route.path}/foo/bar") }
+ let!(:redirect4) { route.create_redirect("#{route.path}/baz/quz") }
+ let!(:other_redirect) { route.create_redirect("other") }
+
+ it 'returns the redirect routes' do
+ expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
+ expect(route.conflicting_redirects).to match_array([redirect1, redirect2, redirect3, redirect4])
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 401eaac07a1..63e71f5ff2f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -849,6 +849,65 @@ describe User, models: true do
end
end
+ describe '.find_by_full_path' do
+ let!(:user) { create(:user) }
+
+ context 'with a route matching the given path' do
+ let!(:route) { user.namespace.route }
+
+ it 'returns the user' do
+ expect(User.find_by_full_path(route.path)).to eq(user)
+ end
+
+ it 'is case-insensitive' do
+ expect(User.find_by_full_path(route.path.upcase)).to eq(user)
+ expect(User.find_by_full_path(route.path.downcase)).to eq(user)
+ end
+ end
+
+ context 'with a redirect route matching the given path' do
+ let!(:redirect_route) { user.namespace.redirect_routes.create(path: 'foo') }
+
+ context 'without the follow_redirects option' do
+ it 'returns nil' do
+ expect(User.find_by_full_path(redirect_route.path)).to eq(nil)
+ end
+ end
+
+ context 'with the follow_redirects option set to true' do
+ it 'returns the user' do
+ expect(User.find_by_full_path(redirect_route.path, follow_redirects: true)).to eq(user)
+ end
+
+ it 'is case-insensitive' do
+ expect(User.find_by_full_path(redirect_route.path.upcase, follow_redirects: true)).to eq(user)
+ expect(User.find_by_full_path(redirect_route.path.downcase, follow_redirects: true)).to eq(user)
+ end
+ end
+ end
+
+ context 'without a route or a redirect route matching the given path' do
+ context 'without the follow_redirects option' do
+ it 'returns nil' do
+ expect(User.find_by_full_path('unknown')).to eq(nil)
+ end
+ end
+ context 'with the follow_redirects option set to true' do
+ it 'returns nil' do
+ expect(User.find_by_full_path('unknown', follow_redirects: true)).to eq(nil)
+ end
+ end
+ end
+
+ context 'with a group route matching the given path' do
+ let!(:group) { create(:group, path: 'group_path') }
+
+ it 'returns nil' do
+ expect(User.find_by_full_path('group_path')).to eq(nil)
+ end
+ end
+ end
+
describe 'all_ssh_keys' do
it { is_expected.to have_many(:keys).dependent(:destroy) }
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 163df072cf6..50e96d56191 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'project routing' do
before do
allow(Project).to receive(:find_by_full_path).and_return(false)
- allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq').and_return(true)
+ allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(true)
end
# Shared examples for a resource inside a Project
@@ -93,13 +93,13 @@ describe 'project routing' do
end
context 'name with dot' do
- before { allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys').and_return(true) }
+ before { allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys', any_args).and_return(true) }
it { expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') }
end
context 'with nested group' do
- before { allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq').and_return(true) }
+ before { allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq', any_args).and_return(true) }
it { expect(get('/gitlab/subgroup/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlabhq') }
end