diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2017-07-04 05:15:27 +0800 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2017-07-04 05:15:27 +0800 |
commit | 39573c6dde39de2345f100586c2c10f74187f6c1 (patch) | |
tree | b98c5d4b2e211397450dad6009bf97584f772ce5 /spec | |
parent | 23bfd8c13c803f4efdb9eaf8e6e3c1ffd17640e8 (diff) | |
parent | 049d4baed0f3532359feb729c5f0938d3d4518ef (diff) | |
download | gitlab-ce-39573c6dde39de2345f100586c2c10f74187f6c1.tar.gz |
Merge remote-tracking branch 'upstream/master' into 30634-protected-pipeline
* upstream/master: (119 commits)
Speed up operations performed by gitlab-shell
Change the force flag to a keyword argument
add image - issue boards - moving card
copyedit == ee !2296
Reset @full_path to nil when cache expires
Replace existing runner links with icons and tooltips, move into btn-group.
add margin between captcha and register button
Eagerly create a milestone that is used in a feature spec
Adjust readme repo width
Resolve "Issue Board -> "Remove from board" button when viewing an issue gives js error and fails"
Set force_remove_source_branch default to false.
Fix rubocop offenses
Make entrypoint and command keys to be array of strings
Add issuable-list class to shared mr/issue lists to fix new responsive layout
New navigation breadcrumbs
Restore timeago translations in renderTimeago.
Fix curl example paths (missing the 'files' segment)
Automatically hide sidebar on smaller screens
Fix typo in IssuesFinder comment
Make Project#ensure_repository force create a repo
...
Diffstat (limited to 'spec')
67 files changed, 1348 insertions, 561 deletions
diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb index 80a418feb3e..ada011e7595 100644 --- a/spec/controllers/abuse_reports_controller_spec.rb +++ b/spec/controllers/abuse_reports_controller_spec.rb @@ -13,6 +13,31 @@ describe AbuseReportsController do sign_in(reporter) end + describe 'GET new' do + context 'when the user has already been deleted' do + it 'redirects the reporter to root_path' do + user_id = user.id + user.destroy + + get :new, { user_id: user_id } + + expect(response).to redirect_to root_path + expect(flash[:alert]).to eq('Cannot create the abuse report. The user has been deleted.') + end + end + + context 'when the user has already been blocked' do + it 'redirects the reporter to the user\'s profile' do + user.block + + get :new, { user_id: user.id } + + expect(response).to redirect_to user + expect(flash[:alert]).to eq('Cannot create the abuse report. This user has been blocked.') + end + end + end + describe 'POST create' do context 'with valid attributes' do it 'saves the abuse report' do diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index f3263bc177d..c6e5fb61cf9 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -23,6 +23,21 @@ describe Groups::MilestonesController do project.team << [user, :master] end + describe "#index" do + it 'shows group milestones page' do + get :index, group_id: group.to_param + + expect(response).to have_http_status(200) + end + + it 'shows group milestones JSON' do + get :index, group_id: group.to_param, format: :json + + expect(response).to have_http_status(200) + expect(response.content_type).to eq 'application/json' + end + end + it_behaves_like 'milestone tabs' describe "#create" do diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index aef1c17a239..1bb2db11e7f 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -220,7 +220,7 @@ FactoryGirl.define do active: true, properties: { 'project_url' => 'http://redmine/projects/project_name_in_redmine', - 'issues_url' => "http://redmine/#{project.id}/project_name_in_redmine/:id", + 'issues_url' => 'http://redmine/projects/project_name_in_redmine/issues/:id', 'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new' } ) diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb index 5e6cd64c5c1..b88e801c3d7 100644 --- a/spec/features/abuse_report_spec.rb +++ b/spec/features/abuse_report_spec.rb @@ -12,7 +12,7 @@ feature 'Abuse reports', feature: true do click_link 'Report abuse' - fill_in 'abuse_report_message', with: 'This user send spam' + fill_in 'abuse_report_message', with: 'This user sends spam' click_button 'Send report' expect(page).to have_content 'Thank you for your report' diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 301c243febd..1c9595def21 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -79,6 +79,22 @@ describe 'Issue Boards', feature: true, js: true do end end + it 'does not show remove button for backlog or closed issues' do + create(:issue, project: project) + create(:issue, :closed, project: project) + + visit namespace_project_board_path(project.namespace, project, board) + wait_for_requests + + click_card(find('.board:nth-child(1)').first('.card')) + + expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board' + + click_card(find('.board:nth-child(3)').first('.card')) + + expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board' + end + context 'assignee' do it 'updates the issues assignee' do click_card(card) diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index f29186f368d..e9ef5d7983a 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -RSpec.describe 'Dashboard Projects', feature: true do +feature 'Dashboard Projects' do let(:user) { create(:user) } - let(:project) { create(:project, name: "awesome stuff") } + let(:project) { create(:project, name: 'awesome stuff') } let(:project2) { create(:project, :public, name: 'Community project') } before do @@ -15,6 +15,14 @@ RSpec.describe 'Dashboard Projects', feature: true do expect(page).to have_content('awesome stuff') end + it 'shows "New project" button' do + visit dashboard_projects_path + + page.within '#content-body' do + expect(page).to have_link('New project') + end + end + context 'when last_repository_updated_at, last_activity_at and update_at are present' do it 'shows the last_repository_updated_at attribute as the update date' do project.update_attributes!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago) @@ -47,8 +55,8 @@ RSpec.describe 'Dashboard Projects', feature: true do end end - describe "with a pipeline", redis: true do - let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha) } + describe 'with a pipeline', redis: true do + let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha) } before do # Since the cache isn't updated when a new pipeline is created diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index ea749528c11..d492a15ea17 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -129,7 +129,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do before do large_diff.find('.diff-line-num', match: :prefer_exact).hover - large_diff.find('.add-diff-note').click + large_diff.find('.add-diff-note', match: :prefer_exact).click large_diff.find('.note-textarea').send_keys comment_text large_diff.find_button('Comment').click wait_for_requests diff --git a/spec/features/issuables/user_sees_sidebar_spec.rb b/spec/features/issuables/user_sees_sidebar_spec.rb new file mode 100644 index 00000000000..4d7a7dc1806 --- /dev/null +++ b/spec/features/issuables/user_sees_sidebar_spec.rb @@ -0,0 +1,30 @@ +require 'rails_helper' + +describe 'Issue Sidebar on Mobile' do + include MobileHelpers + + let(:project) { create(:project, :public) } + let(:merge_request) { create(:merge_request, source_project: project) } + let(:issue) { create(:issue, project: project) } + let!(:user) { create(:user)} + + before do + sign_in(user) + end + + context 'mobile sidebar on merge requests', js: true do + before do + visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) + end + + it_behaves_like "issue sidebar stays collapsed on mobile" + end + + context 'mobile sidebar on issues', js: true do + before do + visit namespace_project_issue_path(project.namespace, project, issue) + end + + it_behaves_like "issue sidebar stays collapsed on mobile" + end +end diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 863f8f75cd8..4cb728cc82b 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -459,7 +459,7 @@ describe 'Filter issues', js: true, feature: true do context 'issue label clicked' do before do - find('.issues-list .issue .issue-info a .label', text: multiple_words_label.title).click + find('.issues-list .issue .issue-main-info .issuable-info a .label', text: multiple_words_label.title).click end it 'filters' do diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 163bc4bb32f..09724781a27 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -154,20 +154,6 @@ feature 'Issue Sidebar', feature: true do end end - context 'as a allowed mobile user', js: true do - before do - project.team << [user, :developer] - resize_screen_xs - visit_issue(project, issue) - end - - context 'mobile sidebar' do - it 'collapses the sidebar for small screens' do - expect(page).not_to have_css('aside.right-sidebar.right-sidebar-collapsed') - end - end - end - context 'as a guest' do before do project.team << [user, :guest] diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index dc981406e4e..df704b55839 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -1,16 +1,16 @@ require 'rails_helper' -feature 'Multiple issue updating from issues#index', feature: true do +feature 'Multiple issue updating from issues#index', :js do let!(:project) { create(:project) } let!(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} before do project.team << [user, :master] - gitlab_sign_in(user) + sign_in(user) end - context 'status', js: true do + context 'status' do it 'sets to closed' do visit namespace_project_issues_path(project.namespace, project) @@ -37,7 +37,7 @@ feature 'Multiple issue updating from issues#index', feature: true do end end - context 'assignee', js: true do + context 'assignee' do it 'updates to current user' do visit namespace_project_issues_path(project.namespace, project) @@ -67,8 +67,8 @@ feature 'Multiple issue updating from issues#index', feature: true do end end - context 'milestone', js: true do - let(:milestone) { create(:milestone, project: project) } + context 'milestone' do + let!(:milestone) { create(:milestone, project: project) } it 'updates milestone' do visit namespace_project_issues_path(project.namespace, project) diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index a111aa87c52..3f8d2255298 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -98,6 +98,6 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end def project_hook_exists?(project) - Gitlab::Git::Hook.new('post-receive', project.repository.path).exists? + Gitlab::Git::Hook.new('post-receive', project).exists? end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 37d9a97033b..22fb1223739 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -1,13 +1,27 @@ -require "spec_helper" +require 'spec_helper' -feature "New project", feature: true do +feature 'New project' do let(:user) { create(:admin) } before do - gitlab_sign_in(user) + sign_in(user) end - context "Visibility level selector" do + it 'shows "New project" page' do + visit new_project_path + + expect(page).to have_content('Project path') + expect(page).to have_content('Project name') + + expect(page).to have_link('GitHub') + expect(page).to have_link('Bitbucket') + expect(page).to have_link('GitLab.com') + expect(page).to have_link('Google Code') + expect(page).to have_button('Repo by URL') + expect(page).to have_link('GitLab export') + end + + context 'Visibility level selector' do Gitlab::VisibilityLevel.options.each do |key, level| it "sets selector to #{key}" do stub_application_setting(default_project_visibility: level) @@ -28,20 +42,20 @@ feature "New project", feature: true do end end - context "Namespace selector" do - context "with user namespace" do + context 'Namespace selector' do + context 'with user namespace' do before do visit new_project_path end - it "selects the user namespace" do - namespace = find("#project_namespace_id") + it 'selects the user namespace' do + namespace = find('#project_namespace_id') expect(namespace.text).to eq user.username end end - context "with group namespace" do + context 'with group namespace' do let(:group) { create(:group, :private, owner: user) } before do @@ -49,13 +63,13 @@ feature "New project", feature: true do visit new_project_path(namespace_id: group.id) end - it "selects the group namespace" do - namespace = find("#project_namespace_id option[selected]") + it 'selects the group namespace' do + namespace = find('#project_namespace_id option[selected]') expect(namespace.text).to eq group.name end - context "on validation error" do + context 'on validation error' do before do fill_in('project_path', with: 'private-group-project') choose('Internal') @@ -64,15 +78,15 @@ feature "New project", feature: true do expect(page).to have_css '.project-edit-errors .alert.alert-danger' end - it "selects the group namespace" do - namespace = find("#project_namespace_id option[selected]") + it 'selects the group namespace' do + namespace = find('#project_namespace_id option[selected]') expect(namespace.text).to eq group.name end end end - context "with subgroup namespace" do + context 'with subgroup namespace' do let(:group) { create(:group, :private, owner: user) } let(:subgroup) { create(:group, parent: group) } @@ -81,8 +95,8 @@ feature "New project", feature: true do visit new_project_path(namespace_id: subgroup.id) end - it "selects the group namespace" do - namespace = find("#project_namespace_id option[selected]") + it 'selects the group namespace' do + namespace = find('#project_namespace_id option[selected]') expect(namespace.text).to eq subgroup.full_path end @@ -94,10 +108,45 @@ feature "New project", feature: true do visit new_project_path end - it 'does not autocomplete sensitive git repo URL' do - autocomplete = find('#project_import_url')['autocomplete'] + context 'from git repository url' do + before do + first('.import_git').click + end + + it 'does not autocomplete sensitive git repo URL' do + autocomplete = find('#project_import_url')['autocomplete'] + + expect(autocomplete).to eq('off') + end + + it 'shows import instructions' do + git_import_instructions = first('.js-toggle-content') - expect(autocomplete).to eq('off') + expect(git_import_instructions).to be_visible + expect(git_import_instructions).to have_content 'Git repository URL' + end + end + + context 'from GitHub' do + before do + first('.import_github').click + end + + it 'shows import instructions' do + expect(page).to have_content('Import Projects from GitHub') + expect(current_path).to eq new_import_github_path + end + end + + context 'from Google Code' do + before do + first('.import_google_code').click + end + + it 'shows import instructions' do + expect(page).to have_content('Import projects from Google Code') + expect(current_path).to eq new_import_google_code_path + end end end end diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb index 1bd7e038939..24fff1a3052 100644 --- a/spec/features/user_can_display_performance_bar_spec.rb +++ b/spec/features/user_can_display_performance_bar_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'User can display performacne bar', :js do +describe 'User can display performance bar', :js do shared_examples 'performance bar is disabled' do it 'does not show the performance bar by default' do expect(page).not_to have_css('#peek') @@ -27,8 +27,8 @@ describe 'User can display performacne bar', :js do find('body').native.send_keys('pb') end - it 'does not show the performance bar by default' do - expect(page).not_to have_css('#peek') + it 'shows the performance bar' do + expect(page).to have_css('#peek') end end end diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 8ace1fb5751..4a52f0d5c58 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -295,22 +295,121 @@ describe IssuesFinder do end end - describe '.not_restricted_by_confidentiality' do - let(:authorized_user) { create(:user) } - let(:project) { create(:empty_project, namespace: authorized_user.namespace) } - let!(:public_issue) { create(:issue, project: project) } - let!(:confidential_issue) { create(:issue, project: project, confidential: true) } - - it 'returns non confidential issues for nil user' do - expect(described_class.send(:not_restricted_by_confidentiality, nil)).to include(public_issue) - end + describe '#with_confidentiality_access_check' do + let(:guest) { create(:user) } + set(:authorized_user) { create(:user) } + set(:project) { create(:empty_project, namespace: authorized_user.namespace) } + set(:public_issue) { create(:issue, project: project) } + set(:confidential_issue) { create(:issue, project: project, confidential: true) } + + context 'when no project filter is given' do + let(:params) { {} } + + context 'for an anonymous user' do + subject { described_class.new(nil, params).with_confidentiality_access_check } + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + end + + context 'for a user without project membership' do + subject { described_class.new(user, params).with_confidentiality_access_check } + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + end + + context 'for a guest user' do + subject { described_class.new(guest, params).with_confidentiality_access_check } + + before do + project.add_guest(guest) + end + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + end + + context 'for a project member with access to view confidential issues' do + subject { described_class.new(authorized_user, params).with_confidentiality_access_check } - it 'returns non confidential issues for user not authorized for the issues projects' do - expect(described_class.send(:not_restricted_by_confidentiality, user)).to include(public_issue) + it 'returns all issues' do + expect(subject).to include(public_issue, confidential_issue) + end + end end - it 'returns all issues for user authorized for the issues projects' do - expect(described_class.send(:not_restricted_by_confidentiality, authorized_user)).to include(public_issue, confidential_issue) + context 'when searching within a specific project' do + let(:params) { { project_id: project.id } } + + context 'for an anonymous user' do + subject { described_class.new(nil, params).with_confidentiality_access_check } + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + + it 'does not filter by confidentiality' do + expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything) + + subject + end + end + + context 'for a user without project membership' do + subject { described_class.new(user, params).with_confidentiality_access_check } + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + + it 'filters by confidentiality' do + expect(Issue).to receive(:where).with(a_string_matching('confidential'), anything) + + subject + end + end + + context 'for a guest user' do + subject { described_class.new(guest, params).with_confidentiality_access_check } + + before do + project.add_guest(guest) + end + + it 'returns only public issues' do + expect(subject).to include(public_issue) + expect(subject).not_to include(confidential_issue) + end + + it 'filters by confidentiality' do + expect(Issue).to receive(:where).with(a_string_matching('confidential'), anything) + + subject + end + end + + context 'for a project member with access to view confidential issues' do + subject { described_class.new(authorized_user, params).with_confidentiality_access_check } + + it 'returns all issues' do + expect(subject).to include(public_issue, confidential_issue) + end + + it 'does not filter by confidentiality' do + expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything) + + subject + end + end end end end diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 8da22dc78fa..e3f9d9db9eb 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -91,7 +91,7 @@ describe GroupsHelper do let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) } it 'outputs the groups in the correct order' do - expect(group_title(very_deep_nested_group)).to match(/>#{group.name}<\/a>.*>#{nested_group.name}<\/a>.*>#{deep_nested_group.name}<\/a>/) + expect(helper.group_title(very_deep_nested_group)).to match(/>#{group.name}<\/a>.*>#{nested_group.name}<\/a>.*>#{deep_nested_group.name}<\/a>/) end end end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 15cb620199d..d2e918ef014 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -77,54 +77,89 @@ describe IssuablesHelper do }.with_indifferent_access end + let(:issues_finder) { IssuesFinder.new(nil, params) } + let(:merge_requests_finder) { MergeRequestsFinder.new(nil, params) } + + before do + allow(helper).to receive(:issues_finder).and_return(issues_finder) + allow(helper).to receive(:merge_requests_finder).and_return(merge_requests_finder) + end + it 'returns the cached value when called for the same issuable type & with the same params' do - expect(helper).to receive(:params).twice.and_return(params) - expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42) + expect(issues_finder).to receive(:count_by_state).and_return(opened: 42) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') - expect(helper).not_to receive(:issuables_count_for_state) + expect(issues_finder).not_to receive(:count_by_state) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') end + it 'takes confidential status into account when searching for issues' do + expect(issues_finder).to receive(:count_by_state).and_return(opened: 42) + + expect(helper.issuables_state_counter_text(:issues, :opened)) + .to include('42') + + expect(issues_finder).to receive(:user_cannot_see_confidential_issues?).twice.and_return(false) + expect(issues_finder).to receive(:count_by_state).and_return(opened: 40) + + expect(helper.issuables_state_counter_text(:issues, :opened)) + .to include('40') + + expect(issues_finder).to receive(:user_can_see_all_confidential_issues?).and_return(true) + expect(issues_finder).to receive(:count_by_state).and_return(opened: 45) + + expect(helper.issuables_state_counter_text(:issues, :opened)) + .to include('45') + end + + it 'does not take confidential status into account when searching for merge requests' do + expect(merge_requests_finder).to receive(:count_by_state).and_return(opened: 42) + expect(merge_requests_finder).not_to receive(:user_cannot_see_confidential_issues?) + expect(merge_requests_finder).not_to receive(:user_can_see_all_confidential_issues?) + + expect(helper.issuables_state_counter_text(:merge_requests, :opened)) + .to include('42') + end + it 'does not take some keys into account in the cache key' do - expect(helper).to receive(:params).and_return({ + expect(issues_finder).to receive(:count_by_state).and_return(opened: 42) + expect(issues_finder).to receive(:params).and_return({ author_id: '11', state: 'foo', sort: 'foo', utf8: 'foo', page: 'foo' }.with_indifferent_access) - expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') - expect(helper).to receive(:params).and_return({ + expect(issues_finder).not_to receive(:count_by_state) + expect(issues_finder).to receive(:params).and_return({ author_id: '11', state: 'bar', sort: 'bar', utf8: 'bar', page: 'bar' }.with_indifferent_access) - expect(helper).not_to receive(:issuables_count_for_state) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') end it 'does not take params order into account in the cache key' do - expect(helper).to receive(:params).and_return('author_id' => '11', 'state' => 'opened') - expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42) + expect(issues_finder).to receive(:params).and_return('author_id' => '11', 'state' => 'opened') + expect(issues_finder).to receive(:count_by_state).and_return(opened: 42) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') - expect(helper).to receive(:params).and_return('state' => 'opened', 'author_id' => '11') - expect(helper).not_to receive(:issuables_count_for_state) + expect(issues_finder).to receive(:params).and_return('state' => 'opened', 'author_id' => '11') + expect(issues_finder).not_to receive(:count_by_state) expect(helper.issuables_state_counter_text(:issues, :opened)) .to eq('<span>Open</span> <span class="badge">42</span>') diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb index 3cb809d42b5..24d4f1b4938 100644 --- a/spec/helpers/milestones_helper_spec.rb +++ b/spec/helpers/milestones_helper_spec.rb @@ -1,6 +1,42 @@ require 'spec_helper' describe MilestonesHelper do + describe '#milestones_filter_dropdown_path' do + let(:project) { create(:empty_project) } + let(:project2) { create(:empty_project) } + let(:group) { create(:group) } + + context 'when @project present' do + it 'returns project milestones JSON URL' do + assign(:project, project) + + expect(helper.milestones_filter_dropdown_path).to eq(namespace_project_milestones_path(project.namespace, project, :json)) + end + end + + context 'when @target_project present' do + it 'returns targeted project milestones JSON URL' do + assign(:target_project, project2) + + expect(helper.milestones_filter_dropdown_path).to eq(namespace_project_milestones_path(project2.namespace, project2, :json)) + end + end + + context 'when @group present' do + it 'returns group milestones JSON URL' do + assign(:group, group) + + expect(helper.milestones_filter_dropdown_path).to eq(group_milestones_path(group, :json)) + end + end + + context 'when neither of @project/@target_project/@group present' do + it 'returns dashboard milestones JSON URL' do + expect(helper.milestones_filter_dropdown_path).to eq(dashboard_milestones_path(:json)) + end + end + end + describe "#milestone_date_range" do def result_for(*args) milestone_date_range(build(:milestone, *args)) diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 3fc03324d16..8e056882108 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,7 +1,7 @@ /* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */ import Cookies from 'js-cookie'; -import AwardsHandler from '~/awards_handler'; +import loadAwardsHandler from '~/awards_handler'; import '~/lib/utils/common_utils'; @@ -26,14 +26,13 @@ import '~/lib/utils/common_utils'; describe('AwardsHandler', function() { preloadFixtures('issues/issue_with_comment.html.raw'); - beforeEach(function() { + beforeEach(function(done) { loadFixtures('issues/issue_with_comment.html.raw'); - awardsHandler = new AwardsHandler; - spyOn(awardsHandler, 'postEmoji').and.callFake((function(_this) { - return function(button, url, emoji, cb) { - return cb(); - }; - })(this)); + loadAwardsHandler(true).then((obj) => { + awardsHandler = obj; + spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); + done(); + }).catch(fail); let isEmojiMenuBuilt = false; openAndWaitForEmojiMenu = function() { diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index 9df92318864..bc13373a27e 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -42,9 +42,6 @@ describe('Issuable output', () => { }).$mount(); }); - afterEach(() => { - }); - it('should render a title/description/edited and update title/description/edited on update', (done) => { vm.poll.options.successCallback({ json() { diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 5ece4ed080b..2c096ed08a8 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -523,6 +523,51 @@ import '~/notes'; }); }); + describe('postComment with Slash commands', () => { + const sampleComment = '/assign @root\n/award :100:'; + const note = { + commands_changes: { + assignee_id: 1, + emoji_award: '100' + }, + errors: { + commands_only: ['Commands applied'] + }, + valid: false + }; + let $form; + let $notesContainer; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + gl.awardsHandler = { + addAwardToEmojiBar: () => {}, + scrollToAwards: () => {} + }; + gl.GfmAutoComplete = { + dataSources: { + commands: '/root/test-project/autocomplete_sources/commands' + } + }; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + }); + + it('should remove slash command placeholder when comment with slash commands is done posting', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); + $('.js-comment-button').click(); + + expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown + deferred.resolve(note); + expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed + }); + }); + describe('update comment with script tags', () => { const sampleComment = '<script></script>'; const updatedComment = '<script></script>'; diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb index a4bb043f8f1..b7d82c36ddd 100644 --- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb @@ -88,12 +88,12 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do it 'queries the collection on the first call' do expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original - expect_any_instance_of(Project).to receive(:issue_reference_pattern).once.and_call_original + expect_any_instance_of(Project).to receive(:external_issue_reference_pattern).once.and_call_original not_cached = reference_filter.call("look for #{reference}", { project: project }) expect_any_instance_of(Project).not_to receive(:default_issues_tracker?) - expect_any_instance_of(Project).not_to receive(:issue_reference_pattern) + expect_any_instance_of(Project).not_to receive(:external_issue_reference_pattern) cached = reference_filter.call("look for #{reference}", { project: project }) diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index e5c1deb338b..a79d365d6c5 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -39,13 +39,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do let(:reference) { "##{issue.iid}" } - it 'ignores valid references when using non-default tracker' do - allow(project).to receive(:default_issues_tracker?).and_return(false) - - exp = act = "Issue #{reference}" - expect(reference_filter(act).to_html).to eq exp - end - it 'links to a valid reference' do doc = reference_filter("Fixed #{reference}") @@ -340,24 +333,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do .to eq({ project => { issue.iid => issue } }) end end - - context 'using an external issue tracker' do - it 'returns a Hash containing the issues per project' do - doc = Nokogiri::HTML.fragment('') - filter = described_class.new(doc, project: project) - - expect(project).to receive(:default_issues_tracker?).and_return(false) - - expect(filter).to receive(:projects_per_reference) - .and_return({ project.path_with_namespace => project }) - - expect(filter).to receive(:references_per_project) - .and_return({ project.path_with_namespace => Set.new([1]) }) - - expect(filter.issues_per_project[project][1]) - .to be_an_instance_of(ExternalIssue) - end - end end describe '.references_in' do diff --git a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb new file mode 100644 index 00000000000..2b8c76f2bb8 --- /dev/null +++ b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +describe Banzai::Pipeline::GfmPipeline do + describe 'integration between parsing regular and external issue references' do + let(:project) { create(:redmine_project, :public) } + + it 'allows to use shorthand external reference syntax for Redmine' do + markdown = '#12' + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq 'http://redmine/projects/project_name_in_redmine/issues/12' + end + + it 'parses cross-project references to regular issues' do + other_project = create(:empty_project, :public) + issue = create(:issue, project: other_project) + markdown = issue.to_reference(project, full: true) + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq( + Gitlab::Routing.url_helpers.namespace_project_issue_path( + other_project.namespace, + other_project, + issue + ) + ) + end + end +end diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb index 58e1a0c1bc1..acdd23f81f3 100644 --- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb @@ -39,16 +39,6 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do expect(subject.nodes_visible_to_user(user, [link])).to eq([]) end end - - context 'when the project uses an external issue tracker' do - it 'returns all nodes' do - link = double(:link) - - expect(project).to receive(:external_issue_tracker).and_return(true) - - expect(subject.nodes_visible_to_user(user, [link])).to eq([link]) - end - end end describe '#referenced_by' do diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index af0e7855a9b..482f03aa0cc 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -598,8 +598,10 @@ module Ci describe "Image and service handling" do context "when extended docker configuration is used" do it "returns image and service when defined" do - config = YAML.dump({ image: { name: "ruby:2.1" }, - services: ["mysql", { name: "docker:dind", alias: "docker" }], + config = YAML.dump({ image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] }, + services: ["mysql", { name: "docker:dind", alias: "docker", + entrypoint: ["/usr/local/bin/init", "run"], + command: ["/usr/local/bin/init", "run"] }], before_script: ["pwd"], rspec: { script: "rspec" } }) @@ -614,8 +616,10 @@ module Ci coverage_regex: nil, tag_list: [], options: { - image: { name: "ruby:2.1" }, - services: [{ name: "mysql" }, { name: "docker:dind", alias: "docker" }] + image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] }, + services: [{ name: "mysql" }, + { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"], + command: ["/usr/local/bin/init", "run"] }] }, allow_failure: false, when: "on_success", @@ -628,8 +632,11 @@ module Ci config = YAML.dump({ image: "ruby:2.1", services: ["mysql"], before_script: ["pwd"], - rspec: { image: { name: "ruby:2.5" }, - services: [{ name: "postgresql", alias: "db-pg" }, "docker:dind"], script: "rspec" } }) + rspec: { image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, + services: [{ name: "postgresql", alias: "db-pg", + entrypoint: ["/usr/local/bin/init", "run"], + command: ["/usr/local/bin/init", "run"] }, "docker:dind"], + script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config, path) @@ -642,8 +649,10 @@ module Ci coverage_regex: nil, tag_list: [], options: { - image: { name: "ruby:2.5" }, - services: [{ name: "postgresql", alias: "db-pg" }, { name: "docker:dind" }] + image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, + services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"], + command: ["/usr/local/bin/init", "run"] }, + { name: "docker:dind" }] }, allow_failure: false, when: "on_success", diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index bca22e39500..1a4d9ed5517 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -38,7 +38,7 @@ describe Gitlab::Ci::Config::Entry::Image do end context 'when configuration is a hash' do - let(:config) { { name: 'ruby:2.2', entrypoint: '/bin/sh' } } + let(:config) { { name: 'ruby:2.2', entrypoint: %w(/bin/sh run) } } describe '#value' do it 'returns image hash' do @@ -66,7 +66,7 @@ describe Gitlab::Ci::Config::Entry::Image do describe '#entrypoint' do it "returns image's entrypoint" do - expect(entry.entrypoint).to eq '/bin/sh' + expect(entry.entrypoint).to eq %w(/bin/sh run) end end end diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb index 7202fe525e4..9ebf947a751 100644 --- a/spec/lib/gitlab/ci/config/entry/service_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -43,7 +43,7 @@ describe Gitlab::Ci::Config::Entry::Service do context 'when configuration is a hash' do let(:config) do - { name: 'postgresql:9.5', alias: 'db', command: 'cmd', entrypoint: '/bin/sh' } + { name: 'postgresql:9.5', alias: 'db', command: %w(cmd run), entrypoint: %w(/bin/sh run) } end describe '#valid?' do @@ -72,13 +72,13 @@ describe Gitlab::Ci::Config::Entry::Service do describe '#command' do it "returns service's command" do - expect(entry.command).to eq 'cmd' + expect(entry.command).to eq %w(cmd run) end end describe '#entrypoint' do it "returns service's entrypoint" do - expect(entry.entrypoint).to eq '/bin/sh' + expect(entry.entrypoint).to eq %w(/bin/sh run) end end end diff --git a/spec/lib/gitlab/database/sha_attribute_spec.rb b/spec/lib/gitlab/database/sha_attribute_spec.rb new file mode 100644 index 00000000000..62c1d37ea1c --- /dev/null +++ b/spec/lib/gitlab/database/sha_attribute_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Database::ShaAttribute do + let(:sha) do + '9a573a369a5bfbb9a4a36e98852c21af8a44ea8b' + end + + let(:binary_sha) do + [sha].pack('H*') + end + + let(:binary_from_db) do + if Gitlab::Database.postgresql? + "\\x#{sha}" + else + binary_sha + end + end + + let(:attribute) { described_class.new } + + describe '#type_cast_from_database' do + it 'converts the binary SHA to a String' do + expect(attribute.type_cast_from_database(binary_from_db)).to eq(sha) + end + end + + describe '#type_cast_for_database' do + it 'converts a SHA String to binary data' do + expect(attribute.type_cast_for_database(sha).to_s).to eq(binary_sha) + end + end +end diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 3f279c21865..73518656bde 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -4,18 +4,20 @@ require 'fileutils' describe Gitlab::Git::Hook, lib: true do describe "#trigger" do let(:project) { create(:project, :repository) } + let(:repo_path) { project.repository.path } let(:user) { create(:user) } + let(:gl_id) { Gitlab::GlId.gl_id(user) } def create_hook(name) - FileUtils.mkdir_p(File.join(project.repository.path, 'hooks')) - File.open(File.join(project.repository.path, 'hooks', name), 'w', 0755) do |f| + FileUtils.mkdir_p(File.join(repo_path, 'hooks')) + File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f| f.write('exit 0') end end def create_failing_hook(name) - FileUtils.mkdir_p(File.join(project.repository.path, 'hooks')) - File.open(File.join(project.repository.path, 'hooks', name), 'w', 0755) do |f| + FileUtils.mkdir_p(File.join(repo_path, 'hooks')) + File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f| f.write(<<-HOOK) echo 'regular message from the hook' echo 'error message from the hook' 1>&2 @@ -27,13 +29,29 @@ describe Gitlab::Git::Hook, lib: true do ['pre-receive', 'post-receive', 'update'].each do |hook_name| context "when triggering a #{hook_name} hook" do context "when the hook is successful" do + let(:hook_path) { File.join(repo_path, 'hooks', hook_name) } + let(:gl_repository) { Gitlab::GlRepository.gl_repository(project, false) } + let(:env) do + { + 'GL_ID' => gl_id, + 'PWD' => repo_path, + 'GL_PROTOCOL' => 'web', + 'GL_REPOSITORY' => gl_repository + } + end + it "returns success with no errors" do create_hook(hook_name) - hook = Gitlab::Git::Hook.new(hook_name, project.repository.path) + hook = Gitlab::Git::Hook.new(hook_name, project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(Gitlab::GlId.gl_id(user), blank, blank, ref) + if hook_name != 'update' + expect(Open3).to receive(:popen3) + .with(env, hook_path, chdir: repo_path).and_call_original + end + + status, errors = hook.trigger(gl_id, blank, blank, ref) expect(status).to be true expect(errors).to be_blank end @@ -42,11 +60,11 @@ describe Gitlab::Git::Hook, lib: true do context "when the hook is unsuccessful" do it "returns failure with errors" do create_failing_hook(hook_name) - hook = Gitlab::Git::Hook.new(hook_name, project.repository.path) + hook = Gitlab::Git::Hook.new(hook_name, project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(Gitlab::GlId.gl_id(user), blank, blank, ref) + status, errors = hook.trigger(gl_id, blank, blank, ref) expect(status).to be false expect(errors).to eq("error message from the hook\n") end @@ -56,11 +74,11 @@ describe Gitlab::Git::Hook, lib: true do context "when the hook doesn't exist" do it "returns success with no errors" do - hook = Gitlab::Git::Hook.new('unknown_hook', project.repository.path) + hook = Gitlab::Git::Hook.new('unknown_hook', project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(Gitlab::GlId.gl_id(user), blank, blank, ref) + status, errors = hook.trigger(gl_id, blank, blank, ref) expect(status).to be true expect(errors).to be_nil end diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index 168a59e5139..30b6a0d8845 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -34,7 +34,7 @@ describe Gitlab::ImportExport::RepoRestorer, services: true do it 'has the webhooks' do restorer.restore - expect(Gitlab::Git::Hook.new('post-receive', project.repository.path_to_repo)).to exist + expect(Gitlab::Git::Hook.new('post-receive', project)).to exist end end end diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index 4ae216d55b0..af50ecdb2ab 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -32,6 +32,17 @@ describe 'Gitlab::Popen', lib: true, no_db: true do end end + context 'with custom options' do + let(:vars) { { 'foobar' => 123, 'PWD' => path } } + let(:options) { { chdir: path } } + + it 'calls popen3 with the provided environment variables' do + expect(Open3).to receive(:popen3).with(vars, 'ls', options) + + @output, @status = @klass.new.popen(%w(ls), path, { 'foobar' => 123 }) + end + end + context 'without a directory argument' do before do @output, @status = @klass.new.popen(%w(ls)) @@ -45,7 +56,7 @@ describe 'Gitlab::Popen', lib: true, no_db: true do before do @output, @status = @klass.new.popen(%w[cat]) { |stdin| stdin.write 'hello' } end - + it { expect(@status).to be_zero } it { expect(@output).to eq('hello') } end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index a97a0f8452b..5b1b8f9516a 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -4,6 +4,7 @@ require 'stringio' describe Gitlab::Shell, lib: true do let(:project) { double('Project', id: 7, path: 'diaspora') } let(:gitlab_shell) { Gitlab::Shell.new } + let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } } before do allow(Project).to receive(:find).and_return(project) @@ -50,7 +51,7 @@ describe Gitlab::Shell, lib: true do describe '#add_key' do it 'removes trailing garbage' do allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) - expect(Gitlab::Utils).to receive(:system_silent).with( + expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] ) @@ -100,17 +101,91 @@ describe Gitlab::Shell, lib: true do allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) end + describe '#add_repository' do + it 'returns true when the command succeeds' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'add-project', 'current/storage', 'project/path.git'], + nil, popen_vars).and_return([nil, 0]) + + expect(gitlab_shell.add_repository('current/storage', 'project/path')).to be true + end + + it 'returns false when the command fails' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'add-project', 'current/storage', 'project/path.git'], + nil, popen_vars).and_return(["error", 1]) + + expect(gitlab_shell.add_repository('current/storage', 'project/path')).to be false + end + end + + describe '#remove_repository' do + it 'returns true when the command succeeds' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], + nil, popen_vars).and_return([nil, 0]) + + expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true + end + + it 'returns false when the command fails' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], + nil, popen_vars).and_return(["error", 1]) + + expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false + end + end + + describe '#mv_repository' do + it 'returns true when the command succeeds' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'], + nil, popen_vars).and_return([nil, 0]) + + expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be true + end + + it 'returns false when the command fails' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'], + nil, popen_vars).and_return(["error", 1]) + + expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be false + end + end + + describe '#fork_repository' do + it 'returns true when the command succeeds' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + nil, popen_vars).and_return([nil, 0]) + + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be true + end + + it 'return false when the command fails' do + expect(Gitlab::Popen).to receive(:popen) + .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + nil, popen_vars).and_return(["error", 1]) + + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be false + end + end + describe '#fetch_remote' do it 'returns true when the command succeeds' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800']).and_return([nil, 0]) + .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'], + nil, popen_vars).and_return([nil, 0]) expect(gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage')).to be true end it 'raises an exception when the command fails' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800']).and_return(["error", 1]) + .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'], + nil, popen_vars).and_return(["error", 1]) expect { gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage') }.to raise_error(Gitlab::Shell::Error, "error") end @@ -119,14 +194,16 @@ describe Gitlab::Shell, lib: true do describe '#import_repository' do it 'returns true when the command succeeds' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"]).and_return([nil, 0]) + .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"], + nil, popen_vars).and_return([nil, 0]) expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true end it 'raises an exception when the command fails' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"]).and_return(["error", 1]) + .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"], + nil, popen_vars).and_return(["error", 1]) expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error") end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 3c7c7562b46..c6718827028 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -30,7 +30,8 @@ describe Gitlab::UsageData do expect(count_data.keys).to match_array(%i( boards ci_builds - ci_pipelines + ci_internal_pipelines + ci_external_pipelines ci_runners ci_triggers ci_pipeline_schedules diff --git a/spec/migrations/rename_duplicated_variable_key_spec.rb b/spec/migrations/rename_duplicated_variable_key_spec.rb new file mode 100644 index 00000000000..11096564dfa --- /dev/null +++ b/spec/migrations/rename_duplicated_variable_key_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20170622135451_rename_duplicated_variable_key.rb') + +describe RenameDuplicatedVariableKey, :migration do + let(:variables) { table(:ci_variables) } + let(:projects) { table(:projects) } + + before do + projects.create!(id: 1) + variables.create!(id: 1, key: 'key1', project_id: 1) + variables.create!(id: 2, key: 'key2', project_id: 1) + variables.create!(id: 3, key: 'keyX', project_id: 1) + variables.create!(id: 4, key: 'keyX', project_id: 1) + variables.create!(id: 5, key: 'keyY', project_id: 1) + variables.create!(id: 6, key: 'keyX', project_id: 1) + variables.create!(id: 7, key: 'key7', project_id: 1) + variables.create!(id: 8, key: 'keyY', project_id: 1) + end + + it 'correctly remove duplicated records with smaller id' do + migrate! + + expect(variables.pluck(:id, :key)).to contain_exactly( + [1, 'key1'], + [2, 'key2'], + [3, 'keyX_3'], + [4, 'keyX_4'], + [5, 'keyY_5'], + [6, 'keyX'], + [7, 'key7'], + [8, 'keyY'] + ) + end +end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 090f9e70c50..dc7a0d80752 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe Ability, lib: true do context 'using a nil subject' do - it 'is always empty' do - expect(Ability.allowed(nil, nil).to_set).to be_empty + it 'has no permissions' do + expect(Ability.policy_for(nil, nil)).to be_banned end end @@ -255,12 +255,15 @@ describe Ability, lib: true do describe '.project_disabled_features_rules' do let(:project) { create(:empty_project, :wiki_disabled) } - subject { described_class.allowed(project.owner, project) } + subject { described_class.policy_for(project.owner, project) } context 'wiki named abilities' do it 'disables wiki abilities if the project has no wiki' do expect(project).to receive(:has_external_wiki?).and_return(false) - expect(subject).not_to include(:read_wiki, :create_wiki, :update_wiki, :admin_wiki) + expect(subject).not_to be_allowed(:read_wiki) + expect(subject).not_to be_allowed(:create_wiki) + expect(subject).not_to be_allowed(:update_wiki) + expect(subject).not_to be_allowed(:admin_wiki) end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index f36b5ca09ec..776a674a6d9 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -769,6 +769,12 @@ describe Ci::Pipeline, models: true do end end + describe '.internal_sources' do + subject { described_class.internal_sources } + + it { is_expected.to be_an(Array) } + end + describe '#status' do let(:build) do create(:ci_build, :created, pipeline: pipeline, name: 'test') diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 329682a0771..50f7c029af8 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -3,8 +3,16 @@ require 'spec_helper' describe Ci::Variable, models: true do subject { build(:ci_variable) } - it { is_expected.to include_module(HasVariable) } - it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id) } + let(:secret_value) { 'secret' } + + describe 'validations' do + it { is_expected.to include_module(HasVariable) } + it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) } + it { is_expected.to validate_length_of(:key).is_at_most(255) } + it { is_expected.to allow_value('foo').for(:key) } + it { is_expected.not_to allow_value('foo bar').for(:key) } + it { is_expected.not_to allow_value('foo/bar').for(:key) } + end describe '.unprotected' do subject { described_class.unprotected } diff --git a/spec/models/concerns/feature_gate_spec.rb b/spec/models/concerns/feature_gate_spec.rb new file mode 100644 index 00000000000..3f601243245 --- /dev/null +++ b/spec/models/concerns/feature_gate_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe FeatureGate do + describe 'User' do + describe '#flipper_id' do + context 'when user is not persisted' do + let(:user) { build(:user) } + + it { expect(user.flipper_id).to be_nil } + end + + context 'when user is persisted' do + let(:user) { create(:user) } + + it { expect(user.flipper_id).to eq "User:#{user.id}" } + end + end + end +end diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index 65f05121b40..36aedd2f701 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -132,6 +132,19 @@ describe Group, 'Routable' do end end + describe '#expires_full_path_cache' do + context 'with RequestStore active', :request_store do + it 'expires the full_path cache' do + expect(group.full_path).to eq('foo') + + group.route.update(path: 'bar', name: 'bar') + group.expires_full_path_cache + + expect(group.full_path).to eq('bar') + end + end + end + describe '#full_name' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } diff --git a/spec/models/concerns/sha_attribute_spec.rb b/spec/models/concerns/sha_attribute_spec.rb new file mode 100644 index 00000000000..9e37c2b20c4 --- /dev/null +++ b/spec/models/concerns/sha_attribute_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe ShaAttribute do + let(:model) { Class.new { include ShaAttribute } } + + before do + columns = [ + double(:column, name: 'name', type: :text), + double(:column, name: 'sha1', type: :binary) + ] + + allow(model).to receive(:columns).and_return(columns) + end + + describe '#sha_attribute' do + it 'defines a SHA attribute for a binary column' do + expect(model).to receive(:attribute) + .with(:sha1, an_instance_of(Gitlab::Database::ShaAttribute)) + + model.sha_attribute(:sha1) + end + + it 'raises ArgumentError when the column type is not :binary' do + expect { model.sha_attribute(:name) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb deleted file mode 100644 index d1e17c4f684..00000000000 --- a/spec/models/concerns/sortable_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'spec_helper' - -describe Sortable do - let(:relation) { Issue.all } - - describe '#where' do - it 'orders by id, descending' do - order_node = relation.where(iid: 1).order_values.first - expect(order_node).to be_a(Arel::Nodes::Descending) - expect(order_node.expr.name).to eq(:id) - end - end - - describe '#find_by' do - it 'does not order' do - expect(relation).to receive(:unscope).with(:order).and_call_original - - relation.find_by(iid: 1) - end - end -end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index d4f898f6d9f..62c4ea01ce1 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -342,6 +342,17 @@ describe Namespace, models: true do end end + describe '#soft_delete_without_removing_associations' do + let(:project1) { create(:project_empty_repo, namespace: namespace) } + + it 'updates the deleted_at timestamp but preserves projects' do + namespace.soft_delete_without_removing_associations + + expect(Project.all).to include(project1) + expect(namespace.deleted_at).not_to be_nil + end + end + describe '#user_ids_for_project_authorizations' do it 'returns the user IDs for which to refresh authorizations' do expect(namespace.user_ids_for_project_authorizations) diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index c86f56c55eb..4a1de76f099 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -64,12 +64,12 @@ describe JiraService, models: true do end end - describe '#reference_pattern' do + describe '.reference_pattern' do it_behaves_like 'allows project key on reference pattern' it 'does not allow # on the code' do - expect(subject.reference_pattern.match('#123')).to be_nil - expect(subject.reference_pattern.match('1#23#12')).to be_nil + expect(described_class.reference_pattern.match('#123')).to be_nil + expect(described_class.reference_pattern.match('1#23#12')).to be_nil end end diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 6631d9040b1..441b3f896ca 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -31,11 +31,11 @@ describe RedmineService, models: true do end end - describe '#reference_pattern' do + describe '.reference_pattern' do it_behaves_like 'allows project key on reference pattern' it 'does allow # on the reference' do - expect(subject.reference_pattern.match('#123')[:issue]).to eq('123') + expect(described_class.reference_pattern.match('#123')[:issue]).to eq('123') end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5565fd2d391..ad98b4b669f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1170,6 +1170,16 @@ describe Project, models: true do expect(relation.search(project.namespace.name)).to eq([project]) end + + describe 'with pending_delete project' do + let(:pending_delete_project) { create(:empty_project, pending_delete: true) } + + it 'shows pending deletion project' do + search_result = described_class.search(pending_delete_project.name) + + expect(search_result).to eq([pending_delete_project]) + end + end end describe '#rename_repo' do @@ -1206,6 +1216,8 @@ describe Project, models: true do expect(project).to receive(:expire_caches_before_rename) + expect(project).to receive(:expires_full_path_cache) + project.rename_repo end @@ -1334,7 +1346,7 @@ describe Project, models: true do .with(project.repository_storage_path, project.path_with_namespace) .and_return(true) - expect(project).to receive(:create_repository) + expect(project).to receive(:create_repository).with(force: true) project.ensure_repository end @@ -1347,6 +1359,19 @@ describe Project, models: true do project.ensure_repository end + + it 'creates the repository if it is a fork' do + expect(project).to receive(:forked?).and_return(true) + + allow(project).to receive(:repository_exists?) + .and_return(false) + + expect(shell).to receive(:add_repository) + .with(project.repository_storage_path, project.path_with_namespace) + .and_return(true) + + project.ensure_repository + end end describe '#user_can_push_to_empty_repo?' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 3e984ec7588..c69f0a495db 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -780,7 +780,7 @@ describe Repository, models: true do context 'when pre hooks were successful' do it 'runs without errors' do expect_any_instance_of(GitHooksService).to receive(:execute) - .with(user, project.repository.path_to_repo, old_rev, blank_sha, 'refs/heads/feature') + .with(user, project, old_rev, blank_sha, 'refs/heads/feature') expect { repository.rm_branch(user, 'feature') }.not_to raise_error end @@ -823,12 +823,7 @@ describe Repository, models: true do service = GitHooksService.new expect(GitHooksService).to receive(:new).and_return(service) expect(service).to receive(:execute) - .with( - user, - repository.path_to_repo, - old_rev, - new_rev, - 'refs/heads/feature') + .with(user, project, old_rev, new_rev, 'refs/heads/feature') .and_yield(service).and_return(true) end @@ -1474,9 +1469,9 @@ describe Repository, models: true do it 'passes commit SHA to pre-receive and update hooks,\ and tag SHA to post-receive hook' do - pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', repository.path_to_repo) - update_hook = Gitlab::Git::Hook.new('update', repository.path_to_repo) - post_receive_hook = Gitlab::Git::Hook.new('post-receive', repository.path_to_repo) + pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project) + update_hook = Gitlab::Git::Hook.new('update', project) + post_receive_hook = Gitlab::Git::Hook.new('post-receive', project) allow(Gitlab::Git::Hook).to receive(:new) .and_return(pre_receive_hook, update_hook, post_receive_hook) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8e895ec6634..448555d2190 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -754,42 +754,49 @@ describe User, models: true do end describe '.search' do - let(:user) { create(:user) } + let!(:user) { create(:user, name: 'user', username: 'usern', email: 'email@gmail.com') } + let!(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@gmail.com') } - it 'returns users with a matching name' do - expect(described_class.search(user.name)).to eq([user]) - end + describe 'name matching' do + it 'returns users with a matching name with exact match first' do + expect(described_class.search(user.name)).to eq([user, user2]) + end - it 'returns users with a partially matching name' do - expect(described_class.search(user.name[0..2])).to eq([user]) - end + it 'returns users with a partially matching name' do + expect(described_class.search(user.name[0..2])).to eq([user2, user]) + end - it 'returns users with a matching name regardless of the casing' do - expect(described_class.search(user.name.upcase)).to eq([user]) + it 'returns users with a matching name regardless of the casing' do + expect(described_class.search(user2.name.upcase)).to eq([user2]) + end end - it 'returns users with a matching Email' do - expect(described_class.search(user.email)).to eq([user]) - end + describe 'email matching' do + it 'returns users with a matching Email' do + expect(described_class.search(user.email)).to eq([user, user2]) + end - it 'returns users with a partially matching Email' do - expect(described_class.search(user.email[0..2])).to eq([user]) - end + it 'returns users with a partially matching Email' do + expect(described_class.search(user.email[0..2])).to eq([user2, user]) + end - it 'returns users with a matching Email regardless of the casing' do - expect(described_class.search(user.email.upcase)).to eq([user]) + it 'returns users with a matching Email regardless of the casing' do + expect(described_class.search(user2.email.upcase)).to eq([user2]) + end end - it 'returns users with a matching username' do - expect(described_class.search(user.username)).to eq([user]) - end + describe 'username matching' do + it 'returns users with a matching username' do + expect(described_class.search(user.username)).to eq([user, user2]) + end - it 'returns users with a partially matching username' do - expect(described_class.search(user.username[0..2])).to eq([user]) - end + it 'returns users with a partially matching username' do + expect(described_class.search(user.username[0..2])).to eq([user2, user]) + end - it 'returns users with a matching username regardless of the casing' do - expect(described_class.search(user.username.upcase)).to eq([user]) + it 'returns users with a matching username regardless of the casing' do + expect(described_class.search(user2.username.upcase)).to eq([user2]) + end end end diff --git a/spec/policies/base_policy_spec.rb b/spec/policies/base_policy_spec.rb index 02acdcb36df..e1963091a72 100644 --- a/spec/policies/base_policy_spec.rb +++ b/spec/policies/base_policy_spec.rb @@ -3,17 +3,17 @@ require 'spec_helper' describe BasePolicy, models: true do describe '.class_for' do it 'detects policy class based on the subject ancestors' do - expect(described_class.class_for(GenericCommitStatus.new)).to eq(CommitStatusPolicy) + expect(DeclarativePolicy.class_for(GenericCommitStatus.new)).to eq(CommitStatusPolicy) end it 'detects policy class for a presented subject' do presentee = Ci::BuildPresenter.new(Ci::Build.new) - expect(described_class.class_for(presentee)).to eq(Ci::BuildPolicy) + expect(DeclarativePolicy.class_for(presentee)).to eq(Ci::BuildPolicy) end it 'uses GlobalPolicy when :global is given' do - expect(described_class.class_for(:global)).to eq(GlobalPolicy) + expect(DeclarativePolicy.class_for(:global)).to eq(GlobalPolicy) end end end diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index b4c6f3141fb..2a8e6653eb8 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -5,8 +5,8 @@ describe Ci::BuildPolicy, :models do let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } - let(:policies) do - described_class.abilities(user, build).to_set + let(:policy) do + described_class.new(user, build) end shared_context 'public pipelines disabled' do @@ -21,7 +21,7 @@ describe Ci::BuildPolicy, :models do context 'when public builds are enabled' do it 'does not include ability to read build' do - expect(policies).not_to include :read_build + expect(policy).not_to be_allowed :read_build end end @@ -29,7 +29,7 @@ describe Ci::BuildPolicy, :models do include_context 'public pipelines disabled' it 'does not include ability to read build' do - expect(policies).not_to include :read_build + expect(policy).not_to be_allowed :read_build end end end @@ -39,7 +39,7 @@ describe Ci::BuildPolicy, :models do context 'when public builds are enabled' do it 'includes ability to read build' do - expect(policies).to include :read_build + expect(policy).to be_allowed :read_build end end @@ -47,7 +47,7 @@ describe Ci::BuildPolicy, :models do include_context 'public pipelines disabled' it 'does not include ability to read build' do - expect(policies).not_to include :read_build + expect(policy).not_to be_allowed :read_build end end end @@ -62,7 +62,7 @@ describe Ci::BuildPolicy, :models do context 'when public builds are enabled' do it 'includes ability to read build' do - expect(policies).to include :read_build + expect(policy).to be_allowed :read_build end end @@ -70,7 +70,7 @@ describe Ci::BuildPolicy, :models do include_context 'public pipelines disabled' it 'does not include ability to read build' do - expect(policies).not_to include :read_build + expect(policy).not_to be_allowed :read_build end end end @@ -82,7 +82,7 @@ describe Ci::BuildPolicy, :models do context 'when public builds are enabled' do it 'includes ability to read build' do - expect(policies).to include :read_build + expect(policy).to be_allowed :read_build end end @@ -90,7 +90,7 @@ describe Ci::BuildPolicy, :models do include_context 'public pipelines disabled' it 'does not include ability to read build' do - expect(policies).to include :read_build + expect(policy).to be_allowed :read_build end end end @@ -110,7 +110,7 @@ describe Ci::BuildPolicy, :models do let(:branch_policy) { :no_one_can_push } it 'does not include ability to update build' do - expect(policies).not_to include :update_build + expect(policies).to be_disallowed :update_build end end @@ -118,7 +118,7 @@ describe Ci::BuildPolicy, :models do let(:branch_policy) { :developers_can_push } it 'includes ability to update build' do - expect(policies).to include :update_build + expect(policies).to be_allowed :update_build end end @@ -126,7 +126,7 @@ describe Ci::BuildPolicy, :models do let(:branch_policy) { :developers_can_merge } it 'includes ability to update build' do - expect(policies).to include :update_build + expect(policies).to be_allowed :update_build end end end diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb index 4ecf07a1bf2..db09be96875 100644 --- a/spec/policies/ci/pipeline_policy_spec.rb +++ b/spec/policies/ci/pipeline_policy_spec.rb @@ -23,7 +23,7 @@ describe Ci::PipelinePolicy, :models do let(:branch_policy) { :no_one_can_push } it 'does not include ability to update pipeline' do - expect(policies).not_to include :update_pipeline + expect(policies).to be_disallowed :update_pipeline end end @@ -31,7 +31,7 @@ describe Ci::PipelinePolicy, :models do let(:branch_policy) { :developers_can_push } it 'includes ability to update pipeline' do - expect(policies).to include :update_pipeline + expect(policies).to be_allowed :update_pipeline end end @@ -39,7 +39,7 @@ describe Ci::PipelinePolicy, :models do let(:branch_policy) { :developers_can_merge } it 'includes ability to update pipeline' do - expect(policies).to include :update_pipeline + expect(policies).to be_allowed :update_pipeline end end end diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb index 63ad5eb7322..ed4010e723b 100644 --- a/spec/policies/ci/trigger_policy_spec.rb +++ b/spec/policies/ci/trigger_policy_spec.rb @@ -6,36 +6,36 @@ describe Ci::TriggerPolicy, :models do let(:trigger) { create(:ci_trigger, project: project, owner: owner) } let(:policies) do - described_class.abilities(user, trigger).to_set + described_class.new(user, trigger) end shared_examples 'allows to admin and manage trigger' do it 'does include ability to admin trigger' do - expect(policies).to include :admin_trigger + expect(policies).to be_allowed :admin_trigger end it 'does include ability to manage trigger' do - expect(policies).to include :manage_trigger + expect(policies).to be_allowed :manage_trigger end end shared_examples 'allows to manage trigger' do it 'does not include ability to admin trigger' do - expect(policies).not_to include :admin_trigger + expect(policies).not_to be_allowed :admin_trigger end it 'does include ability to manage trigger' do - expect(policies).to include :manage_trigger + expect(policies).to be_allowed :manage_trigger end end shared_examples 'disallows to admin and manage trigger' do it 'does not include ability to admin trigger' do - expect(policies).not_to include :admin_trigger + expect(policies).not_to be_allowed :admin_trigger end it 'does not include ability to manage trigger' do - expect(policies).not_to include :manage_trigger + expect(policies).not_to be_allowed :manage_trigger end end diff --git a/spec/policies/deploy_key_policy_spec.rb b/spec/policies/deploy_key_policy_spec.rb index 28e10f0bfe2..f15f4a11f02 100644 --- a/spec/policies/deploy_key_policy_spec.rb +++ b/spec/policies/deploy_key_policy_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe DeployKeyPolicy, models: true do - subject { described_class.abilities(current_user, deploy_key).to_set } + subject { described_class.new(current_user, deploy_key) } describe 'updating a deploy_key' do context 'when a regular user' do @@ -16,7 +16,7 @@ describe DeployKeyPolicy, models: true do project.deploy_keys << deploy_key end - it { is_expected.to include(:update_deploy_key) } + it { is_expected.to be_allowed(:update_deploy_key) } end context 'tries to update private deploy key attached to other project' do @@ -27,13 +27,13 @@ describe DeployKeyPolicy, models: true do other_project.deploy_keys << deploy_key end - it { is_expected.not_to include(:update_deploy_key) } + it { is_expected.to be_disallowed(:update_deploy_key) } end context 'tries to update public deploy key' do let(:deploy_key) { create(:another_deploy_key, public: true) } - it { is_expected.not_to include(:update_deploy_key) } + it { is_expected.to be_disallowed(:update_deploy_key) } end end @@ -43,13 +43,13 @@ describe DeployKeyPolicy, models: true do context ' tries to update private deploy key' do let(:deploy_key) { create(:deploy_key, public: false) } - it { is_expected.to include(:update_deploy_key) } + it { is_expected.to be_allowed(:update_deploy_key) } end context 'when an admin user tries to update public deploy key' do let(:deploy_key) { create(:another_deploy_key, public: true) } - it { is_expected.to include(:update_deploy_key) } + it { is_expected.to be_allowed(:update_deploy_key) } end end end diff --git a/spec/policies/environment_policy_spec.rb b/spec/policies/environment_policy_spec.rb index 650432520bb..035e20c7452 100644 --- a/spec/policies/environment_policy_spec.rb +++ b/spec/policies/environment_policy_spec.rb @@ -8,8 +8,8 @@ describe EnvironmentPolicy do create(:environment, :with_review_app, project: project) end - let(:policies) do - described_class.abilities(user, environment).to_set + let(:policy) do + described_class.new(user, environment) end describe '#rules' do @@ -17,7 +17,7 @@ describe EnvironmentPolicy do let(:project) { create(:project, :private) } it 'does not include ability to stop environment' do - expect(policies).not_to include :stop_environment + expect(policy).to be_disallowed :stop_environment end end @@ -25,7 +25,7 @@ describe EnvironmentPolicy do let(:project) { create(:project, :public) } it 'does not include ability to stop environment' do - expect(policies).not_to include :stop_environment + expect(policy).to be_disallowed :stop_environment end end @@ -38,7 +38,7 @@ describe EnvironmentPolicy do context 'when team member has ability to stop environment' do it 'does includes ability to stop environment' do - expect(policies).to include :stop_environment + expect(policy).to be_allowed :stop_environment end end @@ -49,7 +49,7 @@ describe EnvironmentPolicy do end it 'does not include ability to stop environment' do - expect(policies).not_to include :stop_environment + expect(policy).to be_disallowed :stop_environment end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index a8331ceb5ff..06db0ea56e3 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -36,16 +36,24 @@ describe GroupPolicy, models: true do group.add_owner(owner) end - subject { described_class.abilities(current_user, group).to_set } + subject { described_class.new(current_user, group) } + + def expect_allowed(*permissions) + permissions.each { |p| is_expected.to be_allowed(p) } + end + + def expect_disallowed(*permissions) + permissions.each { |p| is_expected.not_to be_allowed(p) } + end context 'with no user' do let(:current_user) { nil } it do - is_expected.to include(:read_group) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_disallowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -53,10 +61,10 @@ describe GroupPolicy, models: true do let(:current_user) { guest } it do - is_expected.to include(:read_group) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_disallowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -64,10 +72,10 @@ describe GroupPolicy, models: true do let(:current_user) { reporter } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -75,10 +83,10 @@ describe GroupPolicy, models: true do let(:current_user) { developer } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -86,10 +94,10 @@ describe GroupPolicy, models: true do let(:current_user) { master } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_allowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -97,10 +105,10 @@ describe GroupPolicy, models: true do let(:current_user) { owner } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.to include(*master_permissions) - is_expected.to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_allowed(*master_permissions) + expect_allowed(*owner_permissions) end end @@ -108,10 +116,10 @@ describe GroupPolicy, models: true do let(:current_user) { admin } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.to include(*master_permissions) - is_expected.to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_allowed(*master_permissions) + expect_allowed(*owner_permissions) end end @@ -130,16 +138,16 @@ describe GroupPolicy, models: true do nested_group.add_owner(owner) end - subject { described_class.abilities(current_user, nested_group).to_set } + subject { described_class.new(current_user, nested_group) } context 'with no user' do let(:current_user) { nil } it do - is_expected.not_to include(:read_group) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_disallowed(:read_group) + expect_disallowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -147,10 +155,10 @@ describe GroupPolicy, models: true do let(:current_user) { guest } it do - is_expected.to include(:read_group) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_disallowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -158,10 +166,10 @@ describe GroupPolicy, models: true do let(:current_user) { reporter } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -169,10 +177,10 @@ describe GroupPolicy, models: true do let(:current_user) { developer } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -180,10 +188,10 @@ describe GroupPolicy, models: true do let(:current_user) { master } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_allowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -191,10 +199,10 @@ describe GroupPolicy, models: true do let(:current_user) { owner } it do - is_expected.to include(:read_group) - is_expected.to include(*reporter_permissions) - is_expected.to include(*master_permissions) - is_expected.to include(*owner_permissions) + expect_allowed(:read_group) + expect_allowed(*reporter_permissions) + expect_allowed(*master_permissions) + expect_allowed(*owner_permissions) end end end diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index 4a07c864428..c978cbd6185 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -9,7 +9,7 @@ describe IssuePolicy, models: true do let(:reporter_from_group_link) { create(:user) } def permissions(user, issue) - described_class.abilities(user, issue).to_set + described_class.new(user, issue) end context 'a private project' do @@ -30,42 +30,42 @@ describe IssuePolicy, models: true do end it 'does not allow non-members to read issues' do - expect(permissions(non_member, issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + expect(permissions(guest, issue)).to be_allowed(:read_issue) + expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue) - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) + expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(author, issue)).to be_disallowed(:admin_issue) - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) + expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(assignee, issue)).to be_disallowed(:admin_issue) - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end context 'with confidential issues' do @@ -73,37 +73,37 @@ describe IssuePolicy, models: true do let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } it 'does not allow non-members to read confidential issues' do - expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporters from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) + expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue) - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) + expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue) - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end end end @@ -123,37 +123,37 @@ describe IssuePolicy, models: true do end it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + expect(permissions(guest, issue)).to be_allowed(:read_issue) + expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue) - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) + expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(author, issue)).to be_disallowed(:admin_issue) - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) + expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(assignee, issue)).to be_disallowed(:admin_issue) - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue) + expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue) end context 'with confidential issues' do @@ -161,32 +161,32 @@ describe IssuePolicy, models: true do let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows reporter from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) + expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue) - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) + expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue) - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue) end end end diff --git a/spec/policies/personal_snippet_policy_spec.rb b/spec/policies/personal_snippet_policy_spec.rb index 58aa1145c9e..4d6350fc653 100644 --- a/spec/policies/personal_snippet_policy_spec.rb +++ b/spec/policies/personal_snippet_policy_spec.rb @@ -14,7 +14,7 @@ describe PersonalSnippetPolicy, models: true do end def permissions(user) - described_class.abilities(user, snippet).to_set + described_class.new(user, snippet) end context 'public snippet' do @@ -24,9 +24,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(nil) } it do - is_expected.to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -34,9 +34,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(regular_user) } it do - is_expected.to include(:read_personal_snippet) - is_expected.to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_allowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -44,9 +44,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(snippet.author) } it do - is_expected.to include(:read_personal_snippet) - is_expected.to include(:comment_personal_snippet) - is_expected.to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_allowed(:comment_personal_snippet) + is_expected.to be_allowed(*author_permissions) end end end @@ -58,9 +58,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(nil) } it do - is_expected.not_to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_disallowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -68,9 +68,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(regular_user) } it do - is_expected.to include(:read_personal_snippet) - is_expected.to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_allowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -78,9 +78,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(external_user) } it do - is_expected.not_to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_disallowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -88,9 +88,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(snippet.author) } it do - is_expected.to include(:read_personal_snippet) - is_expected.to include(:comment_personal_snippet) - is_expected.to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_allowed(:comment_personal_snippet) + is_expected.to be_allowed(*author_permissions) end end end @@ -102,9 +102,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(nil) } it do - is_expected.not_to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_disallowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -112,9 +112,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(regular_user) } it do - is_expected.not_to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_disallowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -122,9 +122,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(external_user) } it do - is_expected.not_to include(:read_personal_snippet) - is_expected.not_to include(:comment_personal_snippet) - is_expected.not_to include(*author_permissions) + is_expected.to be_disallowed(:read_personal_snippet) + is_expected.to be_disallowed(:comment_personal_snippet) + is_expected.to be_disallowed(*author_permissions) end end @@ -132,9 +132,9 @@ describe PersonalSnippetPolicy, models: true do subject { permissions(snippet.author) } it do - is_expected.to include(:read_personal_snippet) - is_expected.to include(:comment_personal_snippet) - is_expected.to include(*author_permissions) + is_expected.to be_allowed(:read_personal_snippet) + is_expected.to be_allowed(:comment_personal_snippet) + is_expected.to be_allowed(*author_permissions) end end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index d70e15f006b..ca435dd0218 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -73,37 +73,45 @@ describe ProjectPolicy, models: true do project.team << [reporter, :reporter] end + def expect_allowed(*permissions) + permissions.each { |p| is_expected.to be_allowed(p) } + end + + def expect_disallowed(*permissions) + permissions.each { |p| is_expected.not_to be_allowed(p) } + end + it 'does not include the read_issue permission when the issue author is not a member of the private project' do project = create(:empty_project, :private) issue = create(:issue, project: project) user = issue.author - expect(project.team.member?(issue.author)).to eq(false) + expect(project.team.member?(issue.author)).to be false - expect(BasePolicy.class_for(project).abilities(user, project).can_set) - .not_to include(:read_issue) - - expect(Ability.allowed?(user, :read_issue, project)).to be_falsy + expect(Ability).not_to be_allowed(user, :read_issue, project) end - it 'does not include the wiki permissions when the feature is disabled' do - project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) - wiki_permissions = [:read_wiki, :create_wiki, :update_wiki, :admin_wiki, :download_wiki_code] + context 'when the feature is disabled' do + subject { described_class.new(owner, project) } - permissions = described_class.abilities(owner, project).to_set + before do + project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) + end - expect(permissions).not_to include(*wiki_permissions) + it 'does not include the wiki permissions' do + expect_disallowed :read_wiki, :create_wiki, :update_wiki, :admin_wiki, :download_wiki_code + end end context 'abilities for non-public projects' do let(:project) { create(:empty_project, namespace: owner.namespace) } - subject { described_class.abilities(current_user, project).to_set } + subject { described_class.new(current_user, project) } context 'with no user' do let(:current_user) { nil } - it { is_expected.to be_empty } + it { is_expected.to be_banned } end context 'guests' do @@ -114,18 +122,18 @@ describe ProjectPolicy, models: true do end it do - is_expected.to include(*guest_permissions) - is_expected.not_to include(*reporter_public_build_permissions) - is_expected.not_to include(*team_member_reporter_permissions) - is_expected.not_to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_disallowed(*reporter_public_build_permissions) + expect_disallowed(*team_member_reporter_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end context 'public builds enabled' do it do - is_expected.to include(*guest_permissions) - is_expected.to include(:read_build, :read_pipeline) + expect_allowed(*guest_permissions) + expect_allowed(:read_build, :read_pipeline) end end @@ -135,8 +143,8 @@ describe ProjectPolicy, models: true do end it do - is_expected.to include(*guest_permissions) - is_expected.not_to include(:read_build, :read_pipeline) + expect_allowed(*guest_permissions) + expect_disallowed(:read_build, :read_pipeline) end end @@ -147,8 +155,8 @@ describe ProjectPolicy, models: true do end it do - is_expected.not_to include(:read_build) - is_expected.to include(:read_pipeline) + expect_disallowed(:read_build) + expect_allowed(:read_pipeline) end end end @@ -157,12 +165,13 @@ describe ProjectPolicy, models: true do let(:current_user) { reporter } it do - is_expected.to include(*guest_permissions) - is_expected.to include(*reporter_permissions) - is_expected.to include(*team_member_reporter_permissions) - is_expected.not_to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -170,12 +179,12 @@ describe ProjectPolicy, models: true do let(:current_user) { dev } it do - is_expected.to include(*guest_permissions) - is_expected.to include(*reporter_permissions) - is_expected.to include(*team_member_reporter_permissions) - is_expected.to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_allowed(*developer_permissions) + expect_disallowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -183,12 +192,12 @@ describe ProjectPolicy, models: true do let(:current_user) { master } it do - is_expected.to include(*guest_permissions) - is_expected.to include(*reporter_permissions) - is_expected.to include(*team_member_reporter_permissions) - is_expected.to include(*developer_permissions) - is_expected.to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*master_permissions) + expect_disallowed(*owner_permissions) end end @@ -196,12 +205,12 @@ describe ProjectPolicy, models: true do let(:current_user) { owner } it do - is_expected.to include(*guest_permissions) - is_expected.to include(*reporter_permissions) - is_expected.to include(*team_member_reporter_permissions) - is_expected.to include(*developer_permissions) - is_expected.to include(*master_permissions) - is_expected.to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*master_permissions) + expect_allowed(*owner_permissions) end end @@ -209,12 +218,12 @@ describe ProjectPolicy, models: true do let(:current_user) { admin } it do - is_expected.to include(*guest_permissions) - is_expected.to include(*reporter_permissions) - is_expected.not_to include(*team_member_reporter_permissions) - is_expected.to include(*developer_permissions) - is_expected.to include(*master_permissions) - is_expected.to include(*owner_permissions) + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_disallowed(*team_member_reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*master_permissions) + expect_allowed(*owner_permissions) end end end diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb index d2b2528c57a..2799f03fb9b 100644 --- a/spec/policies/project_snippet_policy_spec.rb +++ b/spec/policies/project_snippet_policy_spec.rb @@ -15,7 +15,15 @@ describe ProjectSnippetPolicy, models: true do def abilities(user, snippet_visibility) snippet = create(:project_snippet, snippet_visibility, project: project) - described_class.abilities(user, snippet).to_set + described_class.new(user, snippet) + end + + def expect_allowed(*permissions) + permissions.each { |p| is_expected.to be_allowed(p) } + end + + def expect_disallowed(*permissions) + permissions.each { |p| is_expected.not_to be_allowed(p) } end context 'public snippet' do @@ -23,8 +31,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(nil, :public) } it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -32,8 +40,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(regular_user, :public) } it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -41,8 +49,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(external_user, :public) } it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end end @@ -52,8 +60,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(nil, :internal) } it do - is_expected.not_to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_disallowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -61,8 +69,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(regular_user, :internal) } it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -70,8 +78,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(external_user, :internal) } it do - is_expected.not_to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_disallowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -83,8 +91,8 @@ describe ProjectSnippetPolicy, models: true do end it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end end @@ -94,8 +102,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(nil, :private) } it do - is_expected.not_to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_disallowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -103,19 +111,19 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(regular_user, :private) } it do - is_expected.not_to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_disallowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end context 'snippet author' do let(:snippet) { create(:project_snippet, :private, author: regular_user, project: project) } - subject { described_class.abilities(regular_user, snippet).to_set } + subject { described_class.new(regular_user, snippet) } it do - is_expected.to include(:read_project_snippet) - is_expected.to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_allowed(*author_permissions) end end @@ -127,8 +135,8 @@ describe ProjectSnippetPolicy, models: true do end it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -140,8 +148,8 @@ describe ProjectSnippetPolicy, models: true do end it do - is_expected.to include(:read_project_snippet) - is_expected.not_to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_disallowed(*author_permissions) end end @@ -149,8 +157,8 @@ describe ProjectSnippetPolicy, models: true do subject { abilities(create(:admin), :private) } it do - is_expected.to include(:read_project_snippet) - is_expected.to include(*author_permissions) + expect_allowed(:read_project_snippet) + expect_allowed(*author_permissions) end end end diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb index d5761390d39..0251d5dcf1c 100644 --- a/spec/policies/user_policy_spec.rb +++ b/spec/policies/user_policy_spec.rb @@ -4,34 +4,34 @@ describe UserPolicy, models: true do let(:current_user) { create(:user) } let(:user) { create(:user) } - subject { described_class.abilities(current_user, user).to_set } + subject { UserPolicy.new(current_user, user) } describe "reading a user's information" do - it { is_expected.to include(:read_user) } + it { is_expected.to be_allowed(:read_user) } end describe "destroying a user" do context "when a regular user tries to destroy another regular user" do - it { is_expected.not_to include(:destroy_user) } + it { is_expected.not_to be_allowed(:destroy_user) } end context "when a regular user tries to destroy themselves" do let(:current_user) { user } - it { is_expected.to include(:destroy_user) } + it { is_expected.to be_allowed(:destroy_user) } end context "when an admin user tries to destroy a regular user" do let(:current_user) { create(:user, :admin) } - it { is_expected.to include(:destroy_user) } + it { is_expected.to be_allowed(:destroy_user) } end context "when an admin user tries to destroy a ghost user" do let(:current_user) { create(:user, :admin) } let(:user) { create(:user, :ghost) } - it { is_expected.not_to include(:destroy_user) } + it { is_expected.not_to be_allowed(:destroy_user) } end end end diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb index f169e6661d1..1d8aaeea8f2 100644 --- a/spec/requests/api/features_spec.rb +++ b/spec/requests/api/features_spec.rb @@ -4,6 +4,13 @@ describe API::Features do let(:user) { create(:user) } let(:admin) { create(:admin) } + before do + Flipper.unregister_groups + Flipper.register(:perf_team) do |actor| + actor.respond_to?(:admin) && actor.admin? + end + end + describe 'GET /features' do let(:expected_features) do [ @@ -16,6 +23,14 @@ describe API::Features do 'name' => 'feature_2', 'state' => 'off', 'gates' => [{ 'key' => 'boolean', 'value' => false }] + }, + { + 'name' => 'feature_3', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'groups', 'value' => ['perf_team'] } + ] } ] end @@ -23,6 +38,7 @@ describe API::Features do before do Feature.get('feature_1').enable Feature.get('feature_2').disable + Feature.get('feature_3').enable Feature.group(:perf_team) end it 'returns a 401 for anonymous users' do @@ -47,30 +63,70 @@ describe API::Features do describe 'POST /feature' do let(:feature_name) { 'my_feature' } - it 'returns a 401 for anonymous users' do - post api("/features/#{feature_name}") - expect(response).to have_http_status(401) - end + context 'when the feature does not exist' do + it 'returns a 401 for anonymous users' do + post api("/features/#{feature_name}") - it 'returns a 403 for users' do - post api("/features/#{feature_name}", user) + expect(response).to have_http_status(401) + end - expect(response).to have_http_status(403) - end + it 'returns a 403 for users' do + post api("/features/#{feature_name}", user) - it 'creates an enabled feature if passed true' do - post api("/features/#{feature_name}", admin), value: 'true' + expect(response).to have_http_status(403) + end - expect(response).to have_http_status(201) - expect(Feature.get(feature_name)).to be_enabled - end + context 'when passed value=true' do + it 'creates an enabled feature' do + post api("/features/#{feature_name}", admin), value: 'true' - it 'creates a feature with the given percentage if passed an integer' do - post api("/features/#{feature_name}", admin), value: '50' + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'on', + 'gates' => [{ 'key' => 'boolean', 'value' => true }]) + end + + it 'creates an enabled feature for the given Flipper group when passed feature_group=perf_team' do + post api("/features/#{feature_name}", admin), value: 'true', feature_group: 'perf_team' + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'groups', 'value' => ['perf_team'] } + ]) + end + + it 'creates an enabled feature for the given user when passed user=username' do + post api("/features/#{feature_name}", admin), value: 'true', user: user.username - expect(response).to have_http_status(201) - expect(Feature.get(feature_name).percentage_of_time_value).to be(50) + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'actors', 'value' => ["User:#{user.id}"] } + ]) + end + end + + it 'creates a feature with the given percentage if passed an integer' do + post api("/features/#{feature_name}", admin), value: '50' + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'percentage_of_time', 'value' => 50 } + ]) + end end context 'when the feature exists' do @@ -80,11 +136,83 @@ describe API::Features do feature.disable # This also persists the feature on the DB end - it 'enables the feature if passed true' do - post api("/features/#{feature_name}", admin), value: 'true' + context 'when passed value=true' do + it 'enables the feature' do + post api("/features/#{feature_name}", admin), value: 'true' - expect(response).to have_http_status(201) - expect(feature).to be_enabled + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'on', + 'gates' => [{ 'key' => 'boolean', 'value' => true }]) + end + + it 'enables the feature for the given Flipper group when passed feature_group=perf_team' do + post api("/features/#{feature_name}", admin), value: 'true', feature_group: 'perf_team' + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'groups', 'value' => ['perf_team'] } + ]) + end + + it 'enables the feature for the given user when passed user=username' do + post api("/features/#{feature_name}", admin), value: 'true', user: user.username + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'actors', 'value' => ["User:#{user.id}"] } + ]) + end + end + + context 'when feature is enabled and value=false is passed' do + it 'disables the feature' do + feature.enable + expect(feature).to be_enabled + + post api("/features/#{feature_name}", admin), value: 'false' + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'off', + 'gates' => [{ 'key' => 'boolean', 'value' => false }]) + end + + it 'disables the feature for the given Flipper group when passed feature_group=perf_team' do + feature.enable(Feature.group(:perf_team)) + expect(Feature.get(feature_name).enabled?(admin)).to be_truthy + + post api("/features/#{feature_name}", admin), value: 'false', feature_group: 'perf_team' + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'off', + 'gates' => [{ 'key' => 'boolean', 'value' => false }]) + end + + it 'disables the feature for the given user when passed user=username' do + feature.enable(user) + expect(Feature.get(feature_name).enabled?(user)).to be_truthy + + post api("/features/#{feature_name}", admin), value: 'false', user: user.username + + expect(response).to have_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'off', + 'gates' => [{ 'key' => 'boolean', 'value' => false }]) + end end context 'with a pre-existing percentage value' do @@ -96,7 +224,13 @@ describe API::Features do post api("/features/#{feature_name}", admin), value: '30' expect(response).to have_http_status(201) - expect(Feature.get(feature_name).percentage_of_time_value).to be(30) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'percentage_of_time', 'value' => 30 } + ]) end end end diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index ac7ccfbaab0..213678c27f5 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -12,7 +12,6 @@ describe GitHooksService, services: true do @oldrev = sample_commit.parent_id @newrev = sample_commit.id @ref = 'refs/heads/feature' - @repo_path = project.repository.path_to_repo end describe '#execute' do @@ -21,7 +20,7 @@ describe GitHooksService, services: true do hook = double(trigger: [true, nil]) expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) - service.execute(user, @repo_path, @blankrev, @newrev, @ref) { } + service.execute(user, project, @blankrev, @newrev, @ref) { } end end @@ -31,7 +30,7 @@ describe GitHooksService, services: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + service.execute(user, project, @blankrev, @newrev, @ref) end.to raise_error(GitHooksService::PreReceiveError) end end @@ -43,7 +42,7 @@ describe GitHooksService, services: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + service.execute(user, project, @blankrev, @newrev, @ref) end.to raise_error(GitHooksService::PreReceiveError) end end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index ca827fc0f39..8e8816870e1 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -401,18 +401,6 @@ describe GitPushService, services: true do expect(SystemNoteService).not_to receive(:cross_reference) execute_service(project, commit_author, @oldrev, @newrev, @ref ) end - - it "doesn't close issues when external issue tracker is in use" do - allow_any_instance_of(Project).to receive(:default_issues_tracker?) - .and_return(false) - external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern) - allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker) - - # The push still shouldn't create cross-reference notes. - expect do - execute_service(project, commit_author, @oldrev, @newrev, 'refs/heads/hurf' ) - end.not_to change { Note.where(project_id: project.id, system: true).count } - end end context "to non-default branches" do diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index a37257d1bf4..d59b37bee36 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -15,6 +15,14 @@ describe Groups::DestroyService, services: true do group.add_user(user, Gitlab::Access::OWNER) end + def destroy_group(group, user, async) + if async + Groups::DestroyService.new(group, user).async_execute + else + Groups::DestroyService.new(group, user).execute + end + end + shared_examples 'group destruction' do |async| context 'database records' do before do @@ -30,30 +38,14 @@ describe Groups::DestroyService, services: true do context 'file system' do context 'Sidekiq inline' do before do - # Run sidekiq immediatly to check that renamed dir will be removed + # Run sidekiq immediately to check that renamed dir will be removed Sidekiq::Testing.inline! { destroy_group(group, user, async) } end - it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey } - it { expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey } - end - - context 'Sidekiq fake' do - before do - # Don't run sidekiq to check if renamed repository exists - Sidekiq::Testing.fake! { destroy_group(group, user, async) } + it 'verifies that paths have been deleted' do + expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey + expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey end - - it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey } - it { expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_truthy } - end - end - - def destroy_group(group, user, async) - if async - Groups::DestroyService.new(group, user).async_execute - else - Groups::DestroyService.new(group, user).execute end end end @@ -61,6 +53,26 @@ describe Groups::DestroyService, services: true do describe 'asynchronous delete' do it_behaves_like 'group destruction', true + context 'Sidekiq fake' do + before do + # Don't run Sidekiq to verify that group and projects are not actually destroyed + Sidekiq::Testing.fake! { destroy_group(group, user, true) } + end + + after do + # Clean up stale directories + gitlab_shell.rm_namespace(project.repository_storage_path, group.path) + gitlab_shell.rm_namespace(project.repository_storage_path, remove_path) + end + + it 'verifies original paths and projects still exist' do + expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_truthy + expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey + expect(Project.unscoped.count).to eq(1) + expect(Group.unscoped.count).to eq(2) + end + end + context 'potential race conditions' do context "when the `GroupDestroyWorker` task runs immediately" do it "deletes the group" do diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 76c52d55ae5..441a5276c56 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -30,6 +30,12 @@ describe Projects::TransferService, services: true do transfer_project(project, user, group) end + it 'expires full_path cache' do + expect(project).to receive(:expires_full_path_cache) + + transfer_project(project, user, group) + end + it 'executes system hooks' do expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks) diff --git a/spec/support/issue_tracker_service_shared_example.rb b/spec/support/issue_tracker_service_shared_example.rb index e70b3963d9d..a6ab03cb808 100644 --- a/spec/support/issue_tracker_service_shared_example.rb +++ b/spec/support/issue_tracker_service_shared_example.rb @@ -8,15 +8,15 @@ end RSpec.shared_examples 'allows project key on reference pattern' do |url_attr| it 'allows underscores in the project name' do - expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' + expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' end it 'allows numbers in the project name' do - expect(subject.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234' + expect(described_class.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234' end it 'requires the project name to begin with A-Z' do - expect(subject.reference_pattern.match('3EXT_EXT-1234')).to eq nil - expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' + expect(described_class.reference_pattern.match('3EXT_EXT-1234')).to eq nil + expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' end end diff --git a/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb new file mode 100644 index 00000000000..96c821b26f7 --- /dev/null +++ b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb @@ -0,0 +1,9 @@ +shared_examples 'issue sidebar stays collapsed on mobile' do + before do + resize_screen_xs + end + + it 'keeps the sidebar collapsed' do + expect(page).not_to have_css('.right-sidebar.right-sidebar-collapsed') + end +end |