Skip to content
Snippets Groups Projects
Commit a5ab3467 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent eb30dd6e
No related branches found
No related tags found
No related merge requests found
Showing
with 614 additions and 156 deletions
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
 
Loading
Loading
Loading
Loading
@@ -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)
Loading
Loading
@@ -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
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.
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
Loading
Loading
// Jest Snapshot v1, https://goo.gl/fbAQLP
 
exports[`Settings Form renders 1`] = `
<div
class="card"
>
<form>
<form>
<div
class="card"
>
<!---->
<div
class="card-header"
>
Loading
Loading
@@ -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"
Loading
Loading
@@ -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"
Loading
Loading
@@ -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:"
Loading
Loading
@@ -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>
`;
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);
Loading
Loading
@@ -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' });
Loading
Loading
@@ -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,
Loading
Loading
@@ -35,7 +40,7 @@ describe('Settings Form', () => {
 
beforeEach(() => {
store = createStore();
store.dispatch('setInitialState', { helpPagePath });
store.dispatch('setInitialState', stringifiedFormOptions);
mountComponent();
});
 
Loading
Loading
@@ -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);
Loading
Loading
@@ -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', () => {
Loading
Loading
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,
};
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;
Loading
Loading
@@ -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);
});
});
 
Loading
Loading
Loading
Loading
@@ -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);
});
});
});
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
 
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
# 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
Loading
Loading
@@ -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
Loading
Loading
@@ -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)
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
@@ -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) }
Loading
Loading
Loading
Loading
@@ -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
 
Loading
Loading
@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment