diff options
Diffstat (limited to 'spec')
32 files changed, 841 insertions, 178 deletions
diff --git a/spec/controllers/groups/group_links_controller_spec.rb b/spec/controllers/groups/group_links_controller_spec.rb index 8f04822fee6..04f2e33b26a 100644 --- a/spec/controllers/groups/group_links_controller_spec.rb +++ b/spec/controllers/groups/group_links_controller_spec.rb @@ -111,4 +111,100 @@ describe Groups::GroupLinksController do end end end + + describe '#update' do + let!(:link) do + create(:group_group_link, { shared_group: shared_group, + shared_with_group: shared_with_group }) + end + + let(:expiry_date) { 1.month.from_now.to_date } + + subject do + post(:update, params: { group_id: shared_group, + id: link.id, + group_link: { group_access: Gitlab::Access::GUEST, + expires_at: expiry_date } }) + end + + context 'when user has admin access to the shared group' do + before do + shared_group.add_owner(user) + end + + it 'updates existing link' do + expect(link.group_access).to eq(Gitlab::Access::DEVELOPER) + expect(link.expires_at).to be_nil + + subject + + link.reload + + expect(link.group_access).to eq(Gitlab::Access::GUEST) + expect(link.expires_at).to eq(expiry_date) + end + end + + context 'when user does not have admin access to the shared group' do + it 'renders 404' do + subject + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(share_group_with_group: false) + end + + it 'renders 404' do + subject + + expect(response).to have_gitlab_http_status(404) + end + end + end + + describe '#destroy' do + let!(:link) do + create(:group_group_link, { shared_group: shared_group, + shared_with_group: shared_with_group }) + end + + subject do + post(:destroy, params: { group_id: shared_group, + id: link.id }) + end + + context 'when user has admin access to the shared group' do + before do + shared_group.add_owner(user) + end + + it 'deletes existing link' do + expect { subject }.to change(GroupGroupLink, :count).by(-1) + end + end + + context 'when user does not have admin access to the shared group' do + it 'renders 404' do + subject + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(share_group_with_group: false) + end + + it 'renders 404' do + subject + + expect(response).to have_gitlab_http_status(404) + end + end + end end diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index a144d9e0786..1c8a2bd160d 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -31,6 +31,12 @@ describe Groups::GroupMembersController do expect(assigns(:invited_members).map(&:invite_email)).to match_array(invited.map(&:invite_email)) end + it 'assigns skip groups' do + get :index, params: { group_id: group } + + expect(assigns(:skip_groups)).to match_array(group.related_group_ids) + end + it 'restricts search to one email' do get :index, params: { group_id: group, search_invited: invited.first.invite_email } diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb index b48b7dc86e0..cbcda5d0dc7 100644 --- a/spec/controllers/health_check_controller_spec.rb +++ b/spec/controllers/health_check_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe HealthCheckController do +describe HealthCheckController, :request_store do include StubENV let(:xml_response) { Hash.from_xml(response.body)['hash'] } @@ -18,7 +18,7 @@ describe HealthCheckController do describe 'GET #index' do context 'when services are up but accessed from outside whitelisted ips' do before do - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(not_whitelisted_ip) end it 'returns a not found page' do @@ -48,7 +48,7 @@ describe HealthCheckController do context 'when services are up and accessed from whitelisted ips' do before do - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(whitelisted_ip) end it 'supports successful plaintext response' do @@ -95,7 +95,7 @@ describe HealthCheckController do before do allow(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') allow(HealthCheck::Utils).to receive(:process_checks).with(['email']).and_return('Email is on fire') - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(whitelisted_ip) end it 'supports failure plaintext response' do diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 1d378b9b9dc..331eafba0d3 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MetricsController do +describe MetricsController, :request_store do include StubENV let(:metrics_multiproc_dir) { @metrics_multiproc_dir } @@ -53,7 +53,7 @@ describe MetricsController do context 'accessed from whitelisted ip' do before do - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(whitelisted_ip) end it_behaves_like 'endpoint providing metrics' @@ -61,7 +61,7 @@ describe MetricsController do context 'accessed from ip in whitelisted range' do before do - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(ip_in_whitelisted_range) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(ip_in_whitelisted_range) end it_behaves_like 'endpoint providing metrics' @@ -69,7 +69,7 @@ describe MetricsController do context 'accessed from not whitelisted ip' do before do - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(not_whitelisted_ip) end it 'returns the expected error response' do diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 257e5cb8bf0..9a4889a0335 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -167,14 +167,14 @@ describe 'Admin Groups' do it 'adds admin a to a group as developer', :js do visit group_group_members_path(group) - page.within '.users-group-form' do + page.within '.invite-users-form' do select2(current_user.id, from: '#user_ids', multiple: true) select 'Developer', from: 'access_level' end - click_button 'Add to group' + click_button 'Invite' - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).to have_content(current_user.name) expect(page).to have_content('Developer') end @@ -187,7 +187,7 @@ describe 'Admin Groups' do visit group_group_members_path(group) - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).to have_content(current_user.name) expect(page).to have_content('Developer') end @@ -196,7 +196,7 @@ describe 'Admin Groups' do visit group_group_members_path(group) - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).not_to have_content(current_user.name) expect(page).not_to have_content('Developer') end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 7c40ac5bde3..d1889d3a89a 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -98,12 +98,12 @@ describe "Admin::Projects" do it 'adds admin a to a project as developer', :js do visit project_project_members_path(project) - page.within '.users-project-form' do + page.within '.invite-users-form' do select2(current_user.id, from: '#user_ids', multiple: true) select 'Developer', from: 'access_level' end - click_button 'Add to project' + click_button 'Invite' page.within '.content-list' do expect(page).to have_content(current_user.name) diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb new file mode 100644 index 00000000000..55f9418521f --- /dev/null +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Groups > Members > Manage groups', :js do + include Select2Helper + include Spec::Support::Helpers::Features::ListRowsHelpers + + let(:user) { create(:user) } + let(:shared_with_group) { create(:group) } + let(:shared_group) { create(:group) } + + before do + shared_group.add_owner(user) + sign_in(user) + end + + context 'with share groups with groups feature flag' do + before do + stub_feature_flags(shared_with_group: true) + end + + it 'add group to group' do + visit group_group_members_path(shared_group) + + add_group(shared_with_group.id, 'Reporter') + + page.within(first_row) do + expect(page).to have_content(shared_with_group.name) + expect(page).to have_content('Reporter') + end + end + + it 'remove user from group' do + create(:group_group_link, shared_group: shared_group, + shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER) + + visit group_group_members_path(shared_group) + + expect(page).to have_content(shared_with_group.name) + + accept_confirm do + find(:css, '#existing_shares li', text: shared_with_group.name).find(:css, 'a.btn-remove').click + end + + wait_for_requests + + expect(page).not_to have_content(shared_with_group.name) + end + + it 'update group to owner level' do + create(:group_group_link, shared_group: shared_group, + shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER) + + visit group_group_members_path(shared_group) + + page.within(first_row) do + click_button('Developer') + click_link('Maintainer') + + wait_for_requests + + expect(page).to have_button('Maintainer') + end + end + + def add_group(id, role) + page.click_link 'Invite group' + page.within ".invite-group-form" do + select2(id, from: "#shared_with_group_id") + select(role, from: "shared_group_access") + click_button "Invite" + end + end + end + + context 'without share groups with groups feature flag' do + before do + stub_feature_flags(share_group_with_group: false) + end + + it 'does not render invitation form and tabs' do + visit group_group_members_path(shared_group) + + expect(page).not_to have_link('Invite member') + expect(page).not_to have_link('Invite group') + end + end +end diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index cdd16ae9441..e4ba3022d8b 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -113,7 +113,8 @@ describe 'Groups > Members > Manage members' do visit group_group_members_path(group) - expect(page).not_to have_button 'Add to group' + expect(page).not_to have_selector '.invite-users-form' + expect(page).not_to have_selector '.invite-group-form' page.within(second_row) do # Can not modify user2 role @@ -125,11 +126,10 @@ describe 'Groups > Members > Manage members' do end def add_user(id, role) - page.within ".users-group-form" do + page.within ".invite-users-form" do select2(id, from: "#user_ids", multiple: true) select(role, from: "access_level") + click_button "Invite" end - - click_button "Add to group" end end diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb index 9c17aac09e8..fda129ce422 100644 --- a/spec/features/groups/members/search_members_spec.rb +++ b/spec/features/groups/members/search_members_spec.rb @@ -24,7 +24,7 @@ describe 'Search group member' do find('.user-search-btn').click end - group_members_list = find(".card .content-list") + group_members_list = find('[data-qa-selector="members_list"]') expect(group_members_list).to have_content(member.name) expect(group_members_list).not_to have_content(user.name) end diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index 6d92c777033..84000ef73ce 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -87,12 +87,12 @@ describe 'Project members list' do end def add_user(id, role) - page.within ".users-project-form" do + page.within ".invite-users-form" do select2(id, from: "#user_ids", multiple: true) select(role, from: "access_level") end - click_button "Add to project" + click_button "Invite" end def visit_members_page diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 501dd05300a..cbcd03b33ce 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -20,10 +20,10 @@ describe 'Projects > Members > Maintainer adds member with expiration date', :js date = 4.days.from_now visit project_project_members_path(project) - page.within '.users-project-form' do + page.within '.invite-users-form' do select2(new_member.id, from: '#user_ids', multiple: true) fill_in 'expires_at', with: date.to_s(:medium) + "\n" - click_on 'Add to project' + click_on 'Invite' end page.within "#project_member_#{new_member.project_members.first.id}" do diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb index 6d94388a6e2..705c60f15ee 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/settings/user_manages_project_members_spec.rb @@ -37,7 +37,7 @@ describe 'Projects > Settings > User manages project members' do visit(project_project_members_path(project)) - page.within('.users-project-form') do + page.within('.invite-users-form') do click_link('Import') end diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index f161a1df9c3..5636b5db84f 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -10,6 +10,7 @@ describe GroupMembersFinder, '#execute' do let(:user2) { create(:user) } let(:user3) { create(:user) } let(:user4) { create(:user) } + let(:user5) { create(:user, :two_factor_via_otp) } it 'returns members for top-level group' do member1 = group.add_maintainer(user1) @@ -67,4 +68,43 @@ describe GroupMembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member2, member3, member4]) end + + it 'returns searched members if requested' do + group.add_maintainer(user2) + nested_group.add_maintainer(user2) + nested_group.add_maintainer(user3) + nested_group.add_maintainer(user4) + member = group.add_maintainer(user1) + + result = described_class.new(group).execute(include_relations: [:direct, :descendants], params: { search: user1.name }) + + expect(result.to_a).to match_array([member]) + end + + it 'returns members with two-factor auth if requested by owner' do + group.add_owner(user2) + group.add_maintainer(user1) + nested_group.add_maintainer(user2) + nested_group.add_maintainer(user3) + nested_group.add_maintainer(user4) + member = group.add_maintainer(user5) + + result = described_class.new(group, user2).execute(include_relations: [:direct, :descendants], params: { two_factor: 'enabled' }) + + expect(result.to_a).to contain_exactly(member) + end + + it 'returns members without two-factor auth if requested by owner' do + member1 = group.add_owner(user2) + member2 = group.add_maintainer(user1) + nested_group.add_maintainer(user2) + member3 = nested_group.add_maintainer(user3) + member4 = nested_group.add_maintainer(user4) + member_with_2fa = group.add_maintainer(user5) + + result = described_class.new(group, user2).execute(include_relations: [:direct, :descendants], params: { two_factor: 'disabled' }) + + expect(result.to_a).not_to include(member_with_2fa) + expect(result.to_a).to match_array([member1, member2, member3, member4]) + end end diff --git a/spec/fixtures/emails/envelope_to_header.eml b/spec/fixtures/emails/envelope_to_header.eml new file mode 100644 index 00000000000..4b6418d4c06 --- /dev/null +++ b/spec/fixtures/emails/envelope_to_header.eml @@ -0,0 +1,32 @@ +Return-Path: <jake@example.com> +Received: from myserver.example.com ([unix socket]) by myserver (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail.example.com (mail.example.com [IPv6:2607:f8b0:4001:c03::234]) by myserver.example.com (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@example.com>; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by myserver.example.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.example.com>; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +From: "jake@example.com" <jake@example.com> +To: "support@example.com" <support@example.com> +Subject: Insert hilarious subject line here +Date: Tue, 26 Nov 2019 14:22:41 +0000 +Message-ID: <7e2296f83dbf4de388cbf5f56f52c11f@EXDAG29-1.EXCHANGE.INT> +Accept-Language: de-DE, en-US +Content-Language: de-DE +X-MS-Has-Attach: +X-MS-TNEF-Correlator: +x-ms-exchange-transport-fromentityheader: Hosted +x-originating-ip: [62.96.54.178] +Content-Type: multipart/alternative; + boundary="_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_" +MIME-Version: 1.0 +Envelope-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com + +--_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_ +Content-Type: text/plain; charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + + + +--_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_ +Content-Type: text/html; charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +Look, a message with some alternate headers! We should really support them. diff --git a/spec/fixtures/emails/forwarded_new_issue.eml b/spec/fixtures/emails/forwarded_new_issue.eml index 258106bb897..e3688697651 100644 --- a/spec/fixtures/emails/forwarded_new_issue.eml +++ b/spec/fixtures/emails/forwarded_new_issue.eml @@ -1,13 +1,13 @@ -Delivered-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.adventuretime.ooo -Return-Path: <jake@adventuretime.ooo> -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700 +Delivered-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com +Return-Path: <jake@example.com> +Received: from iceking.example.com ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.example.com (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.example.com>; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.example.com>; Thu, 13 Jun 2013 14:03:48 -0700 Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog <jake@adventuretime.ooo> -Delivered-To: support@adventuretime.ooo -To: support@adventuretime.ooo +From: Jake the Dog <jake@example.com> +Delivered-To: support@example.com +To: support@example.com Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> Subject: New Issue by email Mime-Version: 1.0 diff --git a/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap index 9724033f3c9..bef4674bd8b 100644 --- a/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap +++ b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap @@ -1,10 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Settings Form renders 1`] = ` -<div - class="card" -> - <form> +<form> + <div + class="card" + > + <!----> <div class="card-header" > @@ -12,11 +13,13 @@ exports[`Settings Form renders 1`] = ` Tag expiration policy </div> - <div class="card-body" > - <gl-form-group-stub + <!----> + <!----> + + <glformgroup-stub id="expiration-policy-toggle-group" label="Expiration policy:" label-align="right" @@ -26,7 +29,7 @@ exports[`Settings Form renders 1`] = ` <div class="d-flex align-items-start" > - <gl-toggle-stub + <gltoggle-stub id="expiration-policy-toggle" labeloff="Toggle Status: OFF" labelon="Toggle Status: ON" @@ -41,81 +44,96 @@ exports[`Settings Form renders 1`] = ` </strong> </span> </div> - </gl-form-group-stub> + </glformgroup-stub> - <gl-form-group-stub + <glformgroup-stub id="expiration-policy-interval-group" label="Expiration interval:" label-align="right" label-cols="3" label-for="expiration-policy-interval" > - <gl-form-select-stub + <glformselect-stub + disabled="true" id="expiration-policy-interval" + value="bar" > <option - value="1" + value="foo" > - Option 1 + + Foo + </option> - <option - value="2" + value="bar" > - Option 2 + + Bar + </option> - </gl-form-select-stub> - </gl-form-group-stub> + </glformselect-stub> + </glformgroup-stub> - <gl-form-group-stub + <glformgroup-stub id="expiration-policy-schedule-group" label="Expiration schedule:" label-align="right" label-cols="3" label-for="expiration-policy-schedule" > - <gl-form-select-stub + <glformselect-stub + disabled="true" id="expiration-policy-schedule" + value="bar" > <option - value="1" + value="foo" > - Option 1 + + Foo + </option> - <option - value="2" + value="bar" > - Option 2 + + Bar + </option> - </gl-form-select-stub> - </gl-form-group-stub> + </glformselect-stub> + </glformgroup-stub> - <gl-form-group-stub + <glformgroup-stub id="expiration-policy-latest-group" label="Expiration latest:" label-align="right" label-cols="3" label-for="expiration-policy-latest" > - <gl-form-select-stub + <glformselect-stub + disabled="true" id="expiration-policy-latest" + value="bar" > <option - value="1" + value="foo" > - Option 1 + + Foo + </option> - <option - value="2" + value="bar" > - Option 2 + + Bar + </option> - </gl-form-select-stub> - </gl-form-group-stub> + </glformselect-stub> + </glformgroup-stub> - <gl-form-group-stub + <glformgroup-stub id="expiration-policy-name-matching-group" invalid-feedback="The value of this input should be less than 255 characters" label="Expire Docker tags with name matching:" @@ -123,33 +141,41 @@ exports[`Settings Form renders 1`] = ` label-cols="3" label-for="expiration-policy-name-matching" > - <gl-form-textarea-stub + <glformtextarea-stub + disabled="true" id="expiration-policy-name-matching" placeholder=".*" trim="" value="" /> - </gl-form-group-stub> + </glformgroup-stub> + </div> - <div - class="card-footer text-right" + class="card-footer" > - <gl-button-stub - type="reset" - > - Cancel - </gl-button-stub> - - <gl-button-stub - type="submit" - variant="success" + <div + class="d-flex justify-content-end" > + <glbutton-stub + class="mr-2 d-block" + type="reset" + > + Cancel + </glbutton-stub> + + <glbutton-stub + class="d-block" + type="submit" + variant="success" + > + + Save expiration policy - Save Expiration Policy - - </gl-button-stub> + </glbutton-stub> + </div> </div> - </form> -</div> + <!----> + </div> +</form> `; diff --git a/spec/frontend/registry/settings/components/settings_form_spec.js b/spec/frontend/registry/settings/components/settings_form_spec.js index b944e5a2cea..bd733e965a4 100644 --- a/spec/frontend/registry/settings/components/settings_form_spec.js +++ b/spec/frontend/registry/settings/components/settings_form_spec.js @@ -1,8 +1,10 @@ import Vuex from 'vuex'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { mount, createLocalVue } from '@vue/test-utils'; +import stubChildren from 'helpers/stub_children'; import component from '~/registry/settings/components/settings_form.vue'; import { createStore } from '~/registry/settings/store/'; import { NAME_REGEX_LENGTH } from '~/registry/settings/constants'; +import { stringifiedFormOptions } from '../mock_data'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -13,7 +15,6 @@ describe('Settings Form', () => { let saveSpy; let resetSpy; - const helpPagePath = 'foo'; const findFormGroup = name => wrapper.find(`#expiration-policy-${name}-group`); const findFormElements = (name, father = wrapper) => father.find(`#expiration-policy-${name}`); const findCancelButton = () => wrapper.find({ ref: 'cancel-button' }); @@ -23,7 +24,11 @@ describe('Settings Form', () => { const mountComponent = (options = {}) => { saveSpy = jest.fn(); resetSpy = jest.fn(); - wrapper = shallowMount(component, { + wrapper = mount(component, { + stubs: { + ...stubChildren(component), + GlCard: false, + }, store, methods: { saveSettings: saveSpy, @@ -35,7 +40,7 @@ describe('Settings Form', () => { beforeEach(() => { store = createStore(); - store.dispatch('setInitialState', { helpPagePath }); + store.dispatch('setInitialState', stringifiedFormOptions); mountComponent(); }); @@ -48,13 +53,13 @@ describe('Settings Form', () => { }); describe.each` - elementName | modelName | value - ${'toggle'} | ${'enabled'} | ${true} - ${'interval'} | ${'older_than'} | ${'foo'} - ${'schedule'} | ${'cadence'} | ${'foo'} - ${'latest'} | ${'keep_n'} | ${'foo'} - ${'name-matching'} | ${'name_regex'} | ${'foo'} - `('%s form element', ({ elementName, modelName, value }) => { + elementName | modelName | value | disabledByToggle + ${'toggle'} | ${'enabled'} | ${true} | ${'not disabled'} + ${'interval'} | ${'older_than'} | ${'foo'} | ${'disabled'} + ${'schedule'} | ${'cadence'} | ${'foo'} | ${'disabled'} + ${'latest'} | ${'keep_n'} | ${'foo'} | ${'disabled'} + ${'name-matching'} | ${'name_regex'} | ${'foo'} | ${'disabled'} + `('$elementName form element', ({ elementName, modelName, value, disabledByToggle }) => { let formGroup; beforeEach(() => { formGroup = findFormGroup(elementName); @@ -89,6 +94,12 @@ describe('Settings Form', () => { expect(wrapper.vm[modelName]).toBe(value); }); }); + + it(`${elementName} is ${disabledByToggle} by enabled set to false`, () => { + store.dispatch('updateSettings', { enabled: false }); + const expectation = disabledByToggle === 'disabled' ? 'true' : undefined; + expect(findFormElements(elementName, formGroup).attributes('disabled')).toBe(expectation); + }); }); describe('form actions', () => { diff --git a/spec/frontend/registry/settings/mock_data.js b/spec/frontend/registry/settings/mock_data.js new file mode 100644 index 00000000000..411363c2c95 --- /dev/null +++ b/spec/frontend/registry/settings/mock_data.js @@ -0,0 +1,12 @@ +export const options = [{ key: 'foo', label: 'Foo' }, { key: 'bar', label: 'Bar', default: true }]; +export const stringifiedOptions = JSON.stringify(options); +export const stringifiedFormOptions = { + cadenceOptions: stringifiedOptions, + keepNOptions: stringifiedOptions, + olderThanOptions: stringifiedOptions, +}; +export const formOptions = { + cadence: options, + keepN: options, + olderThan: options, +}; diff --git a/spec/frontend/registry/settings/store/mutations_spec.js b/spec/frontend/registry/settings/store/mutations_spec.js index a8c7ed3bafa..1a0effbe125 100644 --- a/spec/frontend/registry/settings/store/mutations_spec.js +++ b/spec/frontend/registry/settings/store/mutations_spec.js @@ -1,6 +1,7 @@ import mutations from '~/registry/settings/store/mutations'; import * as types from '~/registry/settings/store/mutation_types'; import createState from '~/registry/settings/store/state'; +import { formOptions, stringifiedFormOptions } from '../mock_data'; describe('Mutations Registry Store', () => { let mockState; @@ -11,11 +12,14 @@ describe('Mutations Registry Store', () => { describe('SET_INITIAL_STATE', () => { it('should set the initial state', () => { - const payload = { helpPagePath: 'foo', projectId: 'bar' }; - const expectedState = { ...mockState, ...payload }; - mutations[types.SET_INITIAL_STATE](mockState, payload); + const expectedState = { ...mockState, projectId: 'foo', formOptions }; + mutations[types.SET_INITIAL_STATE](mockState, { + projectId: 'foo', + ...stringifiedFormOptions, + }); expect(mockState.projectId).toEqual(expectedState.projectId); + expect(mockState.formOptions).toEqual(expectedState.formOptions); }); }); diff --git a/spec/frontend/vuex_shared/bindings_spec.js b/spec/frontend/vuex_shared/bindings_spec.js index 022d9e8e84c..0f91a09018f 100644 --- a/spec/frontend/vuex_shared/bindings_spec.js +++ b/spec/frontend/vuex_shared/bindings_spec.js @@ -3,49 +3,77 @@ import { mapComputed } from '~/vuex_shared/bindings'; describe('Binding utils', () => { describe('mapComputed', () => { - const dummyComponent = { + const defaultArgs = [['baz'], 'bar', 'foo']; + + const createDummy = (mapComputedArgs = defaultArgs) => ({ computed: { - ...mapComputed('foo', 'bar', ['baz']), + ...mapComputed(...mapComputedArgs), }, render() { return null; }, + }); + + const mocks = { + $store: { + state: { + baz: 2, + foo: { + baz: 1, + }, + }, + getters: { + getBaz: 'foo', + }, + dispatch: jest.fn(), + }, }; - it('returns an object with keys equal to the last fn parameter ', () => { + + it('returns an object with keys equal to the first fn parameter ', () => { const keyList = ['foo1', 'foo2']; - const result = mapComputed('foo', 'bar', keyList); + const result = mapComputed(keyList, 'foo', 'bar'); expect(Object.keys(result)).toEqual(keyList); }); + it('returned object has set and get function', () => { - const result = mapComputed('foo', 'bar', ['baz']); + const result = mapComputed(['baz'], 'foo', 'bar'); expect(result.baz.set).toBeDefined(); expect(result.baz.get).toBeDefined(); }); - it('set function invokes $store.dispatch', () => { - const context = shallowMount(dummyComponent, { - mocks: { - $store: { - dispatch: jest.fn(), - }, - }, + describe('set function', () => { + it('invokes $store.dispatch', () => { + const context = shallowMount(createDummy(), { mocks }); + context.vm.baz = 'a'; + expect(context.vm.$store.dispatch).toHaveBeenCalledWith('bar', { baz: 'a' }); + }); + it('uses updateFn in list object mode if updateFn exists', () => { + const context = shallowMount(createDummy([[{ key: 'foo', updateFn: 'baz' }]]), { mocks }); + context.vm.foo = 'b'; + expect(context.vm.$store.dispatch).toHaveBeenCalledWith('baz', { foo: 'b' }); + }); + it('in list object mode defaults to defaultUpdateFn if updateFn do not exists', () => { + const context = shallowMount(createDummy([[{ key: 'foo' }], 'defaultFn']), { mocks }); + context.vm.foo = 'c'; + expect(context.vm.$store.dispatch).toHaveBeenCalledWith('defaultFn', { foo: 'c' }); }); - context.vm.baz = 'a'; - expect(context.vm.$store.dispatch).toHaveBeenCalledWith('bar', { baz: 'a' }); }); - it('get function returns $store.state[root][key]', () => { - const context = shallowMount(dummyComponent, { - mocks: { - $store: { - state: { - foo: { - baz: 1, - }, - }, - }, - }, + + describe('get function', () => { + it('if root is set returns $store.state[root][key]', () => { + const context = shallowMount(createDummy(), { mocks }); + expect(context.vm.baz).toBe(mocks.$store.state.foo.baz); + }); + + it('if root is not set returns $store.state[key]', () => { + const context = shallowMount(createDummy([['baz'], 'bar']), { mocks }); + expect(context.vm.baz).toBe(mocks.$store.state.baz); + }); + + it('when using getters it invoke the appropriate getter', () => { + const context = shallowMount(createDummy([[{ getter: 'getBaz', key: 'baz' }]]), { mocks }); + expect(context.vm.baz).toBe(mocks.$store.getters.getBaz); }); - expect(context.vm.baz).toBe(1); }); }); }); diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb index 629ac34edc1..e5deeb990a1 100644 --- a/spec/helpers/application_settings_helper_spec.rb +++ b/spec/helpers/application_settings_helper_spec.rb @@ -76,6 +76,20 @@ describe ApplicationSettingsHelper do ) end + it 'returns delete_self_monitoring_project_path' do + expect(helper.self_monitoring_project_data).to include( + 'delete_self_monitoring_project_path' => + delete_self_monitoring_project_admin_application_settings_path + ) + end + + it 'returns status_delete_self_monitoring_project_path' do + expect(helper.self_monitoring_project_data).to include( + 'status_delete_self_monitoring_project_path' => + status_delete_self_monitoring_project_admin_application_settings_path + ) + end + it 'returns self_monitoring_project_exists false' do expect(helper.self_monitoring_project_data).to include( 'self_monitoring_project_exists' => false diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index 43c73242f5f..018219e5647 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -5,22 +5,27 @@ require 'spec_helper' describe Gitlab::Email::Receiver do include_context :email_shared_context - context "when the email contains a valid email address in a Delivered-To header" do - let(:email_raw) { fixture_file('emails/forwarded_new_issue.eml') } + context 'when the email contains a valid email address in a header' do let(:handler) { double(:handler) } before do - stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo") - allow(handler).to receive(:execute) allow(handler).to receive(:metrics_params) allow(handler).to receive(:metrics_event) + + stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.example.com") + end + + context 'when in a Delivered-To header' do + let(:email_raw) { fixture_file('emails/forwarded_new_issue.eml') } + + it_behaves_like 'correctly finds the mail key' end - it "finds the mail key" do - expect(Gitlab::Email::Handler).to receive(:for).with(an_instance_of(Mail::Message), 'gitlabhq/gitlabhq+auth_token').and_return(handler) + context 'when in an Envelope-To header' do + let(:email_raw) { fixture_file('emails/envelope_to_header.eml') } - receiver.execute + it_behaves_like 'correctly finds the mail key' end end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 0d9719a5663..ebf56c0ae66 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -229,6 +229,59 @@ describe Gitlab::GitalyClient do end end end + + context 'deadlines', :request_store do + let(:request_deadline) { real_time + 10.0 } + + before do + allow(Gitlab::RequestContext.instance).to receive(:request_deadline).and_return(request_deadline) + end + + it 'includes the deadline information' do + kword_args = described_class.request_kwargs('default', timeout: 2) + + expect(kword_args[:deadline]) + .to be_within(1).of(real_time + 2) + expect(kword_args[:metadata][:deadline_type]).to eq("regular") + end + + it 'limits the deadline do the request deadline if that is closer', :aggregate_failures do + kword_args = described_class.request_kwargs('default', timeout: 15) + + expect(kword_args[:deadline]).to eq(request_deadline) + expect(kword_args[:metadata][:deadline_type]).to eq("limited") + end + + it 'does not limit calls in sidekiq' do + expect(Sidekiq).to receive(:server?).and_return(true) + + kword_args = described_class.request_kwargs('default', timeout: 6.hours.to_i) + + expect(kword_args[:deadline]).to be_within(1).of(real_time + 6.hours.to_i) + expect(kword_args[:metadata][:deadline_type]).to be_nil + end + + it 'does not limit calls in sidekiq when allowed unlimited' do + expect(Sidekiq).to receive(:server?).and_return(true) + + kword_args = described_class.request_kwargs('default', timeout: 0) + + expect(kword_args[:deadline]).to be_nil + expect(kword_args[:metadata][:deadline_type]).to be_nil + end + + it 'includes only the deadline specified by the timeout when there was no deadline' do + allow(Gitlab::RequestContext.instance).to receive(:request_deadline).and_return(nil) + kword_args = described_class.request_kwargs('default', timeout: 6.hours.to_i) + + expect(kword_args[:deadline]).to be_within(1).of(Gitlab::Metrics::System.real_time + 6.hours.to_i) + expect(kword_args[:metadata][:deadline_type]).to be_nil + end + + def real_time + Gitlab::Metrics::System.real_time + end + end end describe 'enforce_gitaly_request_limits?' do diff --git a/spec/lib/gitlab/middleware/request_context_spec.rb b/spec/lib/gitlab/middleware/request_context_spec.rb new file mode 100644 index 00000000000..1ed06a97c1e --- /dev/null +++ b/spec/lib/gitlab/middleware/request_context_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true +require 'fast_spec_helper' +require 'rack' +require 'request_store' +require_relative '../../../support/helpers/next_instance_of' + +describe Gitlab::Middleware::RequestContext do + include NextInstanceOf + + let(:app) { -> (env) {} } + let(:env) { {} } + + around do |example| + RequestStore.begin! + example.run + RequestStore.end! + RequestStore.clear! + end + + describe '#call' do + context 'setting the client ip' do + subject { Gitlab::RequestContext.instance.client_ip } + + context 'with X-Forwarded-For headers' do + let(:load_balancer_ip) { '1.2.3.4' } + let(:headers) do + { + 'HTTP_X_FORWARDED_FOR' => "#{load_balancer_ip}, 127.0.0.1", + 'REMOTE_ADDR' => '127.0.0.1' + } + end + + let(:env) { Rack::MockRequest.env_for("/").merge(headers) } + + it 'returns the load balancer IP' do + endpoint = proc do + [200, {}, ["Hello"]] + end + + described_class.new(endpoint).call(env) + + expect(subject).to eq(load_balancer_ip) + end + end + + context 'request' do + let(:ip) { '192.168.1.11' } + + before do + allow_next_instance_of(Rack::Request) do |instance| + allow(instance).to receive(:ip).and_return(ip) + end + described_class.new(app).call(env) + end + + it { is_expected.to eq(ip) } + end + + context 'before RequestContext middleware run' do + it { is_expected.to be_nil } + end + end + end + + context 'setting the thread cpu time' do + it 'sets the `start_thread_cpu_time`' do + expect { described_class.new(app).call(env) } + .to change { Gitlab::RequestContext.instance.start_thread_cpu_time }.from(nil).to(Float) + end + end + + context 'setting the request start time' do + it 'sets the `request_start_time`' do + expect { described_class.new(app).call(env) } + .to change { Gitlab::RequestContext.instance.request_start_time }.from(nil).to(Float) + end + end +end diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb index 87b8029de2e..1290071549d 100644 --- a/spec/lib/gitlab/request_context_spec.rb +++ b/spec/lib/gitlab/request_context_spec.rb @@ -2,59 +2,44 @@ require 'spec_helper' -describe Gitlab::RequestContext do - describe '#client_ip' do - subject { described_class.client_ip } +describe Gitlab::RequestContext, :request_store do + subject { described_class.instance } - let(:app) { -> (env) {} } - let(:env) { Hash.new } + it { is_expected.to have_attributes(client_ip: nil, start_thread_cpu_time: nil, request_start_time: nil) } - context 'with X-Forwarded-For headers', :request_store do - let(:load_balancer_ip) { '1.2.3.4' } - let(:headers) do - { - 'HTTP_X_FORWARDED_FOR' => "#{load_balancer_ip}, 127.0.0.1", - 'REMOTE_ADDR' => '127.0.0.1' - } - end + describe '#request_deadline' do + let(:request_start_time) { 1575982156.206008 } - let(:env) { Rack::MockRequest.env_for("/").merge(headers) } + it "sets the time to #{Settings.gitlab.max_request_duration_seconds} seconds in the future" do + allow(subject).to receive(:request_start_time).and_return(request_start_time) - it 'returns the load balancer IP' do - client_ip = nil - - endpoint = proc do - client_ip = Gitlab::SafeRequestStore[:client_ip] - [200, {}, ["Hello"]] - end + expect(subject.request_deadline).to eq(1575982156.206008 + Settings.gitlab.max_request_duration_seconds) + expect(subject.request_deadline).to be_a(Float) + end - described_class.new(endpoint).call(env) + it 'returns nil if there is no start time' do + allow(subject).to receive(:request_start_time).and_return(nil) - expect(client_ip).to eq(load_balancer_ip) - end + expect(subject.request_deadline).to be_nil end + end - context 'when RequestStore::Middleware is used' do - around do |example| - RequestStore::Middleware.new(-> (env) { example.run }).call({}) - end + describe '#ensure_request_deadline_not_exceeded!' do + it 'does not raise an error when there was no deadline' do + expect(subject).to receive(:request_deadline).and_return(nil) + expect { subject.ensure_deadline_not_exceeded! }.not_to raise_error + end - context 'request' do - let(:ip) { '192.168.1.11' } + it 'does not raise an error if the deadline is in the future' do + allow(subject).to receive(:request_deadline).and_return(Gitlab::Metrics::System.real_time + 10) - before do - allow_next_instance_of(Rack::Request) do |instance| - allow(instance).to receive(:ip).and_return(ip) - end - described_class.new(app).call(env) - end + expect { subject.ensure_deadline_not_exceeded! }.not_to raise_error + end - it { is_expected.to eq(ip) } - end + it 'raises an error when the deadline is in the past' do + allow(subject).to receive(:request_deadline).and_return(Gitlab::Metrics::System.real_time - 10) - context 'before RequestContext middleware run' do - it { is_expected.to be_nil } - end + expect { subject.ensure_deadline_not_exceeded! }.to raise_error(described_class::RequestDeadlineExceeded) end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 52e60a69a52..bbd50f1c0ef 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -319,6 +319,11 @@ describe ApplicationSetting do end context 'gitaly timeouts' do + it "validates that the default_timeout is lower than the max_request_duration" do + is_expected.to validate_numericality_of(:gitaly_timeout_default) + .is_less_than_or_equal_to(Settings.gitlab.max_request_duration_seconds) + end + [:gitaly_timeout_default, :gitaly_timeout_medium, :gitaly_timeout_fast].each do |timeout_name| it do is_expected.to validate_presence_of(timeout_name) diff --git a/spec/models/group_group_link_spec.rb b/spec/models/group_group_link_spec.rb index e4ad5703a10..a877cc803dd 100644 --- a/spec/models/group_group_link_spec.rb +++ b/spec/models/group_group_link_spec.rb @@ -33,4 +33,12 @@ describe GroupGroupLink do validate_inclusion_of(:group_access).in_array(Gitlab::Access.values)) end end + + describe '#human_access' do + it 'delegates to Gitlab::Access' do + expect(Gitlab::Access).to receive(:human_access).with(group_group_link.group_access) + + group_group_link.human_access + end + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 842acf92c2a..3531c695236 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1003,6 +1003,57 @@ describe Group do end end + describe '#related_group_ids' do + let(:nested_group) { create(:group, parent: group) } + let(:shared_with_group) { create(:group, parent: group) } + + before do + create(:group_group_link, shared_group: nested_group, + shared_with_group: shared_with_group) + end + + subject(:related_group_ids) { nested_group.related_group_ids } + + it 'returns id' do + expect(related_group_ids).to include(nested_group.id) + end + + it 'returns ancestor id' do + expect(related_group_ids).to include(group.id) + end + + it 'returns shared with group id' do + expect(related_group_ids).to include(shared_with_group.id) + end + + context 'with more than one ancestor group' do + let(:ancestor_group) { create(:group) } + + before do + group.update(parent: ancestor_group) + end + + it 'returns all ancestor group ids' do + expect(related_group_ids).to( + include(group.id, ancestor_group.id)) + end + end + + context 'with more than one shared with group' do + let(:another_shared_with_group) { create(:group, parent: group) } + + before do + create(:group_group_link, shared_group: nested_group, + shared_with_group: another_shared_with_group) + end + + it 'returns all shared with group ids' do + expect(related_group_ids).to( + include(shared_with_group.id, another_shared_with_group.id)) + end + end + end + context 'with uploads' do it_behaves_like 'model with uploads', true do let(:model_object) { create(:group, :with_avatar) } diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb index f3ffa8e0ea4..d562a34aec4 100644 --- a/spec/requests/self_monitoring_project_spec.rb +++ b/spec/requests/self_monitoring_project_spec.rb @@ -60,7 +60,7 @@ describe 'Self-Monitoring project requests' do end it_behaves_like 'sets polling header and returns accepted' do - let(:in_progress_message) { 'Job is in progress' } + let(:in_progress_message) { 'Job to create self-monitoring project is in progress' } end end @@ -115,4 +115,110 @@ describe 'Self-Monitoring project requests' do end end end + + describe 'DELETE #delete_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectDeleteWorker } + + subject { delete delete_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + let(:status_api) { status_delete_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'triggers async worker, returns sidekiq job_id with response accepted' + end + end + end + + describe 'GET #status_delete_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectDeleteWorker } + let(:job_id) { 'job_id' } + + subject do + get status_delete_self_monitoring_project_admin_application_settings_path, + params: { job_id: job_id } + end + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + it_behaves_like 'handles invalid job_id' + + context 'when job is in progress' do + before do + allow(worker_class).to receive(:in_progress?) + .with(job_id) + .and_return(true) + + stub_application_setting(instance_administration_project_id: 1) + end + + it_behaves_like 'sets polling header and returns accepted' do + let(:in_progress_message) { 'Job to delete self-monitoring project is in progress' } + end + end + + context 'when self-monitoring project exists and job does not exist' do + before do + stub_application_setting(instance_administration_project_id: 1) + end + + it 'returns bad_request' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq( + 'message' => 'Self-monitoring project was not deleted. Please check logs ' \ + 'for any error messages' + ) + end + end + end + + context 'when self-monitoring project does not exist' do + it 'does not need job_id' do + get status_delete_self_monitoring_project_admin_application_settings_path + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'message' => 'Self-monitoring project has been successfully deleted' + ) + end + end + + it 'returns success with job_id' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'message' => 'Self-monitoring project has been successfully deleted' + ) + end + end + end + end + end + end end diff --git a/spec/support/shared_examples/email_shared_examples.rb b/spec/support/shared_examples/email_shared_examples.rb new file mode 100644 index 00000000000..634a2504766 --- /dev/null +++ b/spec/support/shared_examples/email_shared_examples.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +shared_examples_for 'correctly finds the mail key' do + specify do + expect(Gitlab::Email::Handler).to receive(:for).with(an_instance_of(Mail::Message), 'gitlabhq/gitlabhq+auth_token').and_return(handler) + + receiver.execute + end +end diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb index 8e1d24c4be2..98010150e65 100644 --- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb +++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'Maintainer manages access requests' do expect_no_visible_access_request(entity, user) - page.within('.members-list') do + page.within('[data-qa-selector="members_list"]') do expect(page).to have_content user.name end end diff --git a/spec/support/shared_examples/unique_ip_check_shared_examples.rb b/spec/support/shared_examples/unique_ip_check_shared_examples.rb index 65d86ddee9e..9bdfa762fc8 100644 --- a/spec/support/shared_examples/unique_ip_check_shared_examples.rb +++ b/spec/support/shared_examples/unique_ip_check_shared_examples.rb @@ -2,6 +2,8 @@ shared_context 'unique ips sign in limit' do include StubENV + let(:request_context) { Gitlab::RequestContext.instance } + before do Gitlab::Redis::Cache.with(&:flushall) Gitlab::Redis::Queues.with(&:flushall) @@ -15,10 +17,13 @@ shared_context 'unique ips sign in limit' do unique_ips_limit_enabled: true, unique_ips_limit_time_window: 10000 ) + + # Make sure we're working with the same reqeust context everywhere + allow(Gitlab::RequestContext).to receive(:instance).and_return(request_context) end def change_ip(ip) - allow(Gitlab::RequestContext).to receive(:client_ip).and_return(ip) + allow(request_context).to receive(:client_ip).and_return(ip) end def request_from_ip(ip) |