diff options
author | Yorick Peterse <yorick@yorickpeterse.com> | 2019-10-30 15:22:45 +0100 |
---|---|---|
committer | Yorick Peterse <yorick@yorickpeterse.com> | 2019-10-30 15:22:45 +0100 |
commit | ad8eea383406037a207c80421e6e4bfa357f8044 (patch) | |
tree | 396b89ad72b9d7e35fab26c6ee22c978a12defbb /spec | |
parent | 228d752ff09362002cc904d28edee7d63cc3cef2 (diff) | |
parent | b0f939a79fe16ff760d6e589c8f9cd71c0fa1da7 (diff) | |
download | gitlab-ce-ad8eea383406037a207c80421e6e4bfa357f8044.tar.gz |
Merge dev.gitlab.org@master into GitLab.com@master
Diffstat (limited to 'spec')
39 files changed, 959 insertions, 114 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 94afe741057..bb7f5ec2b28 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -186,7 +186,7 @@ describe ApplicationController do expect(response).to have_gitlab_http_status(404) end - it 'redirects to login page via authenticate_user! if not authenticated' do + it 'redirects to login page if not authenticated' do get :index expect(response).to redirect_to new_user_session_path diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb index da68c8c8697..e5e50cfd55e 100644 --- a/spec/controllers/concerns/internal_redirect_spec.rb +++ b/spec/controllers/concerns/internal_redirect_spec.rb @@ -19,7 +19,8 @@ describe InternalRedirect do [ 'Hello world', '//example.com/hello/world', - 'https://example.com/hello/world' + 'https://example.com/hello/world', + "not-starting-with-a-slash\n/starting/with/slash" ] end diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb index cb8c0b8f71c..823b9a50434 100644 --- a/spec/controllers/concerns/lfs_request_spec.rb +++ b/spec/controllers/concerns/lfs_request_spec.rb @@ -16,13 +16,17 @@ describe LfsRequest do end def project - @project ||= Project.find(params[:id]) + @project ||= Project.find_by(id: params[:id]) end def download_request? true end + def upload_request? + false + end + def ci? false end @@ -49,4 +53,41 @@ describe LfsRequest do expect(assigns(:storage_project)).to eq(project) end end + + context 'user is authenticated without access to lfs' do + before do + allow(controller).to receive(:authenticate_user) + allow(controller).to receive(:authentication_result) do + Gitlab::Auth::Result.new + end + end + + context 'with access to the project' do + it 'returns 403' do + get :show, params: { id: project.id } + + expect(response.status).to eq(403) + end + end + + context 'without access to the project' do + context 'project does not exist' do + it 'returns 404' do + get :show, params: { id: 'does not exist' } + + expect(response.status).to eq(404) + end + end + + context 'project is private' do + let(:project) { create(:project, :private) } + + it 'returns 404' do + get :show, params: { id: project.id } + + expect(response.status).to eq(404) + end + end + end + end end diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb index 34765ae3951..fc8fe1ac4f6 100644 --- a/spec/controllers/projects/autocomplete_sources_controller_spec.rb +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -8,6 +8,10 @@ describe Projects::AutocompleteSourcesController do let_it_be(:issue) { create(:issue, project: project) } let_it_be(:user) { create(:user) } + def members_by_username(username) + json_response.find { |member| member['username'] == username } + end + describe 'GET members' do before do group.add_owner(user) @@ -17,22 +21,21 @@ describe Projects::AutocompleteSourcesController do it 'returns an array of member object' do get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } - all = json_response.find {|member| member["username"] == 'all'} - the_group = json_response.find {|member| member["username"] == group.full_path} - the_user = json_response.find {|member| member["username"] == user.username} - - expect(all.symbolize_keys).to include(username: 'all', - name: 'All Project and Group Members', - count: 1) - - expect(the_group.symbolize_keys).to include(type: group.class.name, - name: group.full_name, - avatar_url: group.avatar_url, - count: 1) - - expect(the_user.symbolize_keys).to include(type: user.class.name, - name: user.name, - avatar_url: user.avatar_url) + expect(members_by_username('all').symbolize_keys).to include( + username: 'all', + name: 'All Project and Group Members', + count: 1) + + expect(members_by_username(group.full_path).symbolize_keys).to include( + type: group.class.name, + name: group.full_name, + avatar_url: group.avatar_url, + count: 1) + + expect(members_by_username(user.username).symbolize_keys).to include( + type: user.class.name, + name: user.name, + avatar_url: user.avatar_url) end end diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 9c4d6fdcb2a..1977e92e42b 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -142,7 +142,7 @@ describe Projects::CommitsController do context 'token authentication' do context 'public project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do + it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do before do public_project = create(:project, :repository, :public) @@ -152,7 +152,7 @@ describe Projects::CommitsController do end context 'private project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, public: false do + it_behaves_like 'authenticates sessionless user', :show, :atom, { public: false, ignore_incrementing: true } do before do private_project = create(:project, :repository, :private) private_project.add_maintainer(user) diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb index 4c224e960a6..31868f5f717 100644 --- a/spec/controllers/projects/error_tracking_controller_spec.rb +++ b/spec/controllers/projects/error_tracking_controller_spec.rb @@ -146,7 +146,7 @@ describe Projects::ErrorTrackingController do it 'redirects to sign-in page' do post :list_projects, params: list_projects_params - expect(response).to have_gitlab_http_status(:unauthorized) + expect(response).to have_gitlab_http_status(:redirect) end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 4c2b58551bf..8770a5ee303 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1441,7 +1441,7 @@ describe Projects::IssuesController do context 'private project with token authentication' do let(:private_project) { create(:project, :private) } - it_behaves_like 'authenticates sessionless user', :index, :atom do + it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do before do default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) @@ -1449,7 +1449,7 @@ describe Projects::IssuesController do end end - it_behaves_like 'authenticates sessionless user', :calendar, :ics do + it_behaves_like 'authenticates sessionless user', :calendar, :ics, ignore_incrementing: true do before do default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index b99b5d611fc..f077b4c99fc 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -41,7 +41,7 @@ describe Projects::TagsController do context 'private project with token authentication' do let(:private_project) { create(:project, :repository, :private) } - it_behaves_like 'authenticates sessionless user', :index, :atom do + it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do before do default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 321f5ecdbc9..22538565698 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1149,7 +1149,7 @@ describe ProjectsController do context 'private project with token authentication' do let(:private_project) { create(:project, :private) } - it_behaves_like 'authenticates sessionless user', :show, :atom do + it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do before do default_params.merge!(id: private_project, namespace_id: private_project.namespace) diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index a9a127da56f..f6eeb8d7065 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -819,7 +819,10 @@ describe 'Pipelines', :js do context 'when project is private' do let(:project) { create(:project, :private, :repository) } - it { expect(page).to have_content 'You need to sign in' } + it 'redirects the user to sign_in and displays the flash alert' do + expect(page).to have_content 'You need to sign in' + expect(page.current_path).to eq("/users/sign_in") + end end end diff --git a/spec/features/projects/tags/user_views_tags_spec.rb b/spec/features/projects/tags/user_views_tags_spec.rb index f344b682715..bc570f502bf 100644 --- a/spec/features/projects/tags/user_views_tags_spec.rb +++ b/spec/features/projects/tags/user_views_tags_spec.rb @@ -15,7 +15,7 @@ describe 'User views tags', :feature do it do visit project_tags_path(project, format: :atom) - expect(page).to have_gitlab_http_status(401) + expect(page.current_path).to eq("/users/sign_in") end end diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 2681f098fec..7590c399cf9 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -128,6 +128,89 @@ describe LabelsFinder do expect(finder.execute).to eq [private_subgroup_label_1] end end + + context 'when including labels from group projects with limited visibility' do + let(:finder) { described_class.new(user, group_id: group_4.id) } + let(:group_4) { create(:group) } + let(:limited_visibility_project) { create(:project, :public, group: group_4) } + let(:visible_project) { create(:project, :public, group: group_4) } + let!(:group_label_1) { create(:group_label, group: group_4) } + let!(:limited_visibility_label) { create(:label, project: limited_visibility_project) } + let!(:visible_label) { create(:label, project: visible_project) } + + shared_examples 'with full visibility' do + it 'returns all projects labels' do + expect(finder.execute).to eq [group_label_1, limited_visibility_label, visible_label] + end + end + + shared_examples 'with limited visibility' do + it 'returns only authorized projects labels' do + expect(finder.execute).to eq [group_label_1, visible_label] + end + end + + context 'when merge requests and issues are not visible for non members' do + before do + limited_visibility_project.project_feature.update!( + merge_requests_access_level: ProjectFeature::PRIVATE, + issues_access_level: ProjectFeature::PRIVATE + ) + end + + context 'when user is not a group member' do + it_behaves_like 'with limited visibility' + end + + context 'when user is a group member' do + before do + group_4.add_developer(user) + end + + it_behaves_like 'with full visibility' + end + end + + context 'when merge requests are not visible for non members' do + before do + limited_visibility_project.project_feature.update!( + merge_requests_access_level: ProjectFeature::PRIVATE + ) + end + + context 'when user is not a group member' do + it_behaves_like 'with full visibility' + end + + context 'when user is a group member' do + before do + group_4.add_developer(user) + end + + it_behaves_like 'with full visibility' + end + end + + context 'when issues are not visible for non members' do + before do + limited_visibility_project.project_feature.update!( + issues_access_level: ProjectFeature::PRIVATE + ) + end + + context 'when user is not a group member' do + it_behaves_like 'with full visibility' + end + + context 'when user is a group member' do + before do + group_4.add_developer(user) + end + + it_behaves_like 'with full visibility' + end + end + end end context 'filtering by project_id' do diff --git a/spec/fixtures/api/graphql/recursive-introspection.graphql b/spec/fixtures/api/graphql/recursive-introspection.graphql new file mode 100644 index 00000000000..db970bb14b6 --- /dev/null +++ b/spec/fixtures/api/graphql/recursive-introspection.graphql @@ -0,0 +1,57 @@ +query allSchemaTypes { + __schema { + types { + fields { + type{ + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/spec/fixtures/api/graphql/recursive-query.graphql b/spec/fixtures/api/graphql/recursive-query.graphql new file mode 100644 index 00000000000..d1616c4de6e --- /dev/null +++ b/spec/fixtures/api/graphql/recursive-query.graphql @@ -0,0 +1,47 @@ +{ + project(fullPath: "gitlab-org/gitlab-ce") { + group { + projects { + edges { + node { + group { + projects { + edges { + node { + group { + projects { + edges { + node { + group { + projects { + edges { + node { + group { + projects { + edges { + node { + group { + description + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/spec/fixtures/api/graphql/small-recursive-introspection.graphql b/spec/fixtures/api/graphql/small-recursive-introspection.graphql new file mode 100644 index 00000000000..1025043b474 --- /dev/null +++ b/spec/fixtures/api/graphql/small-recursive-introspection.graphql @@ -0,0 +1,15 @@ +query allSchemaTypes { + __schema { + types { + fields { + type { + fields { + type { + name + } + } + } + } + } + } +} diff --git a/spec/frontend/project_find_file_spec.js b/spec/frontend/project_find_file_spec.js index 8102033139f..e60f9f62747 100644 --- a/spec/frontend/project_find_file_spec.js +++ b/spec/frontend/project_find_file_spec.js @@ -3,6 +3,9 @@ import $ from 'jquery'; import ProjectFindFile from '~/project_find_file'; import axios from '~/lib/utils/axios_utils'; import { TEST_HOST } from 'helpers/test_constants'; +import sanitize from 'sanitize-html'; + +jest.mock('sanitize-html', () => jest.fn(val => val)); const BLOB_URL_TEMPLATE = `${TEST_HOST}/namespace/project/blob/master`; const FILE_FIND_URL = `${TEST_HOST}/namespace/project/files/master?format=json`; @@ -38,31 +41,31 @@ describe('ProjectFindFile', () => { href: el.querySelector('a').href, })); + const files = [ + 'fileA.txt', + 'fileB.txt', + 'fi#leC.txt', + 'folderA/fileD.txt', + 'folder#B/fileE.txt', + 'folde?rC/fil#F.txt', + ]; + beforeEach(() => { // Create a mock adapter for stubbing axios API requests mock = new MockAdapter(axios); element = $(TEMPLATE); + mock.onGet(FILE_FIND_URL).replyOnce(200, files); + getProjectFindFileInstance(); // This triggers a load / axios call + subsequent render in the constructor }); afterEach(() => { // Reset the mock adapter mock.restore(); + sanitize.mockClear(); }); it('loads and renders elements from remote server', done => { - const files = [ - 'fileA.txt', - 'fileB.txt', - 'fi#leC.txt', - 'folderA/fileD.txt', - 'folder#B/fileE.txt', - 'folde?rC/fil#F.txt', - ]; - mock.onGet(FILE_FIND_URL).replyOnce(200, files); - - getProjectFindFileInstance(); // This triggers a load / axios call + subsequent render in the constructor - setImmediate(() => { expect(findFiles()).toEqual( files.map(text => ({ @@ -74,4 +77,14 @@ describe('ProjectFindFile', () => { done(); }); }); + + it('sanitizes search text', done => { + const searchText = element.find('.file-finder-input').val(); + + setImmediate(() => { + expect(sanitize).toHaveBeenCalledTimes(1); + expect(sanitize).toHaveBeenCalledWith(searchText); + done(); + }); + }); }); diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 364f215420a..32851249b2e 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -3,18 +3,18 @@ require 'spec_helper' describe MarkupHelper do - let!(:project) { create(:project, :repository) } - - let(:user) { create(:user, username: 'gfm') } - let(:commit) { project.commit } - let(:issue) { create(:issue, project: project) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:snippet) { create(:project_snippet, project: project) } - - before do - # Ensure the generated reference links aren't redacted + set(:project) { create(:project, :repository) } + set(:user) do + user = create(:user, username: 'gfm') project.add_maintainer(user) + user + end + set(:issue) { create(:issue, project: project) } + set(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + set(:snippet) { create(:project_snippet, project: project) } + let(:commit) { project.commit } + before do # Helper expects a @project instance variable helper.instance_variable_set(:@project, project) @@ -44,8 +44,8 @@ describe MarkupHelper do describe "override default project" do let(:actual) { issue.to_reference } - let(:second_project) { create(:project, :public) } - let(:second_issue) { create(:issue, project: second_project) } + set(:second_project) { create(:project, :public) } + set(:second_issue) { create(:issue, project: second_project) } it 'links to the issue' do expected = urls.project_issue_path(second_project, second_issue) @@ -55,7 +55,7 @@ describe MarkupHelper do describe 'uploads' do let(:text) { "![ImageTest](/uploads/test.png)" } - let(:group) { create(:group) } + set(:group) { create(:group) } subject { helper.markdown(text) } @@ -77,7 +77,7 @@ describe MarkupHelper do end describe "with a group in the context" do - let(:project_in_group) { create(:project, group: group) } + set(:project_in_group) { create(:project, group: group) } before do helper.instance_variable_set(:@group, group) @@ -235,38 +235,48 @@ describe MarkupHelper do end describe '#render_wiki_content' do + let(:wiki) { double('WikiPage', path: "file.#{extension}") } + let(:context) do + { + pipeline: :wiki, project: project, project_wiki: wiki, + page_slug: 'nested/page', issuable_state_filter_enabled: true + } + end + before do - @wiki = double('WikiPage') - allow(@wiki).to receive(:content).and_return('wiki content') - allow(@wiki).to receive(:slug).and_return('nested/page') - helper.instance_variable_set(:@project_wiki, @wiki) + expect(wiki).to receive(:content).and_return('wiki content') + expect(wiki).to receive(:slug).and_return('nested/page') + helper.instance_variable_set(:@project_wiki, wiki) end - it "uses Wiki pipeline for markdown files" do - allow(@wiki).to receive(:format).and_return(:markdown) + context 'when file is Markdown' do + let(:extension) { 'md' } - expect(helper).to receive(:markdown_unsafe).with('wiki content', - pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", - issuable_state_filter_enabled: true) + it 'renders using #markdown_unsafe helper method' do + expect(helper).to receive(:markdown_unsafe).with('wiki content', context) - helper.render_wiki_content(@wiki) + helper.render_wiki_content(wiki) + end end - it "uses Asciidoctor for asciidoc files" do - allow(@wiki).to receive(:format).and_return(:asciidoc) + context 'when file is Asciidoc' do + let(:extension) { 'adoc' } - expect(helper).to receive(:asciidoc_unsafe).with('wiki content') + it 'renders using Gitlab::Asciidoc' do + expect(Gitlab::Asciidoc).to receive(:render) - helper.render_wiki_content(@wiki) + helper.render_wiki_content(wiki) + end end - it "uses the Gollum renderer for all other file types" do - allow(@wiki).to receive(:format).and_return(:rdoc) - formatted_content_stub = double('formatted_content') - expect(formatted_content_stub).to receive(:html_safe) - allow(@wiki).to receive(:formatted_content).and_return(formatted_content_stub) + context 'any other format' do + let(:extension) { 'foo' } - helper.render_wiki_content(@wiki) + it 'renders all other formats using Gitlab::OtherMarkup' do + expect(Gitlab::OtherMarkup).to receive(:render) + + helper.render_wiki_content(wiki) + end end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 2ecbe548520..120ba67f328 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -227,6 +227,14 @@ describe Milestone do end end + describe '#to_ability_name' do + it 'returns milestone' do + milestone = build(:milestone) + + expect(milestone.to_ability_name).to eq('milestone') + end + end + describe '.search' do let(:milestone) { create(:milestone, title: 'foo', description: 'bar') } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 4c320b4b145..3ab88b52568 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -379,6 +379,63 @@ describe Note do expect(label_note.cross_reference?).to be_falsy end end + + context 'when system note metadata is not present' do + let(:note) { build(:note, :system) } + + before do + allow(note).to receive(:system_note_metadata).and_return(nil) + end + + it 'delegates to the system note service' do + expect(SystemNotes::IssuablesService).to receive(:cross_reference?).with(note.note) + + note.cross_reference? + end + end + + context 'with a system note' do + let(:issue) { create(:issue, project: create(:project, :repository)) } + let(:note) { create(:system_note, note: "test", noteable: issue, project: issue.project) } + + shared_examples 'system_note_metadata includes note action' do + it 'delegates to the cross-reference regex' do + expect(note).to receive(:matches_cross_reference_regex?) + + note.cross_reference? + end + end + + context 'with :label action' do + let!(:metadata) {create(:system_note_metadata, note: note, action: :label)} + + it_behaves_like 'system_note_metadata includes note action' + + it { expect(note.cross_reference?).to be_falsy } + + context 'with cross reference label note' do + let(:label) { create(:label, project: issue.project)} + let(:note) { create(:system_note, note: "added #{label.to_reference} label", noteable: issue, project: issue.project) } + + it { expect(note.cross_reference?).to be_truthy } + end + end + + context 'with :milestone action' do + let!(:metadata) {create(:system_note_metadata, note: note, action: :milestone)} + + it_behaves_like 'system_note_metadata includes note action' + + it { expect(note.cross_reference?).to be_falsy } + + context 'with cross reference milestone note' do + let(:milestone) { create(:milestone, project: issue.project)} + let(:note) { create(:system_note, note: "added #{milestone.to_reference} milestone", noteable: issue, project: issue.project) } + + it { expect(note.cross_reference?).to be_truthy } + end + end + end end describe 'clear_blank_line_code!' do @@ -578,24 +635,30 @@ describe Note do end describe '#to_ability_name' do - it 'returns snippet for a project snippet note' do - expect(build(:note_on_project_snippet).to_ability_name).to eq('project_snippet') + it 'returns note' do + expect(build(:note).to_ability_name).to eq('note') + end + end + + describe '#noteable_ability_name' do + it 'returns project_snippet for a project snippet note' do + expect(build(:note_on_project_snippet).noteable_ability_name).to eq('project_snippet') end it 'returns personal_snippet for a personal snippet note' do - expect(build(:note_on_personal_snippet).to_ability_name).to eq('personal_snippet') + expect(build(:note_on_personal_snippet).noteable_ability_name).to eq('personal_snippet') end it 'returns merge_request for an MR note' do - expect(build(:note_on_merge_request).to_ability_name).to eq('merge_request') + expect(build(:note_on_merge_request).noteable_ability_name).to eq('merge_request') end it 'returns issue for an issue note' do - expect(build(:note_on_issue).to_ability_name).to eq('issue') + expect(build(:note_on_issue).noteable_ability_name).to eq('issue') end - it 'returns issue for a commit note' do - expect(build(:note_on_commit).to_ability_name).to eq('commit') + it 'returns commit for a commit note' do + expect(build(:note_on_commit).noteable_ability_name).to eq('commit') end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5922a6f36f5..71b999048b4 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3416,7 +3416,7 @@ describe Project do end end - describe '.ids_with_milestone_available_for' do + describe '.ids_with_issuables_available_for' do let!(:user) { create(:user) } it 'returns project ids with milestones available for user' do @@ -3426,7 +3426,7 @@ describe Project do project_4 = create(:project, :public) project_4.project_feature.update(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE ) - project_ids = described_class.ids_with_milestone_available_for(user).pluck(:id) + project_ids = described_class.ids_with_issuables_available_for(user).pluck(:id) expect(project_ids).to include(project_2.id, project_3.id) expect(project_ids).not_to include(project_1.id, project_4.id) @@ -4444,6 +4444,14 @@ describe Project do end end + describe '#to_ability_name' do + it 'returns project' do + project = build(:project_empty_repo) + + expect(project.to_ability_name).to eq('project') + end + end + describe '#execute_hooks' do let(:data) { { ref: 'refs/heads/master', data: 'data' } } it 'executes active projects hooks with the specified scope' do diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 8f3730f97ac..38415613b9e 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -358,6 +358,23 @@ describe WikiPage do end end + describe '#path' do + let(:path) { 'mypath.md' } + let(:wiki_page) { instance_double('Gitlab::Git::WikiPage', path: path).as_null_object } + + it 'returns the path when persisted' do + page = described_class.new(wiki, wiki_page, true) + + expect(page.path).to eq(path) + end + + it 'returns nil when not persisted' do + page = described_class.new(wiki, wiki_page, false) + + expect(page.path).to be_nil + end + end + describe '#directory' do context 'when the page is at the root directory' do it 'returns an empty string' do diff --git a/spec/policies/commit_policy_spec.rb b/spec/policies/commit_policy_spec.rb index 41f6fb08426..40183f51e9e 100644 --- a/spec/policies/commit_policy_spec.rb +++ b/spec/policies/commit_policy_spec.rb @@ -8,28 +8,42 @@ describe CommitPolicy do let(:commit) { project.repository.head_commit } let(:policy) { described_class.new(user, commit) } + shared_examples 'can read commit and create a note' do + it 'can read commit' do + expect(policy).to be_allowed(:read_commit) + end + + it 'can create a note' do + expect(policy).to be_allowed(:create_note) + end + end + + shared_examples 'cannot read commit nor create a note' do + it 'can not read commit' do + expect(policy).to be_disallowed(:read_commit) + end + + it 'can not create a note' do + expect(policy).to be_disallowed(:create_note) + end + end + context 'when project is public' do let(:project) { create(:project, :public, :repository) } - it 'can read commit and create a note' do - expect(policy).to be_allowed(:read_commit) - end + it_behaves_like 'can read commit and create a note' context 'when repository access level is private' do let(:project) { create(:project, :public, :repository, :repository_private) } - it 'can not read commit and create a note' do - expect(policy).to be_disallowed(:read_commit) - end + it_behaves_like 'cannot read commit nor create a note' context 'when the user is a project member' do before do project.add_developer(user) end - it 'can read commit and create a note' do - expect(policy).to be_allowed(:read_commit) - end + it_behaves_like 'can read commit and create a note' end end end @@ -37,9 +51,7 @@ describe CommitPolicy do context 'when project is private' do let(:project) { create(:project, :private, :repository) } - it 'can not read commit and create a note' do - expect(policy).to be_disallowed(:read_commit) - end + it_behaves_like 'cannot read commit nor create a note' context 'when the user is a project member' do before do @@ -50,6 +62,18 @@ describe CommitPolicy do expect(policy).to be_allowed(:read_commit) end end + + context 'when the user is a guest' do + before do + project.add_guest(user) + end + + it_behaves_like 'cannot read commit nor create a note' + + it 'cannot download code' do + expect(policy).to be_disallowed(:download_code) + end + end end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 9634a591592..ae9d125f970 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -356,6 +356,88 @@ describe GroupPolicy do end end + context 'transfer_projects' do + shared_examples_for 'allowed to transfer projects' do + before do + group.update(project_creation_level: project_creation_level) + end + + it { is_expected.to be_allowed(:transfer_projects) } + end + + shared_examples_for 'not allowed to transfer projects' do + before do + group.update(project_creation_level: project_creation_level) + end + + it { is_expected.to be_disallowed(:transfer_projects) } + end + + context 'reporter' do + let(:current_user) { reporter } + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } + end + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } + end + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS } + end + end + + context 'developer' do + let(:current_user) { developer } + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } + end + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } + end + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS } + end + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } + end + + it_behaves_like 'allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } + end + + it_behaves_like 'allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS } + end + end + + context 'owner' do + let(:current_user) { owner } + + it_behaves_like 'not allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } + end + + it_behaves_like 'allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } + end + + it_behaves_like 'allowed to transfer projects' do + let(:project_creation_level) { ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS } + end + end + end + context "create_projects" do context 'when group has no project creation level set' do before_all do diff --git a/spec/policies/namespace_policy_spec.rb b/spec/policies/namespace_policy_spec.rb index ece0519112a..c0a5119c550 100644 --- a/spec/policies/namespace_policy_spec.rb +++ b/spec/policies/namespace_policy_spec.rb @@ -8,7 +8,7 @@ describe NamespacePolicy do let(:admin) { create(:admin) } let(:namespace) { create(:namespace, owner: owner) } - let(:owner_permissions) { [:create_projects, :admin_namespace, :read_namespace, :read_statistics] } + let(:owner_permissions) { [:create_projects, :admin_namespace, :read_namespace, :read_statistics, :transfer_projects] } subject { described_class.new(current_user, namespace) } @@ -33,6 +33,7 @@ describe NamespacePolicy do let(:owner) { create(:user, projects_limit: 0) } it { is_expected.to be_disallowed(:create_projects) } + it { is_expected.to be_disallowed(:transfer_projects) } end end diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb index d02eccedf32..2aeb75a10b4 100644 --- a/spec/requests/api/graphql/gitlab_schema_spec.rb +++ b/spec/requests/api/graphql/gitlab_schema_spec.rb @@ -15,7 +15,7 @@ describe 'GitlabSchema configurations' do subject - expect(graphql_errors.flatten.first['message']).to include('which exceeds max complexity of 1') + expect_graphql_errors_to_include /which exceeds max complexity of 1/ end end end @@ -23,12 +23,11 @@ describe 'GitlabSchema configurations' do describe '#max_depth' do context 'when query depth is too high' do it 'shows error' do - errors = { "message" => "Query has depth of 2, which exceeds max depth of 1" } allow(GitlabSchema).to receive(:max_query_depth).and_return 1 subject - expect(graphql_errors.flatten).to include(errors) + expect_graphql_errors_to_include /exceeds max depth/ end end @@ -38,7 +37,42 @@ describe 'GitlabSchema configurations' do subject - expect(Array.wrap(graphql_errors).compact).to be_empty + expect_graphql_errors_to_be_empty + end + end + end + end + + context 'depth, complexity and recursion checking' do + context 'unauthenticated recursive queries' do + context 'a not-quite-recursive-enough introspective query' do + it 'succeeds' do + query = File.read(Rails.root.join('spec/fixtures/api/graphql/small-recursive-introspection.graphql')) + + post_graphql(query, current_user: nil) + + expect_graphql_errors_to_be_empty + end + end + + context 'a deep but simple recursive introspective query' do + it 'fails due to recursion' do + query = File.read(Rails.root.join('spec/fixtures/api/graphql/recursive-introspection.graphql')) + + post_graphql(query, current_user: nil) + + expect_graphql_errors_to_include [/Recursive query/] + end + end + + context 'a deep recursive non-introspective query' do + it 'fails due to recursion, complexity and depth' do + allow(GitlabSchema).to receive(:max_query_complexity).and_return 1 + query = File.read(Rails.root.join('spec/fixtures/api/graphql/recursive-query.graphql')) + + post_graphql(query, current_user: nil) + + expect_graphql_errors_to_include [/Recursive query/, /exceeds max complexity/, /exceeds max depth/] end end end @@ -88,7 +122,7 @@ describe 'GitlabSchema configurations' do # Expect errors for each query expect(graphql_errors.size).to eq(3) graphql_errors.each do |single_query_errors| - expect(single_query_errors.first['message']).to include('which exceeds max complexity of 4') + expect_graphql_errors_to_include(/which exceeds max complexity of 4/) end end end @@ -105,12 +139,12 @@ describe 'GitlabSchema configurations' do end context 'when IntrospectionQuery' do - it 'is not too complex' do + it 'is not too complex nor recursive' do query = File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql')) post_graphql(query, current_user: nil) - expect(graphql_errors).to be_nil + expect_graphql_errors_to_be_empty end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 8063004549e..c622add7f8f 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1741,6 +1741,38 @@ describe API::MergeRequests do expect(json_response['state']).to eq('closed') expect(json_response['force_remove_source_branch']).to be_truthy end + + context 'with a merge request across forks' do + let(:fork_owner) { create(:user) } + let(:source_project) { fork_project(project, fork_owner) } + let(:target_project) { project } + + let(:merge_request) do + create(:merge_request, + source_project: source_project, + target_project: target_project, + source_branch: 'fixes', + merge_params: { 'force_remove_source_branch' => false }) + end + + it 'is true for an authorized user' do + put api("/projects/#{target_project.id}/merge_requests/#{merge_request.iid}", fork_owner), params: { state_event: 'close', remove_source_branch: true } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['state']).to eq('closed') + expect(json_response['force_remove_source_branch']).to be true + end + + it 'is false for an unauthorized user' do + expect do + put api("/projects/#{target_project.id}/merge_requests/#{merge_request.iid}", target_project.owner), params: { state_event: 'close', remove_source_branch: true } + end.not_to change { merge_request.reload.merge_params } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['state']).to eq('closed') + expect(json_response['force_remove_source_branch']).to be false + end + end end context "to close a MR" do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 585bf57fa4c..ffac7872464 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -2684,6 +2684,22 @@ describe API::Projects do expect(response).to have_gitlab_http_status(400) end end + + context 'when authenticated as developer' do + before do + group.add_developer(user) + end + + context 'target namespace allows developers to create projects' do + let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } + + it 'fails transferring the project to the target namespace' do + put api("/projects/#{project.id}/transfer", user), params: { namespace: group.id } + + expect(response).to have_gitlab_http_status(400) + end + end + end end it_behaves_like 'custom attributes endpoints', 'projects' do diff --git a/spec/services/auto_merge/base_service_spec.rb b/spec/services/auto_merge/base_service_spec.rb index a409f21a7e4..0a6bcb1badc 100644 --- a/spec/services/auto_merge/base_service_spec.rb +++ b/spec/services/auto_merge/base_service_spec.rb @@ -51,7 +51,7 @@ describe AutoMerge::BaseService do expect(merge_request.merge_params['commit_message']).to eq("Merge branch 'patch-12' into 'master'") expect(merge_request.merge_params['sha']).to eq('200fcc9c260f7219eaf0daba87d818f0922c5b18') expect(merge_request.merge_params['should_remove_source_branch']).to eq(false) - expect(merge_request.merge_params['squash']).to eq(false) + expect(merge_request.squash).to eq(false) expect(merge_request.merge_params['squash_commit_message']).to eq('Update README.md') end end @@ -108,7 +108,6 @@ describe AutoMerge::BaseService do 'commit_message' => "Merge branch 'patch-12' into 'master'", 'sha' => "200fcc9c260f7219eaf0daba87d818f0922c5b18", 'should_remove_source_branch' => false, - 'squash' => false, 'squash_commit_message' => "Update README.md" } end diff --git a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb index c396539cf56..1bebeacb4d4 100644 --- a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb +++ b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb @@ -59,8 +59,9 @@ describe AutoMerge::MergeWhenPipelineSucceedsService do it 'sets the params, merge_user, and flag' do expect(merge_request).to be_valid expect(merge_request.merge_when_pipeline_succeeds).to be_truthy - expect(merge_request.merge_params).to include commit_message: 'Awesome message' + expect(merge_request.merge_params).to include 'commit_message' => 'Awesome message' expect(merge_request.merge_user).to be user + expect(merge_request.auto_merge_strategy).to eq AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS end it 'creates a system note' do @@ -73,7 +74,7 @@ describe AutoMerge::MergeWhenPipelineSucceedsService do end context 'already approved' do - let(:service) { described_class.new(project, user, new_key: true) } + let(:service) { described_class.new(project, user, should_remove_source_branch: true) } let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) } before do @@ -90,7 +91,7 @@ describe AutoMerge::MergeWhenPipelineSucceedsService do expect(SystemNoteService).not_to receive(:merge_when_pipeline_succeeds) service.execute(mr_merge_if_green_enabled) - expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key) + expect(mr_merge_if_green_enabled.merge_params).to have_key('should_remove_source_branch') end end end diff --git a/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb b/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb new file mode 100644 index 00000000000..5b653aa331c --- /dev/null +++ b/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe MergeRequests::AssignsMergeParams do + it 'raises an error when used from an instance that does not respond to #current_user' do + define_class = -> { Class.new { include MergeRequests::AssignsMergeParams }.new } + + expect { define_class.call }.to raise_error %r{can not be included in (.*) without implementing #current_user} + end + + describe '#assign_allowed_merge_params' do + let(:merge_request) { build(:merge_request) } + + let(:params) do + { commit_message: 'Commit Message', + 'should_remove_source_branch' => true, + unknown_symbol: 'Unknown symbol', + 'unknown_string' => 'Unknown String' } + end + + subject(:merge_request_service) do + Class.new do + attr_accessor :current_user + + include MergeRequests::AssignsMergeParams + + def initialize(user) + @current_user = user + end + end + end + + it 'only assigns known parameters to the merge request' do + service = merge_request_service.new(merge_request.author) + + service.assign_allowed_merge_params(merge_request, params) + + expect(merge_request.merge_params).to eq('commit_message' => 'Commit Message', 'should_remove_source_branch' => true) + end + + it 'returns a hash without the known merge params' do + service = merge_request_service.new(merge_request.author) + + result = service.assign_allowed_merge_params(merge_request, params) + + expect(result).to eq({ 'unknown_symbol' => 'Unknown symbol', 'unknown_string' => 'Unknown String' }) + end + + context 'the force_remove_source_branch param' do + let(:params) { { force_remove_source_branch: true } } + + it 'assigns the param if the user is allowed to do that' do + service = merge_request_service.new(merge_request.author) + + result = service.assign_allowed_merge_params(merge_request, params) + + expect(merge_request.force_remove_source_branch?).to be true + expect(result).to be_empty + end + + it 'only removes the param if the user is not allowed to do that' do + service = merge_request_service.new(build(:user)) + + result = service.assign_allowed_merge_params(merge_request, params) + + expect(merge_request.force_remove_source_branch?).to be_falsy + expect(result).to be_empty + end + end + end +end diff --git a/spec/services/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb index 730fccc599e..a272a604184 100644 --- a/spec/services/error_tracking/list_projects_service_spec.rb +++ b/spec/services/error_tracking/list_projects_service_spec.rb @@ -50,6 +50,19 @@ describe ErrorTracking::ListProjectsService do end end + context 'masked param token' do + let(:params) { ActionController::Parameters.new(token: "*********", api_host: new_api_host) } + + before do + expect(error_tracking_setting).to receive(:list_sentry_projects) + .and_return({ projects: [] }) + end + + it 'uses database token' do + expect { subject.execute }.not_to change { error_tracking_setting.token } + end + end + context 'sentry client raises exception' do context 'Sentry::Client::Error' do before do diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 46e86d5b4cb..9b358839c06 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -83,8 +83,9 @@ describe MergeRequests::BuildService do expect(merge_request.force_remove_source_branch?).to be_truthy end - context 'with force_remove_source_branch parameter' do + context 'with force_remove_source_branch parameter when the user is authorized' do let(:mr_params) { params.merge(force_remove_source_branch: '1') } + let(:source_project) { fork_project(project, user) } let(:merge_request) { described_class.new(project, user, mr_params).execute } it 'assigns force_remove_source_branch' do diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 8c796475de0..741420d76a7 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -646,5 +646,29 @@ describe MergeRequests::UpdateService, :mailer do expect(merge_request.allow_collaboration).to be_truthy end end + + context 'updating `force_remove_source_branch`' do + let(:target_project) { create(:project, :repository, :public) } + let(:source_project) { fork_project(target_project, nil, repository: true) } + let(:user) { target_project.owner } + let(:merge_request) do + create(:merge_request, + source_project: source_project, + source_branch: 'fixes', + target_project: target_project) + end + + it "cannot be done by members of the target project when they don't have access" do + expect { update_merge_request(force_remove_source_branch: true) } + .not_to change { merge_request.reload.force_remove_source_branch? }.from(nil) + end + + it 'can be done by members of the target project if they can push to the source project' do + source_project.add_developer(user) + + expect { update_merge_request(force_remove_source_branch: true) } + .to change { merge_request.reload.force_remove_source_branch? }.from(nil).to(true) + end + end end end diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb index b2f9fd6df79..81d59a98b9b 100644 --- a/spec/services/projects/operations/update_service_spec.rb +++ b/spec/services/projects/operations/update_service_spec.rb @@ -145,6 +145,27 @@ describe Projects::Operations::UpdateService do end end + context 'with masked param token' do + let(:params) do + { + error_tracking_setting_attributes: { + enabled: false, + token: '*' * 8 + } + } + end + + before do + create(:project_error_tracking_setting, project: project, token: 'token') + end + + it 'does not update token' do + expect(result[:status]).to eq(:success) + + expect(project.error_tracking_setting.token).to eq('token') + end + end + context 'with invalid parameters' do let(:params) { {} } diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb index 4def83513a4..239d28557ee 100644 --- a/spec/services/projects/participants_service_spec.rb +++ b/spec/services/projects/participants_service_spec.rb @@ -57,4 +57,108 @@ describe Projects::ParticipantsService do end end end + + describe '#project_members' do + subject(:usernames) { service.project_members.map { |member| member[:username] } } + + context 'when there is a project in group namespace' do + set(:public_group) { create(:group, :public) } + set(:public_project) { create(:project, :public, namespace: public_group)} + + set(:public_group_owner) { create(:user) } + + let(:service) { described_class.new(public_project, create(:user)) } + + before do + public_group.add_owner(public_group_owner) + end + + it 'returns members of a group' do + expect(usernames).to include(public_group_owner.username) + end + end + + context 'when there is a private group and a public project' do + set(:public_group) { create(:group, :public) } + set(:private_group) { create(:group, :private, :nested) } + set(:public_project) { create(:project, :public, namespace: public_group)} + + set(:project_issue) { create(:issue, project: public_project)} + + set(:public_group_owner) { create(:user) } + set(:private_group_member) { create(:user) } + set(:public_project_maintainer) { create(:user) } + set(:private_group_owner) { create(:user) } + + set(:group_ancestor_owner) { create(:user) } + + before(:context) do + public_group.add_owner public_group_owner + private_group.add_developer private_group_member + public_project.add_maintainer public_project_maintainer + + private_group.add_owner private_group_owner + private_group.parent.add_owner group_ancestor_owner + end + + context 'when the private group is invited to the public project' do + before(:context) do + create(:project_group_link, group: private_group, project: public_project) + end + + context 'when a user who is outside the public project and the private group is signed in' do + let(:service) { described_class.new(public_project, create(:user)) } + + it 'does not return the private group' do + expect(usernames).not_to include(private_group.name) + end + + it 'does not return private group members' do + expect(usernames).not_to include(private_group_member.username) + end + + it 'returns the project maintainer' do + expect(usernames).to include(public_project_maintainer.username) + end + + it 'returns project members from an invited public group' do + invited_public_group = create(:group, :public) + invited_public_group.add_owner create(:user) + + create(:project_group_link, group: invited_public_group, project: public_project) + + expect(usernames).to include(invited_public_group.users.first.username) + end + + it 'does not return ancestors of the private group' do + expect(usernames).not_to include(group_ancestor_owner.username) + end + end + + context 'when private group owner is signed in' do + let(:service) { described_class.new(public_project, private_group_owner) } + + it 'returns private group members' do + expect(usernames).to include(private_group_member.username) + end + + it 'returns ancestors of the the private group' do + expect(usernames).to include(group_ancestor_owner.username) + end + end + + context 'when the namespace owner of the public project is signed in' do + let(:service) { described_class.new(public_project, public_group_owner) } + + it 'returns private group members' do + expect(usernames).to include(private_group_member.username) + end + + it 'does not return members of the ancestral groups of the private group' do + expect(usernames).to include(group_ancestor_owner.username) + end + end + end + end + end end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 26d8ac9b479..298867f483b 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -222,6 +222,24 @@ describe Projects::TransferService do it { expect(project.errors[:new_namespace]).to include('Project with same name or path in target namespace already exists') } end + context 'target namespace allows developers to create projects' do + let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } + + context 'the user is a member of the target namespace with developer permissions' do + subject(:transfer_project_result) { transfer_project(project, user, group) } + + before do + group.add_developer(user) + end + + it 'does not allow project transfer to the target namespace' do + expect(transfer_project_result).to eq false + expect(project.namespace).to eq(user.namespace) + expect(project.errors[:new_namespace]).to include('Transfer failed, please contact an admin.') + end + end + end + def transfer_project(project, user, new_namespace) service = Projects::TransferService.new(project, user) diff --git a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb index b5149a0fcb1..bc95fcd6b88 100644 --- a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb +++ b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb @@ -34,8 +34,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params| context 'when the personal access token has no api scope', unless: params[:public] do it 'does not log the user in' do - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) + # Several instances of where these specs are shared route the request + # through ApplicationController#route_not_found which does not involve + # the usual auth code from Devise, so does not increment the + # :user_unauthenticated_counter + # + unless params[:ignore_incrementing] + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + end personal_access_token.update(scopes: [:read_user]) @@ -84,8 +91,15 @@ shared_examples 'authenticates sessionless user' do |path, format, params| end it "doesn't log the user in otherwise", unless: params[:public] do - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) + # Several instances of where these specs are shared route the request + # through ApplicationController#route_not_found which does not involve + # the usual auth code from Devise, so does not increment the + # :user_unauthenticated_counter + # + unless params[:ignore_incrementing] + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + end get path, params: default_params.merge(private_token: 'token') diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 4d2ad165fd6..6fb1d279456 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -129,6 +129,7 @@ module GraphqlHelpers allow_unlimited_graphql_complexity allow_unlimited_graphql_depth + allow_high_graphql_recursion type = GitlabSchema.types[class_name.to_s] return "" unless type @@ -213,6 +214,23 @@ module GraphqlHelpers end end + def expect_graphql_errors_to_include(regexes_to_match) + raise "No errors. Was expecting to match #{regexes_to_match}" if graphql_errors.nil? || graphql_errors.empty? + + error_messages = flattened_errors.collect { |error_hash| error_hash["message"] } + Array.wrap(regexes_to_match).flatten.each do |regex| + expect(error_messages).to include a_string_matching regex + end + end + + def expect_graphql_errors_to_be_empty + expect(flattened_errors).to be_empty + end + + def flattened_errors + Array.wrap(graphql_errors).flatten.compact + end + # Raises an error if no response is found def graphql_mutation_response(mutation_name) graphql_data.fetch(GraphqlHelpers.fieldnamerize(mutation_name)) @@ -260,6 +278,10 @@ module GraphqlHelpers allow_any_instance_of(GitlabSchema).to receive(:max_depth).and_return nil allow(GitlabSchema).to receive(:max_query_depth).with(any_args).and_return nil end + + def allow_high_graphql_recursion + allow_any_instance_of(Gitlab::Graphql::QueryAnalyzers::RecursionAnalyzer).to receive(:recursion_threshold).and_return 1000 + end end # This warms our schema, doing this as part of loading the helpers to avoid diff --git a/spec/support/shared_examples/controllers/todos_shared_examples.rb b/spec/support/shared_examples/controllers/todos_shared_examples.rb index f3f9abb7da2..914bf506320 100644 --- a/spec/support/shared_examples/controllers/todos_shared_examples.rb +++ b/spec/support/shared_examples/controllers/todos_shared_examples.rb @@ -39,7 +39,7 @@ shared_examples 'todos actions' do post_create end.to change { user.todos.count }.by(0) - expect(response).to have_gitlab_http_status(parent.is_a?(Group) ? 401 : 302) + expect(response).to have_gitlab_http_status(302) end end end |