summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups/group_links_controller_spec.rb96
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb6
-rw-r--r--spec/controllers/health_check_controller_spec.rb8
-rw-r--r--spec/controllers/metrics_controller_spec.rb8
-rw-r--r--spec/features/admin/admin_groups_spec.rb10
-rw-r--r--spec/features/admin/admin_projects_spec.rb4
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb89
-rw-r--r--spec/features/groups/members/manage_members_spec.rb8
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/projects/members/list_spec.rb4
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb2
-rw-r--r--spec/finders/group_members_finder_spec.rb40
-rw-r--r--spec/fixtures/emails/envelope_to_header.eml32
-rw-r--r--spec/fixtures/emails/forwarded_new_issue.eml16
-rw-r--r--spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap134
-rw-r--r--spec/frontend/registry/settings/components/settings_form_spec.js33
-rw-r--r--spec/frontend/registry/settings/mock_data.js12
-rw-r--r--spec/frontend/registry/settings/store/mutations_spec.js10
-rw-r--r--spec/frontend/vuex_shared/bindings_spec.js80
-rw-r--r--spec/helpers/application_settings_helper_spec.rb14
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb19
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb53
-rw-r--r--spec/lib/gitlab/middleware/request_context_spec.rb78
-rw-r--r--spec/lib/gitlab/request_context_spec.rb67
-rw-r--r--spec/models/application_setting_spec.rb5
-rw-r--r--spec/models/group_group_link_spec.rb8
-rw-r--r--spec/models/group_spec.rb51
-rw-r--r--spec/requests/self_monitoring_project_spec.rb108
-rw-r--r--spec/support/shared_examples/email_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb2
-rw-r--r--spec/support/shared_examples/unique_ip_check_shared_examples.rb7
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)