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

Add latest changes from gitlab-org/gitlab@master

parent d8791851
No related branches found
No related tags found
No related merge requests found
Showing
with 983 additions and 21 deletions
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import createState from '~/code_navigation/store/state';
import App from '~/code_navigation/components/app.vue';
import Popover from '~/code_navigation/components/popover.vue';
const localVue = createLocalVue();
const fetchData = jest.fn();
const showDefinition = jest.fn();
let wrapper;
localVue.use(Vuex);
function factory(initialState = {}) {
const store = new Vuex.Store({
state: {
...createState(),
...initialState,
},
actions: {
fetchData,
showDefinition,
},
});
wrapper = shallowMount(App, { store, localVue });
}
describe('Code navigation app component', () => {
afterEach(() => {
wrapper.destroy();
});
it('fetches data on mount', () => {
factory();
expect(fetchData).toHaveBeenCalled();
});
it('hides popover when no definition set', () => {
factory();
expect(wrapper.find(Popover).exists()).toBe(false);
});
it('renders popover when definition set', () => {
factory({
currentDefinition: { hover: 'console' },
currentDefinitionPosition: { x: 0 },
});
expect(wrapper.find(Popover).exists()).toBe(true);
});
it('calls showDefinition when clicking blob viewer', () => {
setFixtures('<div class="blob-viewer"></div>');
factory();
document.querySelector('.blob-viewer').click();
expect(showDefinition).toHaveBeenCalled();
});
});
import { shallowMount } from '@vue/test-utils';
import Popover from '~/code_navigation/components/popover.vue';
const MOCK_CODE_DATA = Object.freeze({
hover: [
{
language: 'javascript',
value: 'console.log',
},
],
definition_url: 'http://test.com',
});
const MOCK_DOCS_DATA = Object.freeze({
hover: [
{
language: null,
value: 'console.log',
},
],
definition_url: 'http://test.com',
});
let wrapper;
function factory(position, data) {
wrapper = shallowMount(Popover, { propsData: { position, data } });
}
describe('Code navigation popover component', () => {
afterEach(() => {
wrapper.destroy();
});
it('renders popover', () => {
factory({ x: 0, y: 0, height: 0 }, MOCK_CODE_DATA);
expect(wrapper.element).toMatchSnapshot();
});
describe('code output', () => {
it('renders code output', () => {
factory({ x: 0, y: 0, height: 0 }, MOCK_CODE_DATA);
expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(true);
expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(false);
});
});
describe('documentation output', () => {
it('renders code output', () => {
factory({ x: 0, y: 0, height: 0 }, MOCK_DOCS_DATA);
expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(false);
expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(true);
});
});
});
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import actions from '~/code_navigation/store/actions';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { setCurrentHoverElement, addInteractionClass } from '~/code_navigation/utils';
jest.mock('~/flash');
jest.mock('~/code_navigation/utils');
describe('Code navigation actions', () => {
describe('setInitialData', () => {
it('commits SET_INITIAL_DATA', done => {
testAction(
actions.setInitialData,
{ projectPath: 'test' },
{},
[{ type: 'SET_INITIAL_DATA', payload: { projectPath: 'test' } }],
[],
done,
);
});
});
describe('requestDataError', () => {
it('commits REQUEST_DATA_ERROR', () =>
testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []));
it('creates a flash message', () =>
testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []).then(
() => {
expect(createFlash).toHaveBeenCalled();
},
));
});
describe('fetchData', () => {
let mock;
const state = {
projectPath: 'gitlab-org/gitlab',
commitId: '123',
blobPath: 'index',
};
const apiUrl = '/api/1/projects/gitlab-org%2Fgitlab/commits/123/lsif/info';
beforeEach(() => {
window.gon = { api_version: '1' };
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('success', () => {
beforeEach(() => {
mock.onGet(apiUrl).replyOnce(200, [
{
start_line: 0,
start_char: 0,
hover: { value: '123' },
},
{
start_line: 1,
start_char: 0,
hover: null,
},
]);
});
it('commits REQUEST_DATA_SUCCESS with normalized data', done => {
testAction(
actions.fetchData,
null,
state,
[
{ type: 'REQUEST_DATA' },
{
type: 'REQUEST_DATA_SUCCESS',
payload: { '0:0': { start_line: 0, start_char: 0, hover: { value: '123' } } },
},
],
[],
done,
);
});
it('calls addInteractionClass with data', done => {
testAction(
actions.fetchData,
null,
state,
[
{ type: 'REQUEST_DATA' },
{
type: 'REQUEST_DATA_SUCCESS',
payload: { '0:0': { start_line: 0, start_char: 0, hover: { value: '123' } } },
},
],
[],
)
.then(() => {
expect(addInteractionClass).toHaveBeenCalledWith({
start_line: 0,
start_char: 0,
hover: { value: '123' },
});
})
.then(done)
.catch(done.fail);
});
});
describe('error', () => {
beforeEach(() => {
mock.onGet(apiUrl).replyOnce(500);
});
it('dispatches requestDataError', done => {
testAction(
actions.fetchData,
null,
state,
[{ type: 'REQUEST_DATA' }],
[{ type: 'requestDataError' }],
done,
);
});
});
});
describe('showDefinition', () => {
let target;
beforeEach(() => {
target = document.createElement('div');
});
it('returns early when no data exists', done => {
testAction(actions.showDefinition, { target }, {}, [], [], done);
});
it('commits SET_CURRENT_DEFINITION when target is not code navitation element', done => {
testAction(
actions.showDefinition,
{ target },
{ data: {} },
[
{
type: 'SET_CURRENT_DEFINITION',
payload: { definition: undefined, position: undefined },
},
],
[],
done,
);
});
it('commits SET_CURRENT_DEFINITION with LSIF data', done => {
target.classList.add('js-code-navigation');
target.setAttribute('data-line-index', '0');
target.setAttribute('data-char-index', '0');
testAction(
actions.showDefinition,
{ target },
{ data: { '0:0': { hover: 'test' } } },
[
{
type: 'SET_CURRENT_DEFINITION',
payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
},
],
[],
done,
);
});
it('adds hll class to target element', () => {
target.classList.add('js-code-navigation');
target.setAttribute('data-line-index', '0');
target.setAttribute('data-char-index', '0');
return testAction(
actions.showDefinition,
{ target },
{ data: { '0:0': { hover: 'test' } } },
[
{
type: 'SET_CURRENT_DEFINITION',
payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
},
],
[],
).then(() => {
expect(target.classList).toContain('hll');
});
});
it('caches current target element', () => {
target.classList.add('js-code-navigation');
target.setAttribute('data-line-index', '0');
target.setAttribute('data-char-index', '0');
return testAction(
actions.showDefinition,
{ target },
{ data: { '0:0': { hover: 'test' } } },
[
{
type: 'SET_CURRENT_DEFINITION',
payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
},
],
[],
).then(() => {
expect(setCurrentHoverElement).toHaveBeenCalledWith(target);
});
});
});
});
import mutations from '~/code_navigation/store/mutations';
import createState from '~/code_navigation/store/state';
let state;
describe('Code navigation mutations', () => {
beforeEach(() => {
state = createState();
});
describe('SET_INITIAL_DATA', () => {
it('sets initial data', () => {
mutations.SET_INITIAL_DATA(state, {
projectPath: 'test',
commitId: '123',
blobPath: 'index.js',
});
expect(state.projectPath).toBe('test');
expect(state.commitId).toBe('123');
expect(state.blobPath).toBe('index.js');
});
});
describe('REQUEST_DATA', () => {
it('sets loading true', () => {
mutations.REQUEST_DATA(state);
expect(state.loading).toBe(true);
});
});
describe('REQUEST_DATA_SUCCESS', () => {
it('sets loading false', () => {
mutations.REQUEST_DATA_SUCCESS(state, ['test']);
expect(state.loading).toBe(false);
});
it('sets data', () => {
mutations.REQUEST_DATA_SUCCESS(state, ['test']);
expect(state.data).toEqual(['test']);
});
});
describe('REQUEST_DATA_ERROR', () => {
it('sets loading false', () => {
mutations.REQUEST_DATA_ERROR(state);
expect(state.loading).toBe(false);
});
});
describe('SET_CURRENT_DEFINITION', () => {
it('sets current definition and position', () => {
mutations.SET_CURRENT_DEFINITION(state, { definition: 'test', position: { x: 0 } });
expect(state.currentDefinition).toBe('test');
expect(state.currentDefinitionPosition).toEqual({ x: 0 });
});
});
});
import {
cachedData,
getCurrentHoverElement,
setCurrentHoverElement,
addInteractionClass,
} from '~/code_navigation/utils';
afterEach(() => {
if (cachedData.has('current')) {
cachedData.delete('current');
}
});
describe('getCurrentHoverElement', () => {
it.each`
value
${'test'}
${undefined}
`('it returns cached current key', ({ value }) => {
if (value) {
cachedData.set('current', value);
}
expect(getCurrentHoverElement()).toEqual(value);
});
});
describe('setCurrentHoverElement', () => {
it('sets cached current key', () => {
setCurrentHoverElement('test');
expect(getCurrentHoverElement()).toEqual('test');
});
});
describe('addInteractionClass', () => {
beforeEach(() => {
setFixtures(
'<div id="LC1"><span>console</span><span>.</span><span>log</span></div><div id="LC2"><span>function</span></div>',
);
});
it.each`
line | char | index
${0} | ${0} | ${0}
${0} | ${8} | ${2}
${1} | ${0} | ${0}
`(
'it sets code navigation attributes for line $line and character $char',
({ line, char, index }) => {
addInteractionClass({ start_line: line, start_char: char });
expect(document.querySelectorAll(`#LC${line + 1} span`)[index].classList).toContain(
'js-code-navigation',
);
},
);
});
# frozen_string_literal: true
require 'fast_spec_helper'
require 'mail'
require_relative '../../config/initializers/mail_encoding_patch.rb'
describe 'Mail quoted-printable transfer encoding patch and Unicode characters' do
shared_examples 'email encoding' do |email|
it 'enclosing in a new object does not change the encoded original' do
new_email = Mail.new(email)
expect(new_email.subject).to eq(email.subject)
expect(new_email.from).to eq(email.from)
expect(new_email.to).to eq(email.to)
expect(new_email.content_type).to eq(email.content_type)
expect(new_email.content_transfer_encoding).to eq(email.content_transfer_encoding)
expect(new_email.encoded).to eq(email.encoded)
end
end
context 'with a text email' do
context 'with a body that encodes to exactly 74 characters (final newline)' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/plain; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "-123456789-123456789-123456789-123456789-123456789-123456789-123456789-1\n"
end
it_behaves_like 'email encoding', email
end
context 'with a body that encodes to exactly 74 characters (no final newline)' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/plain; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "-123456789-123456789-123456789-123456789-123456789-123456789-123456789-12"
end
it_behaves_like 'email encoding', email
end
context 'with a body that encodes to exactly 75 characters' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/plain; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "-123456789-123456789-123456789-123456789-123456789-123456789-123456789-12\n"
end
it_behaves_like 'email encoding', email
end
end
context 'with an html email' do
context 'with a body that encodes to exactly 74 characters (final newline)' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/html; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "<p>-123456789-123456789-123456789-123456789-123456789-123456789-1234</p>\n"
end
it_behaves_like 'email encoding', email
end
context 'with a body that encodes to exactly 74 characters (no final newline)' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/html; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "<p>-123456789-123456789-123456789-123456789-123456789-123456789-12345</p>"
end
it_behaves_like 'email encoding', email
end
context 'with a body that encodes to exactly 75 characters' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/html; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "<p>-123456789-123456789-123456789-123456789-123456789-123456789-12345</p>\n"
end
it_behaves_like 'email encoding', email
end
end
context 'a multipart email' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
end
text_part = Mail::Part.new do
content_type 'text/plain; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "\r\n\r\n@john.doe, now known as John Dóe has accepted your invitation to join the Administrator / htmltest project.\r\n\r\nhttp://169.254.169.254:3000/root/htmltest\r\n\r\n-- \r\nYou're receiving this email because of your account on 169.254.169.254.\r\n\r\n\r\n\r\n"
end
html_part = Mail::Part.new do
content_type 'text/html; charset=UTF-8'
content_transfer_encoding 'quoted-printable'
body "\r\n\r\n@john.doe, now known as John Dóe has accepted your invitation to join the Administrator / htmltest project.\r\n\r\nhttp://169.254.169.254:3000/root/htmltest\r\n\r\n-- \r\nYou're receiving this email because of your account on 169.254.169.254.\r\n\r\n\r\n\r\n"
end
email.text_part = text_part
email.html_part = html_part
it_behaves_like 'email encoding', email
end
context 'with non UTF-8 charset' do
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
content_type 'text/plain; charset=windows-1251'
content_transfer_encoding 'quoted-printable'
body "This line is very long and will be put in multiple quoted-printable lines. Some Russian character: Д\n\n\n".encode('windows-1251')
end
it_behaves_like 'email encoding', email
it 'can be decoded back' do
expect(Mail.new(email).body.decoded.dup.force_encoding('windows-1251').encode('utf-8')).to include('Some Russian character: Д')
end
end
context 'with binary content' do
context 'can be encoded with \'base64\' content-transfer-encoding' do
image = File.binread('spec/fixtures/rails_sample.jpg')
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
end
part = Mail::Part.new
part.body = [image].pack('m')
part.content_type = 'image/jpg'
part.content_transfer_encoding = 'base64'
email.parts << part
it_behaves_like 'email encoding', email
it 'binary contents are not modified' do
expect(email.parts.first.decoded).to eq(image)
# Enclosing in a new Mail object does not corrupt encoded data
expect(Mail.new(email).parts.first.decoded).to eq(image)
end
end
context 'encoding fails with \'quoted-printable\' content-transfer-encoding' do
image = File.binread('spec/fixtures/rails_sample.jpg')
email = Mail.new do
to 'jane.doe@example.com'
from 'John Dóe <john.doe@example.com>'
subject 'Encoding tést'
end
part = Mail::Part.new
part.body = [image].pack('M*')
part.content_type = 'image/jpg'
part.content_transfer_encoding = 'quoted-printable'
email.parts << part
# The Mail patch in `config/initializers/mail_encoding_patch.rb` fixes
# encoding of non-binary content. The failure below is expected since we
# reverted some upstream changes in order to properly support SMIME signatures
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
it 'content cannot be decoded back' do
# Headers are ok
expect(email.subject).to eq(email.subject)
expect(email.from).to eq(email.from)
expect(email.to).to eq(email.to)
expect(email.content_type).to eq(email.content_type)
expect(email.content_transfer_encoding).to eq(email.content_transfer_encoding)
# Content cannot be recovered
expect(email.parts.first.decoded).not_to eq(image)
end
end
end
end
Loading
Loading
@@ -42,8 +42,8 @@ describe('Grouped Test Reports Modal', () => {
);
});
 
it('renders miliseconds', () => {
expect(vm.$el.textContent).toContain(`${modalDataStructure.execution_time.value} ms`);
it('renders seconds', () => {
expect(vm.$el.textContent).toContain(`${modalDataStructure.execution_time.value} s`);
});
 
it('render title', () => {
Loading
Loading
Loading
Loading
@@ -38,6 +38,13 @@ describe('User Popovers', () => {
expect(document.querySelectorAll(selector).length).toBe(popovers.length);
});
 
it('does not initialize the user popovers twice for the same element', () => {
const newPopovers = initUserPopovers(document.querySelectorAll(selector));
const samePopovers = popovers.every((popover, index) => newPopovers[index] === popover);
expect(samePopovers).toBe(true);
});
describe('when user link emits mouseenter event', () => {
let userLink;
 
Loading
Loading
Loading
Loading
@@ -460,6 +460,20 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
end
end
 
context 'when the deploy token is of group type' do
let(:project_with_group) { create(:project, group: create(:group)) }
let(:deploy_token) { create(:deploy_token, :group, read_repository: true, groups: [project_with_group.group]) }
let(:login) { deploy_token.username }
subject { gl_auth.find_for_git_client(login, deploy_token.token, project: project_with_group, ip: 'ip') }
it 'succeeds when login and a group deploy token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project_with_group, :deploy_token, [:download_code, :read_container_image])
expect(subject).to eq(auth_success)
end
end
context 'when the deploy token has read_registry as a scope' do
let(:deploy_token) { create(:deploy_token, read_repository: false, projects: [project]) }
let(:login) { deploy_token.username }
Loading
Loading
@@ -469,10 +483,10 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
stub_container_registry_config(enabled: true)
end
 
it 'succeeds when login and token are valid' do
it 'succeeds when login and a project token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:read_container_image])
 
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
 
Loading
Loading
Loading
Loading
@@ -20,8 +20,14 @@ describe Gitlab::Email::Hook::SmimeSignatureInterceptor do
Gitlab::Email::Smime::Certificate.new(@cert[:key], @cert[:cert])
end
 
let(:mail_body) { "signed hello with Unicode €áø and\r\n newlines\r\n" }
let(:mail) do
ActionMailer::Base.mail(to: 'test@example.com', from: 'info@example.com', body: 'signed hello')
ActionMailer::Base.mail(to: 'test@example.com',
from: 'info@example.com',
content_transfer_encoding: 'quoted-printable',
content_type: 'text/plain; charset=UTF-8',
body: mail_body)
end
 
before do
Loading
Loading
@@ -46,9 +52,16 @@ describe Gitlab::Email::Hook::SmimeSignatureInterceptor do
ca_cert: root_certificate.cert,
signed_data: mail.encoded)
 
# re-verify signature from a new Mail object content
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
Gitlab::Email::Smime::Signer.verify_signature(
cert: certificate.cert,
ca_cert: root_certificate.cert,
signed_data: Mail.new(mail).encoded)
# envelope in a Mail object and obtain the body
decoded_mail = Mail.new(p7enc.data)
 
expect(decoded_mail.body.encoded).to eq('signed hello')
expect(decoded_mail.body.decoded.dup.force_encoding(decoded_mail.charset)).to eq(mail_body)
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
describe Gitlab::TabWidth, lib: true do
describe '.css_class_for_user' do
it 'returns default CSS class when user is nil' do
css_class = described_class.css_class_for_user(nil)
expect(css_class).to eq('tab-width-8')
end
it "returns CSS class for user's tab width", :aggregate_failures do
[1, 6, 12].each do |i|
user = double('user', tab_width: i)
css_class = described_class.css_class_for_user(user)
expect(css_class).to eq("tab-width-#{i}")
end
end
it 'raises if tab width is out of valid range', :aggregate_failures do
[0, 13, 'foo', nil].each do |i|
expect do
user = double('user', tab_width: i)
described_class.css_class_for_user(user)
end.to raise_error(ArgumentError)
end
end
end
end
Loading
Loading
@@ -7,6 +7,8 @@ describe DeployToken do
 
it { is_expected.to have_many :project_deploy_tokens }
it { is_expected.to have_many(:projects).through(:project_deploy_tokens) }
it { is_expected.to have_many :group_deploy_tokens }
it { is_expected.to have_many(:groups).through(:group_deploy_tokens) }
 
it_behaves_like 'having unique enum values'
 
Loading
Loading
@@ -17,6 +19,29 @@ describe DeployToken do
it { is_expected.to allow_value('GitLab+deploy_token-3.14').for(:username) }
it { is_expected.not_to allow_value('<script>').for(:username).with_message(username_format_message) }
it { is_expected.not_to allow_value('').for(:username).with_message(username_format_message) }
it { is_expected.to validate_presence_of(:deploy_token_type) }
end
describe 'deploy_token_type validations' do
context 'when a deploy token is associated to a group' do
it 'does not allow setting a project to it' do
group_token = create(:deploy_token, :group)
group_token.projects << build(:project)
expect(group_token).not_to be_valid
expect(group_token.errors.full_messages).to include('Deploy token cannot have projects assigned')
end
end
context 'when a deploy token is associated to a project' do
it 'does not allow setting a group to it' do
project_token = create(:deploy_token)
project_token.groups << build(:group)
expect(project_token).not_to be_valid
expect(project_token.errors.full_messages).to include('Deploy token cannot have groups assigned')
end
end
end
 
describe '#ensure_token' do
Loading
Loading
@@ -125,33 +150,148 @@ describe DeployToken do
end
end
 
describe '#holder' do
subject { deploy_token.holder }
context 'when the token is of project type' do
it 'returns the relevant holder token' do
expect(subject).to eq(deploy_token.project_deploy_tokens.first)
end
end
context 'when the token is of group type' do
let(:group) { create(:group) }
let(:deploy_token) { create(:deploy_token, :group) }
it 'returns the relevant holder token' do
expect(subject).to eq(deploy_token.group_deploy_tokens.first)
end
end
end
describe '#has_access_to?' do
let(:project) { create(:project) }
 
subject { deploy_token.has_access_to?(project) }
 
context 'when deploy token is active and related to project' do
let(:deploy_token) { create(:deploy_token, projects: [project]) }
context 'when a project is not passed in' do
let(:project) { nil }
 
it { is_expected.to be_truthy }
it { is_expected.to be_falsy }
end
 
context 'when deploy token is active but not related to project' do
let(:deploy_token) { create(:deploy_token) }
context 'when a project is passed in' do
context 'when deploy token is active and related to project' do
let(:deploy_token) { create(:deploy_token, projects: [project]) }
 
it { is_expected.to be_falsy }
end
it { is_expected.to be_truthy }
end
 
context 'when deploy token is revoked and related to project' do
let(:deploy_token) { create(:deploy_token, :revoked, projects: [project]) }
context 'when deploy token is active but not related to project' do
let(:deploy_token) { create(:deploy_token) }
 
it { is_expected.to be_falsy }
end
it { is_expected.to be_falsy }
end
 
context 'when deploy token is revoked and not related to the project' do
let(:deploy_token) { create(:deploy_token, :revoked) }
context 'when deploy token is revoked and related to project' do
let(:deploy_token) { create(:deploy_token, :revoked, projects: [project]) }
 
it { is_expected.to be_falsy }
it { is_expected.to be_falsy }
end
context 'when deploy token is revoked and not related to the project' do
let(:deploy_token) { create(:deploy_token, :revoked) }
it { is_expected.to be_falsy }
end
context 'and when the token is of group type' do
let_it_be(:group) { create(:group) }
let(:deploy_token) { create(:deploy_token, :group) }
before do
deploy_token.groups << group
end
context 'and the allow_group_deploy_token feature flag is turned off' do
it 'is false' do
stub_feature_flags(allow_group_deploy_token: false)
is_expected.to be_falsy
end
end
context 'and the allow_group_deploy_token feature flag is turned on' do
before do
stub_feature_flags(allow_group_deploy_token: true)
end
context 'and the passed-in project does not belong to any group' do
it { is_expected.to be_falsy }
end
context 'and the passed-in project belongs to the token group' do
it 'is true' do
group.projects << project
is_expected.to be_truthy
end
end
context 'and the passed-in project belongs to a subgroup' do
let(:child_group) { create(:group, parent_id: group.id) }
let(:grandchild_group) { create(:group, parent_id: child_group.id) }
before do
grandchild_group.projects << project
end
context 'and the token group is an ancestor (grand-parent) of this group' do
it { is_expected.to be_truthy }
end
context 'and the token group is not ancestor of this group' do
let(:child2_group) { create(:group, parent_id: group.id) }
it 'is false' do
deploy_token.groups = [child2_group]
is_expected.to be_falsey
end
end
end
context 'and the passed-in project does not belong to the token group' do
it { is_expected.to be_falsy }
end
context 'and the project belongs to a group that is parent of the token group' do
let(:super_group) { create(:group) }
let(:deploy_token) { create(:deploy_token, :group) }
let(:group) { create(:group, parent_id: super_group.id) }
it 'is false' do
super_group.projects << project
is_expected.to be_falsey
end
end
end
end
context 'and the token is of project type' do
let(:deploy_token) { create(:deploy_token, projects: [project]) }
context 'and the passed-in project is the same as the token project' do
it { is_expected.to be_truthy }
end
context 'and the passed-in project is not the same as the token project' do
subject { deploy_token.has_access_to?(create(:project)) }
it { is_expected.to be_falsey }
end
end
end
end
 
Loading
Loading
@@ -183,7 +323,7 @@ describe DeployToken do
end
end
 
context 'when passign a value' do
context 'when passing a value' do
let(:expires_at) { Date.today + 5.months }
let(:deploy_token) { create(:deploy_token, expires_at: expires_at) }
 
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupDeployToken, type: :model do
let(:group) { create(:group) }
let(:deploy_token) { create(:deploy_token) }
subject(:group_deploy_token) { create(:group_deploy_token, group: group, deploy_token: deploy_token) }
it { is_expected.to belong_to :group }
it { is_expected.to belong_to :deploy_token }
it { is_expected.to validate_presence_of :deploy_token }
it { is_expected.to validate_presence_of :group }
it { is_expected.to validate_uniqueness_of(:deploy_token_id).scoped_to(:group_id) }
end
Loading
Loading
@@ -85,4 +85,19 @@ describe UserPreference do
expect(user_preference.timezone).to eq(Time.zone.tzinfo.name)
end
end
describe '#tab_width' do
it 'is set to 8 by default' do
# Intentionally not using factory here to test the constructor.
pref = UserPreference.new
expect(pref.tab_width).to eq(8)
end
it do
is_expected.to validate_numericality_of(:tab_width)
.only_integer
.is_greater_than_or_equal_to(1)
.is_less_than_or_equal_to(12)
end
end
end
Loading
Loading
@@ -20,6 +20,9 @@ describe User, :do_not_mock_admin_mode do
 
describe 'delegations' do
it { is_expected.to delegate_method(:path).to(:namespace).with_prefix }
it { is_expected.to delegate_method(:tab_width).to(:user_preference) }
it { is_expected.to delegate_method(:tab_width=).to(:user_preference).with_arguments(5) }
end
 
describe 'associations' do
Loading
Loading
@@ -4126,4 +4129,41 @@ describe User, :do_not_mock_admin_mode do
end
end
end
describe 'internal methods' do
let_it_be(:user) { create(:user) }
let!(:ghost) { described_class.ghost }
let!(:alert_bot) { described_class.alert_bot }
let!(:non_internal) { [user] }
let!(:internal) { [ghost, alert_bot] }
it 'returns non internal users' do
expect(described_class.internal).to eq(internal)
expect(internal.all?(&:internal?)).to eq(true)
end
it 'returns internal users' do
expect(described_class.non_internal).to eq(non_internal)
expect(non_internal.all?(&:internal?)).to eq(false)
end
describe '#bot?' do
it 'marks bot users' do
expect(user.bot?).to eq(false)
expect(ghost.bot?).to eq(false)
expect(alert_bot.bot?).to eq(true)
end
end
end
describe 'bots & humans' do
it 'returns corresponding users' do
human = create(:user)
bot = create(:user, :bot)
expect(described_class.humans).to match_array([human])
expect(described_class.bots).to match_array([bot])
end
end
end
Loading
Loading
@@ -559,4 +559,18 @@ describe ProjectPolicy do
end
end
end
context 'alert bot' do
let(:current_user) { User.alert_bot }
subject { described_class.new(current_user, project) }
it { is_expected.to be_allowed(:reporter_access) }
context 'within a private project' do
let(:project) { create(:project, :private) }
it { is_expected.to be_allowed(:admin_issue) }
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