summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb44
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb1
-rw-r--r--spec/finders/members_finder_spec.rb44
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_noteable.json28
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_poll_widget.json8
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json3
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js55
-rw-r--r--spec/frontend/vue_shared/components/file_icon_spec.js75
-rw-r--r--spec/helpers/projects_helper_spec.rb2
-rw-r--r--spec/javascripts/environments/environment_item_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js47
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js92
-rw-r--r--spec/lib/gitlab/authorized_keys_spec.rb132
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb21
-rw-r--r--spec/lib/gitlab/internal_post_receive/response_spec.rb121
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb29
-rw-r--r--spec/lib/gitlab/shell_spec.rb485
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb13
-rw-r--r--spec/lib/peek/views/detailed_view_spec.rb81
-rw-r--r--spec/lib/peek/views/redis_detailed_spec.rb8
-rw-r--r--spec/lib/system_check/app/authorized_keys_permission_check_spec.rb67
-rw-r--r--spec/mailers/notify_spec.rb16
-rw-r--r--spec/models/list_spec.rb79
-rw-r--r--spec/models/list_user_preference_spec.rb22
-rw-r--r--spec/models/remote_mirror_spec.rb7
-rw-r--r--spec/requests/api/internal_spec.rb64
-rw-r--r--spec/requests/api/project_snapshots_spec.rb7
-rw-r--r--spec/requests/api/project_snippets_spec.rb67
-rw-r--r--spec/requests/api/snippets_spec.rb63
-rw-r--r--spec/serializers/merge_request_serializer_spec.rb8
-rw-r--r--spec/services/boards/lists/list_service_spec.rb6
-rw-r--r--spec/services/boards/lists/update_service_spec.rb89
-rw-r--r--spec/services/create_snippet_service_spec.rb13
-rw-r--r--spec/services/system_note_service_spec.rb7
-rw-r--r--spec/services/update_snippet_service_spec.rb15
-rw-r--r--spec/views/groups/edit.html.haml_spec.rb2
38 files changed, 1189 insertions, 640 deletions
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 418ca6f3210..1e8a8145b35 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -30,6 +30,21 @@ describe Boards::ListsController do
expect(json_response.length).to eq 3
end
+ it 'avoids n+1 queries when serializing lists' do
+ list_1 = create(:list, board: board)
+ list_1.update_preferences_for(user, { collapsed: true })
+
+ control_count = ActiveRecord::QueryRecorder.new { read_board_list user: user, board: board }.count
+
+ list_2 = create(:list, board: board)
+ list_2.update_preferences_for(user, { collapsed: true })
+
+ list_3 = create(:list, board: board)
+ list_3.update_preferences_for(user, { collapsed: true })
+
+ expect { read_board_list user: user, board: board }.not_to exceed_query_limit(control_count)
+ end
+
context 'with unauthorized user' do
let(:unauth_user) { create(:user) }
@@ -154,6 +169,22 @@ describe Boards::ListsController do
end
end
+ context 'with collapsed preference' do
+ it 'saves collapsed preference for user' do
+ save_setting user: user, board: board, list: planning, setting: { collapsed: true }
+
+ expect(planning.preferences_for(user).collapsed).to eq(true)
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'saves not collapsed preference for user' do
+ save_setting user: user, board: board, list: planning, setting: { collapsed: false }
+
+ expect(planning.preferences_for(user).collapsed).to eq(false)
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
def move(user:, board:, list:, position:)
sign_in(user)
@@ -166,6 +197,19 @@ describe Boards::ListsController do
patch :update, params: params, as: :json
end
+
+ def save_setting(user:, board:, list:, setting: {})
+ sign_in(user)
+
+ params = { namespace_id: project.namespace.to_param,
+ project_id: project,
+ board_id: board.to_param,
+ id: list.to_param,
+ list: setting,
+ format: :json }
+
+ patch :update, params: params, as: :json
+ end
end
describe 'DELETE destroy' do
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index a090461261b..0b3f905b5de 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -14,7 +14,6 @@ describe "User browses files" do
before do
stub_feature_flags(vue_file_list: false)
- stub_feature_flags(csslab: false)
sign_in(user)
end
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 4203f58fe81..6920fb4e572 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -17,11 +17,10 @@ describe MembersFinder, '#execute' do
result = described_class.new(project, user2).execute
- expect(result.to_a).to match_array([member1, member2, member3])
+ expect(result).to contain_exactly(member1, member2, member3)
end
- it 'includes nested group members if asked' do
- project = create(:project, namespace: group)
+ it 'includes nested group members if asked', :nested_groups do
nested_group.request_access(user1)
member1 = group.add_maintainer(user2)
member2 = nested_group.add_maintainer(user3)
@@ -29,7 +28,28 @@ describe MembersFinder, '#execute' do
result = described_class.new(project, user2).execute(include_descendants: true)
- expect(result.to_a).to match_array([member1, member2, member3])
+ expect(result).to contain_exactly(member1, member2, member3)
+ end
+
+ it 'returns the members.access_level when the user is invited', :nested_groups do
+ member_invite = create(:project_member, :invited, project: project, invite_email: create(:user).email)
+ member1 = group.add_maintainer(user2)
+
+ result = described_class.new(project, user2).execute(include_descendants: true)
+
+ expect(result).to contain_exactly(member1, member_invite)
+ expect(result.last.access_level).to eq(member_invite.access_level)
+ end
+
+ it 'returns the highest access_level for the user', :nested_groups do
+ member1 = project.add_guest(user1)
+ group.add_developer(user1)
+ nested_group.add_reporter(user1)
+
+ result = described_class.new(project, user1).execute(include_descendants: true)
+
+ expect(result).to contain_exactly(member1)
+ expect(result.first.access_level).to eq(Gitlab::Access::DEVELOPER)
end
context 'when include_invited_groups_members == true' do
@@ -37,8 +57,8 @@ describe MembersFinder, '#execute' do
set(:linked_group) { create(:group, :public, :access_requestable) }
set(:nested_linked_group) { create(:group, parent: linked_group) }
- set(:linked_group_member) { linked_group.add_developer(user1) }
- set(:nested_linked_group_member) { nested_linked_group.add_developer(user2) }
+ set(:linked_group_member) { linked_group.add_guest(user1) }
+ set(:nested_linked_group_member) { nested_linked_group.add_guest(user2) }
it 'includes all the invited_groups members including members inherited from ancestor groups' do
create(:project_group_link, project: project, group: nested_linked_group)
@@ -60,5 +80,17 @@ describe MembersFinder, '#execute' do
expect(subject).to contain_exactly(linked_group_member)
end
+
+ context 'when the user is a member of invited group and ancestor groups' do
+ it 'returns the highest access_level for the user limited by project_group_link.group_access', :nested_groups do
+ create(:project_group_link, project: project, group: nested_linked_group, group_access: Gitlab::Access::REPORTER)
+ nested_linked_group.add_developer(user1)
+
+ result = subject
+
+ expect(result).to contain_exactly(linked_group_member, nested_linked_group_member)
+ expect(result.first.access_level).to eq(Gitlab::Access::REPORTER)
+ end
+ end
end
end
diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
new file mode 100644
index 00000000000..88b0fecc24c
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
@@ -0,0 +1,28 @@
+{
+ "type": "object",
+ "properties" : {
+ "merge_params": { "type": ["object", "null"] },
+ "state": { "type": "string" },
+ "source_branch": { "type": "string" },
+ "target_branch": { "type": "string" },
+ "diff_head_sha": { "type": "string" },
+ "create_note_path": { "type": ["string", "null"] },
+ "preview_note_path": { "type": ["string", "null"] },
+ "create_issue_to_resolve_discussions_path": { "type": ["string", "null"] },
+ "new_blob_path": { "type": ["string", "null"] },
+ "can_receive_suggestion": { "type": "boolean" },
+ "current_user": {
+ "type": "object",
+ "required": [
+ "can_create_note",
+ "can_update"
+ ],
+ "properties": {
+ "can_create_note": { "type": "boolean" },
+ "can_update": { "type": "boolean" }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
index 2052892dfa3..1eda0e12920 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_poll_widget.json
@@ -24,22 +24,20 @@
"ci_status": { "type": ["string", "null"] },
"cancel_auto_merge_path": { "type": ["string", "null"] },
"test_reports_path": { "type": ["string", "null"] },
- "can_receive_suggestion": { "type": "boolean" },
"create_issue_to_resolve_discussions_path": { "type": ["string", "null"] },
"current_user": {
"type": "object",
"required": [
"can_remove_source_branch",
"can_revert_on_current_merge_request",
- "can_cherry_pick_on_current_merge_request"
+ "can_cherry_pick_on_current_merge_request",
+ "can_create_issue"
],
"properties": {
"can_remove_source_branch": { "type": "boolean" },
"can_revert_on_current_merge_request": { "type": ["boolean", "null"] },
"can_cherry_pick_on_current_merge_request": { "type": ["boolean", "null"] },
- "can_create_note": { "type": "boolean" },
- "can_create_issue": { "type": "boolean" },
- "can_update": { "type": "boolean" }
+ "can_create_issue": { "type": "boolean" }
},
"additionalProperties": false
},
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 779a47222b7..e2df7952d8f 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -5,7 +5,6 @@
{ "$ref": "merge_request_poll_widget.json" },
{
"properties" : {
- "merge_params": { "type": ["object", "null"] },
"source_project_full_path": { "type": ["string", "null"]},
"target_project_full_path": { "type": ["string", "null"]},
"email_patches_path": { "type": "string" },
@@ -13,9 +12,7 @@
"merge_request_basic_path": { "type": "string" },
"merge_request_widget_path": { "type": "string" },
"merge_request_cached_widget_path": { "type": "string" },
- "create_note_path": { "type": ["string", "null"] },
"commit_change_content_path": { "type": "string" },
- "preview_note_path": { "type": ["string", "null"] },
"conflicts_docs_path": { "type": ["string", "null"] },
"merge_request_pipelines_docs_path": { "type": ["string", "null"] },
"ci_environments_status_path": { "type": "string" },
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
new file mode 100644
index 00000000000..1f4d1e17ea0
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -0,0 +1,55 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import AutoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue';
+import eventHub from '~/vue_merge_request_widget/event_hub';
+
+describe('MRWidgetAutoMergeFailed', () => {
+ let wrapper;
+ const mergeError = 'This is the merge error';
+ const findButton = () => wrapper.find('button');
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(AutoMergeFailedComponent, {
+ sync: false,
+ propsData: { ...props },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent({
+ mr: { mergeError },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders failed message', () => {
+ expect(wrapper.text()).toContain('This merge request failed to be merged automatically');
+ });
+
+ it('renders merge error provided', () => {
+ expect(wrapper.text()).toContain(mergeError);
+ });
+
+ it('render refresh button', () => {
+ expect(findButton().text()).toEqual('Refresh');
+ });
+
+ it('emits event and shows loading icon when button is clicked', () => {
+ jest.spyOn(eventHub, '$emit');
+ findButton().trigger('click');
+
+ expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested');
+
+ return wrapper.vm.$nextTick(() => {
+ expect(findButton().attributes('disabled')).toEqual('disabled');
+ expect(
+ findButton()
+ .find(GlLoadingIcon)
+ .exists(),
+ ).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/file_icon_spec.js b/spec/frontend/vue_shared/components/file_icon_spec.js
new file mode 100644
index 00000000000..328eec0a80a
--- /dev/null
+++ b/spec/frontend/vue_shared/components/file_icon_spec.js
@@ -0,0 +1,75 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+
+describe('File Icon component', () => {
+ let wrapper;
+ const findIcon = () => wrapper.find('svg');
+ const getIconName = () =>
+ findIcon()
+ .find('use')
+ .element.getAttribute('xlink:href')
+ .replace(`${gon.sprite_file_icons}#`, '');
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(FileIcon, {
+ sync: false,
+ propsData: { ...props },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('should render a span element and an icon', () => {
+ createComponent({
+ fileName: 'test.js',
+ });
+
+ expect(wrapper.element.tagName).toEqual('SPAN');
+ expect(findIcon().exists()).toBeDefined();
+ });
+
+ it.each`
+ fileName | iconName
+ ${'test.js'} | ${'javascript'}
+ ${'test.png'} | ${'image'}
+ ${'webpack.js'} | ${'webpack'}
+ `('should render a $iconName icon based on file ending', ({ fileName, iconName }) => {
+ createComponent({ fileName });
+ expect(getIconName()).toBe(iconName);
+ });
+
+ it('should render a standard folder icon', () => {
+ createComponent({
+ fileName: 'js',
+ folder: true,
+ });
+
+ expect(findIcon().exists()).toBe(false);
+ expect(wrapper.find(Icon).props('cssClasses')).toContain('folder-icon');
+ });
+
+ it('should render a loading icon', () => {
+ createComponent({
+ fileName: 'test.js',
+ loading: true,
+ });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('should add a special class and a size class', () => {
+ const size = 120;
+ createComponent({
+ fileName: 'test.js',
+ cssClasses: 'extraclasses',
+ size,
+ });
+
+ expect(findIcon().classes()).toContain(`s${size}`);
+ expect(findIcon().classes()).toContain('extraclasses');
+ });
+});
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index a70bfc2adc7..29c43d1977e 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -503,7 +503,7 @@ describe ProjectsHelper do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return('git@localhost:')
- expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream git@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
+ expect(helper.push_to_create_project_command(user)).to eq("git push --set-upstream #{Gitlab.config.gitlab.user}@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)")
end
end
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 388d7063d13..f9ee4648128 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -106,6 +106,7 @@ describe('Environment item', () => {
play_path: '/play',
},
],
+ deployed_at: '2016-11-29T18:11:58.430Z',
},
has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
@@ -139,9 +140,7 @@ describe('Environment item', () => {
it('should render last deployment date', () => {
const timeagoInstance = new timeago(); // eslint-disable-line
- const formatedDate = timeagoInstance.format(
- environment.last_deployment.deployable.created_at,
- );
+ const formatedDate = timeagoInstance.format(environment.last_deployment.deployed_at);
expect(
component.$el.querySelector('.environment-created-date-timeago').textContent,
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
deleted file mode 100644
index 55a11a72551..00000000000
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import Vue from 'vue';
-import autoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('MRWidgetAutoMergeFailed', () => {
- let vm;
- const mergeError = 'This is the merge error';
-
- beforeEach(() => {
- const Component = Vue.extend(autoMergeFailedComponent);
- vm = mountComponent(Component, {
- mr: { mergeError },
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders failed message', () => {
- expect(vm.$el.textContent).toContain('This merge request failed to be merged automatically');
- });
-
- it('renders merge error provided', () => {
- expect(vm.$el.innerText).toContain(mergeError);
- });
-
- it('render refresh button', () => {
- expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Refresh');
- });
-
- it('emits event and shows loading icon when button is clicked', done => {
- spyOn(eventHub, '$emit');
- vm.$el.querySelector('button').click();
-
- expect(eventHub.$emit.calls.argsFor(0)[0]).toEqual('MRWidgetUpdateRequested');
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- expect(vm.$el.querySelector('button .loading-container span').classList).toContain(
- 'gl-spinner',
- );
- done();
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
deleted file mode 100644
index 1f61e19fa84..00000000000
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import Vue from 'vue';
-import fileIcon from '~/vue_shared/components/file_icon.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('File Icon component', () => {
- let vm;
- let FileIcon;
-
- beforeEach(() => {
- FileIcon = Vue.extend(fileIcon);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render a span element with an svg', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'test.js',
- });
-
- expect(vm.$el.tagName).toEqual('SPAN');
- expect(vm.$el.querySelector('span > svg')).toBeDefined();
- });
-
- it('should render a javascript icon based on file ending', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'test.js',
- });
-
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
- `${gon.sprite_file_icons}#javascript`,
- );
- });
-
- it('should render a image icon based on file ending', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'test.png',
- });
-
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
- `${gon.sprite_file_icons}#image`,
- );
- });
-
- it('should render a webpack icon based on file namer', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'webpack.js',
- });
-
- expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(
- `${gon.sprite_file_icons}#webpack`,
- );
- });
-
- it('should render a standard folder icon', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'js',
- folder: true,
- });
-
- expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(
- `${gon.sprite_file_icons}#folder`,
- );
- });
-
- it('should render a loading icon', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'test.js',
- loading: true,
- });
-
- const { classList } = vm.$el.querySelector('.loading-container span');
-
- expect(classList.contains('gl-spinner')).toEqual(true);
- });
-
- it('should add a special class and a size class', () => {
- vm = mountComponent(FileIcon, {
- fileName: 'test.js',
- cssClasses: 'extraclasses',
- size: 120,
- });
-
- const { classList } = vm.$el.firstChild;
- const containsSizeClass = classList.contains('s120');
- const containsCustomClass = classList.contains('extraclasses');
-
- expect(containsSizeClass).toBe(true);
- expect(containsCustomClass).toBe(true);
- });
-});
diff --git a/spec/lib/gitlab/authorized_keys_spec.rb b/spec/lib/gitlab/authorized_keys_spec.rb
index 42bc509eeef..adf36cf1050 100644
--- a/spec/lib/gitlab/authorized_keys_spec.rb
+++ b/spec/lib/gitlab/authorized_keys_spec.rb
@@ -5,10 +5,81 @@ require 'spec_helper'
describe Gitlab::AuthorizedKeys do
let(:logger) { double('logger').as_null_object }
- subject { described_class.new(logger) }
+ subject(:authorized_keys) { described_class.new(logger) }
+
+ describe '#accessible?' do
+ subject { authorized_keys.accessible? }
+
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ context 'can open file' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'cannot open file' do
+ before do
+ allow(File).to receive(:open).and_raise(Errno::EACCES)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#create' do
+ subject { authorized_keys.create }
+
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'authorized_keys file does not exist' do
+ after do
+ delete_authorized_keys_file
+ end
+
+ it 'creates authorized_keys file' do
+ expect(subject).to be_truthy
+ expect(File.exist?(tmp_authorized_keys_path)).to be_truthy
+ end
+ end
+
+ context 'cannot create file' do
+ before do
+ allow(File).to receive(:open).and_raise(Errno::EACCES)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
describe '#add_key' do
+ let(:id) { 'key-741' }
+
+ subject { authorized_keys.add_key(id, key) }
+
context 'authorized_keys file exists' do
+ let(:key) { 'ssh-rsa AAAAB3NzaDAxx2E trailing garbage' }
+
before do
create_authorized_keys_fixture
end
@@ -21,19 +92,20 @@ describe Gitlab::AuthorizedKeys do
auth_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-741\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaDAxx2E"
expect(logger).to receive(:info).with('Adding key (key-741): ssh-rsa AAAAB3NzaDAxx2E')
- expect(subject.add_key('key-741', 'ssh-rsa AAAAB3NzaDAxx2E trailing garbage'))
- .to be_truthy
+ expect(subject).to be_truthy
expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line}\n")
end
end
context 'authorized_keys file does not exist' do
+ let(:key) { 'ssh-rsa AAAAB3NzaDAxx2E' }
+
before do
delete_authorized_keys_file
end
it 'creates the file' do
- expect(subject.add_key('key-741', 'ssh-rsa AAAAB3NzaDAxx2E')).to be_truthy
+ expect(subject).to be_truthy
expect(File.exist?(tmp_authorized_keys_path)).to be_truthy
end
end
@@ -47,6 +119,8 @@ describe Gitlab::AuthorizedKeys do
]
end
+ subject { authorized_keys.batch_add_keys(keys) }
+
context 'authorized_keys file exists' do
before do
create_authorized_keys_fixture
@@ -62,7 +136,7 @@ describe Gitlab::AuthorizedKeys do
expect(logger).to receive(:info).with('Adding key (key-12): ssh-dsa ASDFASGADG')
expect(logger).to receive(:info).with('Adding key (key-123): ssh-rsa GFDGDFSGSDFG')
- expect(subject.batch_add_keys(keys)).to be_truthy
+ expect(subject).to be_truthy
expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line1}\n#{auth_line2}\n")
end
@@ -70,7 +144,7 @@ describe Gitlab::AuthorizedKeys do
let(:keys) { [double(shell_id: 'key-123', key: "ssh-rsa A\tSDFA\nSGADG")] }
it "doesn't add keys" do
- expect(subject.batch_add_keys(keys)).to be_falsey
+ expect(subject).to be_falsey
expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n")
end
end
@@ -82,16 +156,28 @@ describe Gitlab::AuthorizedKeys do
end
it 'creates the file' do
- expect(subject.batch_add_keys(keys)).to be_truthy
+ expect(subject).to be_truthy
expect(File.exist?(tmp_authorized_keys_path)).to be_truthy
end
end
end
describe '#rm_key' do
+ let(:key) { 'key-741' }
+
+ subject { authorized_keys.rm_key(key) }
+
context 'authorized_keys file exists' do
+ let(:other_line) { "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E" }
+ let(:delete_line) { "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E" }
+
before do
create_authorized_keys_fixture
+
+ File.open(tmp_authorized_keys_path, 'a') do |auth_file|
+ auth_file.puts delete_line
+ auth_file.puts other_line
+ end
end
after do
@@ -99,16 +185,10 @@ describe Gitlab::AuthorizedKeys do
end
it "removes the right line" do
- other_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E"
- delete_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E"
erased_line = delete_line.gsub(/./, '#')
- File.open(tmp_authorized_keys_path, 'a') do |auth_file|
- auth_file.puts delete_line
- auth_file.puts other_line
- end
expect(logger).to receive(:info).with('Removing key (key-741)')
- expect(subject.rm_key('key-741')).to be_truthy
+ expect(subject).to be_truthy
expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{erased_line}\n#{other_line}\n")
end
end
@@ -118,13 +198,13 @@ describe Gitlab::AuthorizedKeys do
delete_authorized_keys_file
end
- it 'returns false' do
- expect(subject.rm_key('key-741')).to be_falsey
- end
+ it { is_expected.to be_falsey }
end
end
describe '#clear' do
+ subject { authorized_keys.clear }
+
context 'authorized_keys file exists' do
before do
create_authorized_keys_fixture
@@ -134,9 +214,7 @@ describe Gitlab::AuthorizedKeys do
delete_authorized_keys_file
end
- it "returns true" do
- expect(subject.clear).to be_truthy
- end
+ it { is_expected.to be_truthy }
end
context 'authorized_keys file does not exist' do
@@ -144,13 +222,13 @@ describe Gitlab::AuthorizedKeys do
delete_authorized_keys_file
end
- it "still returns true" do
- expect(subject.clear).to be_truthy
- end
+ it { is_expected.to be_truthy }
end
end
describe '#list_key_ids' do
+ subject { authorized_keys.list_key_ids }
+
context 'authorized_keys file exists' do
before do
create_authorized_keys_fixture(
@@ -163,9 +241,7 @@ describe Gitlab::AuthorizedKeys do
delete_authorized_keys_file
end
- it 'returns array of key IDs' do
- expect(subject.list_key_ids).to eq([1, 2, 3, 9000])
- end
+ it { is_expected.to eq([1, 2, 3, 9000]) }
end
context 'authorized_keys file does not exist' do
@@ -173,9 +249,7 @@ describe Gitlab::AuthorizedKeys do
delete_authorized_keys_file
end
- it 'returns an empty array' do
- expect(subject.list_key_ids).to be_empty
- end
+ it { is_expected.to be_empty }
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 3c6b17c10ec..ec4a6ef05b9 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -483,3 +483,4 @@ lists:
- milestone
- board
- label
+- list_user_preferences
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index d6e1fbaa979..0aef4887c75 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -396,6 +396,27 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(project.lfs_enabled).to be_falsey
end
+
+ it 'overrides project feature access levels' do
+ access_level_keys = project.project_feature.attributes.keys.select { |a| a =~ /_access_level/ }
+
+ # `pages_access_level` is not included, since it is not available in the public API
+ # and has a dependency on project's visibility level
+ # see ProjectFeature model
+ access_level_keys.delete('pages_access_level')
+
+ disabled_access_levels = Hash[access_level_keys.collect { |item| [item, 'disabled'] }]
+
+ project.create_import_data(data: { override_params: disabled_access_levels })
+
+ restored_project_json
+
+ aggregate_failures do
+ access_level_keys.each do |key|
+ expect(project.public_send(key)).to eq(ProjectFeature::DISABLED)
+ end
+ end
+ end
end
context 'with a project that has a group' do
diff --git a/spec/lib/gitlab/internal_post_receive/response_spec.rb b/spec/lib/gitlab/internal_post_receive/response_spec.rb
new file mode 100644
index 00000000000..f43762c9248
--- /dev/null
+++ b/spec/lib/gitlab/internal_post_receive/response_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::InternalPostReceive::Response do
+ subject { described_class.new }
+
+ describe '#add_merge_request_urls' do
+ context 'when there are urls_data' do
+ it 'adds a message for each merge request URL' do
+ urls_data = [
+ { new_merge_request: false, branch_name: 'foo', url: 'http://example.com/foo/bar/merge_requests/1' },
+ { new_merge_request: true, branch_name: 'bar', url: 'http://example.com/foo/bar/merge_requests/new?merge_request%5Bsource_branch%5D=bar' }
+ ]
+
+ subject.add_merge_request_urls(urls_data)
+
+ expected = [a_kind_of(described_class::Message), a_kind_of(described_class::Message)]
+ expect(subject.messages).to match(expected)
+ end
+ end
+ end
+
+ describe '#add_merge_request_url' do
+ context 'when :new_merge_request is false' do
+ it 'adds a basic message to view the existing merge request' do
+ url_data = { new_merge_request: false, branch_name: 'foo', url: 'http://example.com/foo/bar/merge_requests/1' }
+
+ subject.add_merge_request_url(url_data)
+
+ message = <<~MESSAGE.strip
+ View merge request for foo:
+ http://example.com/foo/bar/merge_requests/1
+ MESSAGE
+
+ expect(subject.messages.first.message).to eq(message)
+ expect(subject.messages.first.type).to eq(:basic)
+ end
+ end
+
+ context 'when :new_merge_request is true' do
+ it 'adds a basic message to create a new merge request' do
+ url_data = { new_merge_request: true, branch_name: 'bar', url: 'http://example.com/foo/bar/merge_requests/new?merge_request%5Bsource_branch%5D=bar' }
+
+ subject.add_merge_request_url(url_data)
+
+ message = <<~MESSAGE.strip
+ To create a merge request for bar, visit:
+ http://example.com/foo/bar/merge_requests/new?merge_request%5Bsource_branch%5D=bar
+ MESSAGE
+
+ expect(subject.messages.first.message).to eq(message)
+ expect(subject.messages.first.type).to eq(:basic)
+ end
+ end
+ end
+
+ describe '#add_basic_message' do
+ context 'when text is present' do
+ it 'adds a basic message' do
+ subject.add_basic_message('hello')
+
+ expect(subject.messages.first.message).to eq('hello')
+ expect(subject.messages.first.type).to eq(:basic)
+ end
+ end
+
+ context 'when text is blank' do
+ it 'does not add a message' do
+ subject.add_basic_message(' ')
+
+ expect(subject.messages).to be_blank
+ end
+ end
+ end
+
+ describe '#add_alert_message' do
+ context 'when text is present' do
+ it 'adds a alert message' do
+ subject.add_alert_message('hello')
+
+ expect(subject.messages.first.message).to eq('hello')
+ expect(subject.messages.first.type).to eq(:alert)
+ end
+ end
+
+ context 'when text is blank' do
+ it 'does not add a message' do
+ subject.add_alert_message(' ')
+
+ expect(subject.messages).to be_blank
+ end
+ end
+ end
+
+ describe '#reference_counter_decreased' do
+ context 'initially' do
+ it 'reference_counter_decreased is set to false' do
+ expect(subject.reference_counter_decreased).to eq(false)
+ end
+ end
+ end
+
+ describe '#reference_counter_decreased=' do
+ context 'when the argument is truthy' do
+ it 'reference_counter_decreased is truthy' do
+ subject.reference_counter_decreased = true
+
+ expect(subject.reference_counter_decreased).to be_truthy
+ end
+ end
+
+ context 'when the argument is falsey' do
+ it 'reference_counter_decreased is falsey' do
+ subject.reference_counter_decreased = false
+
+ expect(subject.reference_counter_decreased).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index f52095bf633..16595102375 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -202,7 +202,7 @@ describe Gitlab::Middleware::Go do
def expect_response_with_path(response, protocol, path)
repository_url = case protocol
when :ssh
- "ssh://git@#{Gitlab.config.gitlab.host}/#{path}.git"
+ "ssh://#{Gitlab.config.gitlab.user}@#{Gitlab.config.gitlab.host}/#{path}.git"
when :http, nil
"http://#{Gitlab.config.gitlab.host}/#{path}.git"
end
diff --git a/spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb b/spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb
new file mode 100644
index 00000000000..3b92261f0fe
--- /dev/null
+++ b/spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+describe Gitlab::PerformanceBar::WithTopLevelWarnings do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { Module.new }
+
+ before do
+ subject.singleton_class.prepend(described_class)
+ end
+
+ describe '#has_warnings?' do
+ where(:has_warnings, :results) do
+ false | { data: {} }
+ false | { data: { gitaly: { warnings: [] } } }
+ true | { data: { gitaly: { warnings: [1] } } }
+ true | { data: { gitaly: { warnings: [] }, redis: { warnings: [1] } } }
+ end
+
+ with_them do
+ it do
+ expect(subject.has_warnings?(results)).to eq(has_warnings)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 0ba16b93ee7..fe4853fd819 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -52,38 +52,14 @@ describe Gitlab::Shell do
describe '#add_key' do
context 'when authorized_keys_enabled is true' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls #gitlab_shell_fast_execute with add-key command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([
- :gitlab_shell_keys_path,
- 'add-key',
- 'key-123',
- 'ssh-rsa foobar'
- ])
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
- end
-
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys)
- .to receive(:add_key)
- .with('key-123', 'ssh-rsa foobar')
+ expect(gitlab_authorized_keys)
+ .to receive(:add_key)
+ .with('key-123', 'ssh-rsa foobar')
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
- end
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
end
end
@@ -92,24 +68,10 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- end
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
- end
-
- context 'authorized_keys_file set' do
- it 'does nothing' do
- expect(Gitlab::AuthorizedKeys).not_to receive(:new)
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
end
end
@@ -118,38 +80,14 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls #gitlab_shell_fast_execute with add-key command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([
- :gitlab_shell_keys_path,
- 'add-key',
- 'key-123',
- 'ssh-rsa foobar'
- ])
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
- end
-
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys)
- .to receive(:add_key)
- .with('key-123', 'ssh-rsa foobar')
+ expect(gitlab_authorized_keys)
+ .to receive(:add_key)
+ .with('key-123', 'ssh-rsa foobar')
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
- end
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
end
end
end
@@ -158,50 +96,14 @@ describe Gitlab::Shell do
let(:keys) { [double(shell_id: 'key-123', key: 'ssh-rsa foobar')] }
context 'when authorized_keys_enabled is true' do
- context 'authorized_keys_file not set' do
- let(:io) { double }
-
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- end
-
- context 'valid keys' do
- before do
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls gitlab-keys with batch-add-keys command' do
- expect(IO)
- .to receive(:popen)
- .with("gitlab_shell_keys_path batch-add-keys", 'w')
- .and_yield(io)
-
- expect(io).to receive(:puts).with("key-123\tssh-rsa foobar")
- expect(gitlab_shell.batch_add_keys(keys)).to be_truthy
- end
- end
-
- context 'invalid keys' do
- let(:keys) { [double(shell_id: 'key-123', key: "ssh-rsa A\tSDFA\nSGADG")] }
-
- it 'catches failure and returns false' do
- expect(gitlab_shell.batch_add_keys(keys)).to be_falsey
- end
- end
- end
+ it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys)
+ .to receive(:batch_add_keys)
+ .with(keys)
- expect(gitlab_authorized_keys)
- .to receive(:batch_add_keys)
- .with(keys)
-
- gitlab_shell.batch_add_keys(keys)
- end
+ gitlab_shell.batch_add_keys(keys)
end
end
@@ -210,24 +112,10 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- end
-
- it 'does nothing' do
- expect(IO).not_to receive(:popen)
-
- gitlab_shell.batch_add_keys(keys)
- end
- end
-
- context 'authorized_keys_file set' do
- it 'does nothing' do
- expect(Gitlab::AuthorizedKeys).not_to receive(:new)
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- gitlab_shell.batch_add_keys(keys)
- end
+ gitlab_shell.batch_add_keys(keys)
end
end
@@ -236,72 +124,25 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- context 'authorized_keys_file not set' do
- let(:io) { double }
+ it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls gitlab-keys with batch-add-keys command' do
- expect(IO)
- .to receive(:popen)
- .with("gitlab_shell_keys_path batch-add-keys", 'w')
- .and_yield(io)
+ expect(gitlab_authorized_keys)
+ .to receive(:batch_add_keys)
+ .with(keys)
- expect(io).to receive(:puts).with("key-123\tssh-rsa foobar")
-
- gitlab_shell.batch_add_keys(keys)
- end
- end
-
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
-
- expect(gitlab_authorized_keys)
- .to receive(:batch_add_keys)
- .with(keys)
-
- gitlab_shell.batch_add_keys(keys)
- end
+ gitlab_shell.batch_add_keys(keys)
end
end
end
describe '#remove_key' do
context 'when authorized_keys_enabled is true' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls #gitlab_shell_fast_execute with rm-key command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([
- :gitlab_shell_keys_path,
- 'rm-key',
- 'key-123'
- ])
-
- gitlab_shell.remove_key('key-123')
- end
- end
+ it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
- context 'authorized_keys_file not set' do
- it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
-
- gitlab_shell.remove_key('key-123')
- end
+ gitlab_shell.remove_key('key-123')
end
end
@@ -310,24 +151,10 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- end
-
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- gitlab_shell.remove_key('key-123')
- end
- end
-
- context 'authorized_keys_file set' do
- it 'does nothing' do
- expect(Gitlab::AuthorizedKeys).not_to receive(:new)
-
- gitlab_shell.remove_key('key-123')
- end
+ gitlab_shell.remove_key('key-123')
end
end
@@ -336,64 +163,22 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls #gitlab_shell_fast_execute with rm-key command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([
- :gitlab_shell_keys_path,
- 'rm-key',
- 'key-123'
- ])
-
- gitlab_shell.remove_key('key-123')
- end
- end
-
- context 'authorized_keys_file not set' do
- it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
+ it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
- gitlab_shell.remove_key('key-123')
- end
+ gitlab_shell.remove_key('key-123')
end
end
end
describe '#remove_all_keys' do
context 'when authorized_keys_enabled is true' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
+ it 'calls Gitlab::AuthorizedKeys#clear' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:clear)
- it 'calls #gitlab_shell_fast_execute with clear command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([:gitlab_shell_keys_path, 'clear'])
-
- gitlab_shell.remove_all_keys
- end
- end
-
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#clear' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys).to receive(:clear)
-
- gitlab_shell.remove_all_keys
- end
+ gitlab_shell.remove_all_keys
end
end
@@ -402,24 +187,10 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- end
-
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- gitlab_shell.remove_all_keys
- end
- end
-
- context 'authorized_keys_file set' do
- it 'does nothing' do
- expect(Gitlab::AuthorizedKeys).not_to receive(:new)
-
- gitlab_shell.remove_all_keys
- end
+ gitlab_shell.remove_all_keys
end
end
@@ -428,163 +199,73 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- allow(gitlab_shell)
- .to receive(:gitlab_shell_keys_path)
- .and_return(:gitlab_shell_keys_path)
- end
-
- it 'calls #gitlab_shell_fast_execute with clear command' do
- expect(gitlab_shell)
- .to receive(:gitlab_shell_fast_execute)
- .with([:gitlab_shell_keys_path, 'clear'])
+ it 'calls Gitlab::AuthorizedKeys#clear' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:clear)
- gitlab_shell.remove_all_keys
- end
- end
-
- context 'authorized_keys_file set' do
- it 'calls Gitlab::AuthorizedKeys#clear' do
- expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- expect(gitlab_authorized_keys).to receive(:clear)
-
- gitlab_shell.remove_all_keys
- end
+ gitlab_shell.remove_all_keys
end
end
end
describe '#remove_keys_not_found_in_db' do
context 'when keys are in the file that are not in the DB' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
- @another_key = create(:key) # this one IS in the DB
- end
-
- it 'removes the keys' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- expect(gitlab_shell).to receive(:remove_key).with('key-9876')
- expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@another_key.id}")
-
- gitlab_shell.remove_keys_not_found_in_db
- end
+ before do
+ gitlab_shell.remove_all_keys
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
+ @another_key = create(:key) # this one IS in the DB
end
- context 'authorized_keys_file set' do
- before do
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
- @another_key = create(:key) # this one IS in the DB
- end
-
- it 'removes the keys' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- expect(gitlab_shell).to receive(:remove_key).with('key-9876')
- expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@another_key.id}")
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+ expect(gitlab_shell).to receive(:remove_key).with('key-9876')
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@another_key.id}")
- gitlab_shell.remove_keys_not_found_in_db
- end
+ gitlab_shell.remove_keys_not_found_in_db
end
end
context 'when keys there are duplicate keys in the file that are not in the DB' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- end
-
- it 'removes the keys' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
-
- gitlab_shell.remove_keys_not_found_in_db
- end
+ before do
+ gitlab_shell.remove_all_keys
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
end
- context 'authorized_keys_file set' do
- before do
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- end
-
- it 'removes the keys' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- gitlab_shell.remove_keys_not_found_in_db
- end
+ gitlab_shell.remove_keys_not_found_in_db
end
end
context 'when keys there are duplicate keys in the file that ARE in the DB' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- gitlab_shell.remove_all_keys
- @key = create(:key)
- gitlab_shell.add_key(@key.shell_id, @key.key)
- end
-
- it 'does not remove the key' do
- expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@key.id}")
-
- gitlab_shell.remove_keys_not_found_in_db
- end
+ before do
+ gitlab_shell.remove_all_keys
+ @key = create(:key)
+ gitlab_shell.add_key(@key.shell_id, @key.key)
end
- context 'authorized_keys_file set' do
- before do
- gitlab_shell.remove_all_keys
- @key = create(:key)
- gitlab_shell.add_key(@key.shell_id, @key.key)
- end
-
- it 'does not remove the key' do
- expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@key.id}")
+ it 'does not remove the key' do
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@key.id}")
- gitlab_shell.remove_keys_not_found_in_db
- end
+ gitlab_shell.remove_keys_not_found_in_db
end
end
unless ENV['CI'] # Skip in CI, it takes 1 minute
context 'when the first batch can be skipped, but the next batch has keys that are not in the DB' do
- context 'authorized_keys_file not set' do
- before do
- stub_gitlab_shell_setting(authorized_keys_file: nil)
- gitlab_shell.remove_all_keys
- 100.times { |i| create(:key) } # first batch is all in the DB
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- end
-
- it 'removes the keys not in the DB' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
-
- gitlab_shell.remove_keys_not_found_in_db
- end
+ before do
+ gitlab_shell.remove_all_keys
+ 100.times { |i| create(:key) } # first batch is all in the DB
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
end
- context 'authorized_keys_file set' do
- before do
- gitlab_shell.remove_all_keys
- 100.times { |i| create(:key) } # first batch is all in the DB
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- end
-
- it 'removes the keys not in the DB' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+ it 'removes the keys not in the DB' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- gitlab_shell.remove_keys_not_found_in_db
- end
+ gitlab_shell.remove_keys_not_found_in_db
end
end
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 5c5ff46112f..98421cd12d3 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -14,6 +14,12 @@ describe Gitlab::Workhorse do
[key, command, params]
end
+ before do
+ allow(Feature::Gitaly).to receive(:server_feature_flags).and_return({
+ 'gitaly-feature-foobar' => 'true'
+ })
+ end
+
describe ".send_git_archive" do
let(:ref) { 'master' }
let(:format) { 'zip' }
@@ -41,6 +47,7 @@ describe Gitlab::Workhorse do
expected_params = metadata.merge(
'GitalyRepository' => repository.gitaly_repository.to_h,
'GitalyServer' => {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
}
@@ -69,6 +76,7 @@ describe Gitlab::Workhorse do
expect(command).to eq('git-archive')
expect(params).to eq({
'GitalyServer' => {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -117,6 +125,7 @@ describe Gitlab::Workhorse do
expect(command).to eq("git-format-patch")
expect(params).to eq({
'GitalyServer' => {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -178,6 +187,7 @@ describe Gitlab::Workhorse do
expect(command).to eq("git-diff")
expect(params).to eq({
'GitalyServer' => {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -315,6 +325,7 @@ describe Gitlab::Workhorse do
let(:gitaly_params) do
{
GitalyServer: {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address('default'),
token: Gitlab::GitalyClient.token('default')
}
@@ -463,6 +474,7 @@ describe Gitlab::Workhorse do
expect(command).to eq('git-blob')
expect(params).to eq({
'GitalyServer' => {
+ features: { 'gitaly-feature-foobar' => 'true' },
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
},
@@ -504,6 +516,7 @@ describe Gitlab::Workhorse do
expect(command).to eq('git-snapshot')
expect(params).to eq(
'GitalyServer' => {
+ 'features' => { 'gitaly-feature-foobar' => 'true' },
'address' => Gitlab::GitalyClient.address(project.repository_storage),
'token' => Gitlab::GitalyClient.token(project.repository_storage)
},
diff --git a/spec/lib/peek/views/detailed_view_spec.rb b/spec/lib/peek/views/detailed_view_spec.rb
new file mode 100644
index 00000000000..d8660a55ea9
--- /dev/null
+++ b/spec/lib/peek/views/detailed_view_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Peek::Views::DetailedView, :request_store do
+ context 'when a class defines thresholds' do
+ let(:threshold_view) do
+ Class.new(described_class) do
+ def self.thresholds
+ {
+ calls: 1,
+ duration: 10,
+ individual_call: 5
+ }
+ end
+
+ def key
+ 'threshold-view'
+ end
+ end.new
+ end
+
+ context 'when the results exceed the calls threshold' do
+ before do
+ allow(threshold_view)
+ .to receive(:detail_store).and_return([{ duration: 0.001 }, { duration: 0.001 }])
+ end
+
+ it 'adds a warning to the results key' do
+ expect(threshold_view.results).to include(warnings: [a_string_matching('threshold-view calls')])
+ end
+ end
+
+ context 'when the results exceed the duration threshold' do
+ before do
+ allow(threshold_view)
+ .to receive(:detail_store).and_return([{ duration: 0.011 }])
+ end
+
+ it 'adds a warning to the results key' do
+ expect(threshold_view.results).to include(warnings: [a_string_matching('threshold-view duration')])
+ end
+ end
+
+ context 'when a single call exceeds the duration threshold' do
+ before do
+ allow(threshold_view)
+ .to receive(:detail_store).and_return([{ duration: 0.001 }, { duration: 0.006 }])
+ end
+
+ it 'adds a warning to that call detail entry' do
+ expect(threshold_view.results)
+ .to include(details: a_collection_containing_exactly(
+ { duration: 1.0, warnings: [] },
+ { duration: 6.0, warnings: ['6.0 over 5'] }
+ ))
+ end
+ end
+ end
+
+ context 'when a view does not define thresholds' do
+ let(:no_threshold_view) { Class.new(described_class).new }
+
+ before do
+ allow(no_threshold_view)
+ .to receive(:detail_store).and_return([{ duration: 100 }, { duration: 100 }])
+ end
+
+ it 'does not add warnings to the top level' do
+ expect(no_threshold_view.results).to include(warnings: [])
+ end
+
+ it 'does not add warnings to call details entries' do
+ expect(no_threshold_view.results)
+ .to include(details: a_collection_containing_exactly(
+ { duration: 100000, warnings: [] },
+ { duration: 100000, warnings: [] }
+ ))
+ end
+ end
+end
diff --git a/spec/lib/peek/views/redis_detailed_spec.rb b/spec/lib/peek/views/redis_detailed_spec.rb
index 61096e6c69e..fa9532226f2 100644
--- a/spec/lib/peek/views/redis_detailed_spec.rb
+++ b/spec/lib/peek/views/redis_detailed_spec.rb
@@ -21,10 +21,10 @@ describe Peek::Views::RedisDetailed, :request_store do
expect(subject.results[:details].count).to eq(1)
expect(subject.results[:details].first)
- .to eq({
- cmd: expected,
- duration: 1000
- })
+ .to include({
+ cmd: expected,
+ duration: 1000
+ })
end
end
diff --git a/spec/lib/system_check/app/authorized_keys_permission_check_spec.rb b/spec/lib/system_check/app/authorized_keys_permission_check_spec.rb
new file mode 100644
index 00000000000..1a8123c3f0a
--- /dev/null
+++ b/spec/lib/system_check/app/authorized_keys_permission_check_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe SystemCheck::App::AuthorizedKeysPermissionCheck do
+ subject(:system_check) { described_class.new }
+
+ describe '#skip?' do
+ subject { system_check.skip? }
+
+ context 'authorized keys enabled' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'authorized keys not enabled' do
+ before do
+ stub_application_setting(authorized_keys_enabled: false)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ describe '#check?' do
+ subject { system_check.check? }
+
+ before do
+ expect_next_instance_of(Gitlab::AuthorizedKeys) do |instance|
+ allow(instance).to receive(:accessible?) { accessible? }
+ end
+ end
+
+ context 'authorized keys is accessible' do
+ let(:accessible?) { true }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'authorized keys is not accessible' do
+ let(:accessible?) { false }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#repair!' do
+ subject { system_check.repair! }
+
+ before do
+ expect_next_instance_of(Gitlab::AuthorizedKeys) do |instance|
+ allow(instance).to receive(:create) { created }
+ end
+ end
+
+ context 'authorized_keys file created' do
+ let(:created) { true }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'authorized_keys file is not created' do
+ let(:created) { false }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 6cba7df114c..56fa26d5f23 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -187,6 +187,22 @@ describe Notify do
end
end
+ describe 'that are due soon' do
+ subject { described_class.issue_due_email(recipient.id, issue.id) }
+
+ before do
+ issue.update(due_date: Date.tomorrow)
+ end
+
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
+ it_behaves_like 'it should show Gmail Actions View Issue link'
+ it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+ end
+
describe 'status changed' do
let(:status) { 'closed' }
subject { described_class.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index 18d4549977c..2429cd408a6 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -81,4 +81,83 @@ describe List do
expect(subject.title).to eq 'Closed'
end
end
+
+ describe '#update_preferences_for' do
+ let(:user) { create(:user) }
+ let(:list) { create(:list) }
+
+ context 'when user is present' do
+ context 'when there are no preferences for user' do
+ it 'creates new user preferences' do
+ expect { list.update_preferences_for(user, collapsed: true) }.to change { ListUserPreference.count }.by(1)
+ expect(list.preferences_for(user).collapsed).to eq(true)
+ end
+ end
+
+ context 'when there are preferences for user' do
+ it 'updates user preferences' do
+ list.update_preferences_for(user, collapsed: false)
+
+ expect { list.update_preferences_for(user, collapsed: true) }.not_to change { ListUserPreference.count }
+ expect(list.preferences_for(user).collapsed).to eq(true)
+ end
+ end
+
+ context 'when user is nil' do
+ it 'does not create user preferences' do
+ expect { list.update_preferences_for(nil, collapsed: true) }.not_to change { ListUserPreference.count }
+ end
+ end
+ end
+ end
+
+ describe '#preferences_for' do
+ let(:user) { create(:user) }
+ let(:list) { create(:list) }
+
+ context 'when user is nil' do
+ it 'returns not persisted preferences' do
+ preferences = list.preferences_for(nil)
+
+ expect(preferences.persisted?).to eq(false)
+ expect(preferences.list_id).to eq(list.id)
+ expect(preferences.user_id).to be_nil
+ end
+ end
+
+ context 'when a user preference already exists' do
+ before do
+ list.update_preferences_for(user, collapsed: true)
+ end
+
+ it 'loads preference for user' do
+ preferences = list.preferences_for(user)
+
+ expect(preferences).to be_persisted
+ expect(preferences.collapsed).to eq(true)
+ end
+
+ context 'when preferences are already loaded for user' do
+ it 'gets preloaded user preferences' do
+ fetched_list = described_class.where(id: list.id).with_preferences_for(user).first
+
+ expect(fetched_list).to receive(:preloaded_preferences_for).with(user).and_call_original
+
+ preferences = fetched_list.preferences_for(user)
+
+ expect(preferences.collapsed).to eq(true)
+ end
+ end
+ end
+
+ context 'when preferences for user does not exist' do
+ it 'returns not persisted preferences' do
+ preferences = list.preferences_for(user)
+
+ expect(preferences.persisted?).to eq(false)
+ expect(preferences.user_id).to eq(user.id)
+ expect(preferences.list_id).to eq(list.id)
+ end
+ end
+ end
end
diff --git a/spec/models/list_user_preference_spec.rb b/spec/models/list_user_preference_spec.rb
new file mode 100644
index 00000000000..1335a3700dc
--- /dev/null
+++ b/spec/models/list_user_preference_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ListUserPreference do
+ set(:user) { create(:user) }
+ set(:list) { create(:list) }
+
+ before do
+ list.update_preferences_for(user, { collapsed: true })
+ end
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:list) }
+ it { is_expected.to belong_to(:user) }
+
+ it do
+ is_expected.to validate_uniqueness_of(:user_id).scoped_to(:list_id)
+ .with_message("should have only one list preference per user")
+ end
+ end
+end
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 7edeb56efe2..f8d6e500e10 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -40,6 +40,13 @@ describe RemoteMirror, :mailer do
expect(remote_mirror).to be_invalid
expect(remote_mirror.errors[:url].first).to include('Requests to the local network are not allowed')
end
+
+ it 'returns a nil safe_url' do
+ remote_mirror = build(:remote_mirror, url: 'http://[0:0:0:0:ffff:123.123.123.123]/foo.git')
+
+ expect(remote_mirror.url).to eq('http://[0:0:0:0:ffff:123.123.123.123]/foo.git')
+ expect(remote_mirror.safe_url).to be_nil
+ end
end
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 3ab1818bebb..c94f6d22e74 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -925,19 +925,20 @@ describe API::Internal do
it 'returns link to create new merge request' do
post api('/internal/post_receive'), params: valid_params
- expect(json_response['merge_request_urls']).to match [{
- "branch_name" => branch_name,
- "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}",
- "new_merge_request" => true
- }]
+ message = <<~MESSAGE.strip
+ To create a merge request for #{branch_name}, visit:
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
+ MESSAGE
+
+ expect(json_response['messages']).to include(build_basic_message(message))
end
- it 'returns empty array if printing_merge_request_link_enabled is false' do
+ it 'returns no merge request messages if printing_merge_request_link_enabled is false' do
project.update!(printing_merge_request_link_enabled: false)
post api('/internal/post_receive'), params: valid_params
- expect(json_response['merge_request_urls']).to eq([])
+ expect(json_response['messages']).to be_blank
end
it 'does not invoke MergeRequests::PushOptionsHandlerService' do
@@ -968,11 +969,12 @@ describe API::Internal do
it 'links to the newly created merge request' do
post api('/internal/post_receive'), params: valid_params
- expect(json_response['merge_request_urls']).to match [{
- 'branch_name' => branch_name,
- 'url' => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/1",
- 'new_merge_request' => false
- }]
+ message = <<~MESSAGE.strip
+ View merge request for #{branch_name}:
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/1
+ MESSAGE
+
+ expect(json_response['messages']).to include(build_basic_message(message))
end
it 'adds errors on the service instance to warnings' do
@@ -982,7 +984,8 @@ describe API::Internal do
post api('/internal/post_receive'), params: valid_params
- expect(json_response['warnings']).to eq('Error encountered with push options \'merge_request.create\': my error')
+ message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+ expect(json_response['messages']).to include(build_alert_message(message))
end
it 'adds ActiveRecord errors on invalid MergeRequest records to warnings' do
@@ -995,38 +998,39 @@ describe API::Internal do
post api('/internal/post_receive'), params: valid_params
- expect(json_response['warnings']).to eq('Error encountered with push options \'merge_request.create\': my error')
+ message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+ expect(json_response['messages']).to include(build_alert_message(message))
end
end
context 'broadcast message exists' do
let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
- it 'returns one broadcast message' do
+ it 'outputs a broadcast message' do
post api('/internal/post_receive'), params: valid_params
expect(response).to have_gitlab_http_status(200)
- expect(json_response['broadcast_message']).to eq(broadcast_message.message)
+ expect(json_response['messages']).to include(build_alert_message(broadcast_message.message))
end
end
context 'broadcast message does not exist' do
- it 'returns empty string' do
+ it 'does not output a broadcast message' do
post api('/internal/post_receive'), params: valid_params
expect(response).to have_gitlab_http_status(200)
- expect(json_response['broadcast_message']).to eq(nil)
+ expect(has_alert_messages?(json_response['messages'])).to be_falsey
end
end
context 'nil broadcast message' do
- it 'returns empty string' do
+ it 'does not output a broadcast message' do
allow(BroadcastMessage).to receive(:current).and_return(nil)
post api('/internal/post_receive'), params: valid_params
expect(response).to have_gitlab_http_status(200)
- expect(json_response['broadcast_message']).to eq(nil)
+ expect(has_alert_messages?(json_response['messages'])).to be_falsey
end
end
@@ -1038,8 +1042,7 @@ describe API::Internal do
post api('/internal/post_receive'), params: valid_params
expect(response).to have_gitlab_http_status(200)
- expect(json_response["redirected_message"]).to be_present
- expect(json_response["redirected_message"]).to eq(project_moved.message)
+ expect(json_response['messages']).to include(build_basic_message(project_moved.message))
end
end
@@ -1051,8 +1054,7 @@ describe API::Internal do
post api('/internal/post_receive'), params: valid_params
expect(response).to have_gitlab_http_status(200)
- expect(json_response["project_created_message"]).to be_present
- expect(json_response["project_created_message"]).to eq(project_created.message)
+ expect(json_response['messages']).to include(build_basic_message(project_created.message))
end
end
@@ -1172,4 +1174,18 @@ describe API::Internal do
}
)
end
+
+ def build_alert_message(message)
+ { 'type' => 'alert', 'message' => message }
+ end
+
+ def build_basic_message(message)
+ { 'type' => 'basic', 'message' => message }
+ end
+
+ def has_alert_messages?(messages)
+ messages.any? do |message|
+ message['type'] == 'alert'
+ end
+ end
end
diff --git a/spec/requests/api/project_snapshots_spec.rb b/spec/requests/api/project_snapshots_spec.rb
index 44b5ee1f130..2857715cdbe 100644
--- a/spec/requests/api/project_snapshots_spec.rb
+++ b/spec/requests/api/project_snapshots_spec.rb
@@ -6,6 +6,12 @@ describe API::ProjectSnapshots do
let(:project) { create(:project) }
let(:admin) { create(:admin) }
+ before do
+ allow(Feature::Gitaly).to receive(:server_feature_flags).and_return({
+ 'gitaly-feature-foobar' => 'true'
+ })
+ end
+
describe 'GET /projects/:id/snapshot' do
def expect_snapshot_response_for(repository)
type, params = workhorse_send_data
@@ -13,6 +19,7 @@ describe API::ProjectSnapshots do
expect(type).to eq('git-snapshot')
expect(params).to eq(
'GitalyServer' => {
+ 'features' => { 'gitaly-feature-foobar' => 'true' },
'address' => Gitlab::GitalyClient.address(repository.project.repository_storage),
'token' => Gitlab::GitalyClient.token(repository.project.repository_storage)
},
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 29f69b6ce20..58a28e636f1 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -96,6 +96,28 @@ describe API::ProjectSnippets do
}
end
+ context 'with a regular user' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::PRIVATE])
+ params['visibility'] = 'internal'
+ end
+
+ it 'creates a new snippet' do
+ post api("/projects/#{project.id}/snippets/", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ snippet = ProjectSnippet.find(json_response['id'])
+ expect(snippet.content).to eq(params[:code])
+ expect(snippet.description).to eq(params[:description])
+ expect(snippet.title).to eq(params[:title])
+ expect(snippet.file_name).to eq(params[:file_name])
+ expect(snippet.visibility_level).to eq(Snippet::INTERNAL)
+ end
+ end
+
it 'creates a new snippet' do
post api("/projects/#{project.id}/snippets/", admin), params: params
@@ -108,6 +130,29 @@ describe API::ProjectSnippets do
expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
end
+ it 'creates a new snippet with content parameter' do
+ params[:content] = params.delete(:code)
+
+ post api("/projects/#{project.id}/snippets/", admin), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ snippet = ProjectSnippet.find(json_response['id'])
+ expect(snippet.content).to eq(params[:content])
+ expect(snippet.description).to eq(params[:description])
+ expect(snippet.title).to eq(params[:title])
+ expect(snippet.file_name).to eq(params[:file_name])
+ expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
+ end
+
+ it 'returns 400 when both code and content parameters specified' do
+ params[:content] = params[:code]
+
+ post api("/projects/#{project.id}/snippets/", admin), params: params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('code, content are mutually exclusive')
+ end
+
it 'returns 400 for missing parameters' do
params.delete(:title)
@@ -167,7 +212,20 @@ describe API::ProjectSnippets do
new_content = 'New content'
new_description = 'New description'
- put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), params: { code: new_content, description: new_description }
+ put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), params: { code: new_content, description: new_description, visibility: 'private' }
+
+ expect(response).to have_gitlab_http_status(200)
+ snippet.reload
+ expect(snippet.content).to eq(new_content)
+ expect(snippet.description).to eq(new_description)
+ expect(snippet.visibility).to eq('private')
+ end
+
+ it 'updates snippet with content parameter' do
+ new_content = 'New content'
+ new_description = 'New description'
+
+ put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), params: { content: new_content, description: new_description }
expect(response).to have_gitlab_http_status(200)
snippet.reload
@@ -175,6 +233,13 @@ describe API::ProjectSnippets do
expect(snippet.description).to eq(new_description)
end
+ it 'returns 400 when both code and content parameters specified' do
+ put api("/projects/#{snippet.project.id}/snippets/1234", admin), params: { code: 'some content', content: 'other content' }
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('code, content are mutually exclusive')
+ end
+
it 'returns 404 for invalid snippet id' do
put api("/projects/#{snippet.project.id}/snippets/1234", admin), params: { title: 'foo' }
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index d600076e9fb..cc05b8d5b45 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -193,18 +193,32 @@ describe API::Snippets do
}
end
- it 'creates a new snippet' do
- expect do
- post api("/snippets/", user), params: params
- end.to change { PersonalSnippet.count }.by(1)
+ shared_examples 'snippet creation' do
+ it 'creates a new snippet' do
+ expect do
+ post api("/snippets/", user), params: params
+ end.to change { PersonalSnippet.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq(params[:title])
+ expect(json_response['description']).to eq(params[:description])
+ expect(json_response['file_name']).to eq(params[:file_name])
+ expect(json_response['visibility']).to eq(params[:visibility])
+ end
+ end
+
+ context 'with restricted visibility settings' do
+ before do
+ stub_application_setting(restricted_visibility_levels:
+ [Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PRIVATE])
+ end
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['title']).to eq(params[:title])
- expect(json_response['description']).to eq(params[:description])
- expect(json_response['file_name']).to eq(params[:file_name])
- expect(json_response['visibility']).to eq(params[:visibility])
+ it_behaves_like 'snippet creation'
end
+ it_behaves_like 'snippet creation'
+
it 'returns 400 for missing parameters' do
params.delete(:title)
@@ -253,18 +267,33 @@ describe API::Snippets do
create(:personal_snippet, author: user, visibility_level: visibility_level)
end
- it 'updates snippet' do
- new_content = 'New content'
- new_description = 'New description'
+ shared_examples 'snippet updates' do
+ it 'updates a snippet' do
+ new_content = 'New content'
+ new_description = 'New description'
- put api("/snippets/#{snippet.id}", user), params: { content: new_content, description: new_description }
+ put api("/snippets/#{snippet.id}", user), params: { content: new_content, description: new_description, visibility: 'internal' }
- expect(response).to have_gitlab_http_status(200)
- snippet.reload
- expect(snippet.content).to eq(new_content)
- expect(snippet.description).to eq(new_description)
+ expect(response).to have_gitlab_http_status(200)
+ snippet.reload
+ expect(snippet.content).to eq(new_content)
+ expect(snippet.description).to eq(new_description)
+ expect(snippet.visibility).to eq('internal')
+ end
end
+ context 'with restricted visibility settings' do
+ before do
+ stub_application_setting(restricted_visibility_levels:
+ [Gitlab::VisibilityLevel::PUBLIC,
+ Gitlab::VisibilityLevel::PRIVATE])
+ end
+
+ it_behaves_like 'snippet updates'
+ end
+
+ it_behaves_like 'snippet updates'
+
it 'returns 404 for invalid snippet id' do
put api("/snippets/1234", user), params: { title: 'foo' }
diff --git a/spec/serializers/merge_request_serializer_spec.rb b/spec/serializers/merge_request_serializer_spec.rb
index 276e0f6ff3d..d1483c3c41e 100644
--- a/spec/serializers/merge_request_serializer_spec.rb
+++ b/spec/serializers/merge_request_serializer_spec.rb
@@ -41,6 +41,14 @@ describe MergeRequestSerializer do
end
end
+ context 'noteable merge request serialization' do
+ let(:serializer) { 'noteable' }
+
+ it 'matches noteable merge request json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_noteable', strict: true)
+ end
+ end
+
context 'no serializer' do
let(:serializer) { nil }
diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb
index 2ebfd295fa2..2535f339495 100644
--- a/spec/services/boards/lists/list_service_spec.rb
+++ b/spec/services/boards/lists/list_service_spec.rb
@@ -3,13 +3,15 @@
require 'spec_helper'
describe Boards::Lists::ListService do
+ let(:user) { create(:user) }
+
describe '#execute' do
context 'when board parent is a project' do
let(:project) { create(:project) }
let(:board) { create(:board, project: project) }
let(:label) { create(:label, project: project) }
let!(:list) { create(:list, board: board, label: label) }
- let(:service) { described_class.new(project, double) }
+ let(:service) { described_class.new(project, user) }
it_behaves_like 'lists list service'
end
@@ -19,7 +21,7 @@ describe Boards::Lists::ListService do
let(:board) { create(:board, group: group) }
let(:label) { create(:group_label, group: group) }
let!(:list) { create(:list, board: board, label: label) }
- let(:service) { described_class.new(group, double) }
+ let(:service) { described_class.new(group, user) }
it_behaves_like 'lists list service'
end
diff --git a/spec/services/boards/lists/update_service_spec.rb b/spec/services/boards/lists/update_service_spec.rb
new file mode 100644
index 00000000000..f28bbab941a
--- /dev/null
+++ b/spec/services/boards/lists/update_service_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Boards::Lists::UpdateService do
+ let(:user) { create(:user) }
+ let!(:list) { create(:list, board: board, position: 0) }
+
+ shared_examples 'moving list' do
+ context 'when user can admin list' do
+ it 'calls Lists::MoveService to update list position' do
+ board.parent.add_developer(user)
+ service = described_class.new(board.parent, user, position: 1)
+
+ expect(Boards::Lists::MoveService).to receive(:new).with(board.parent, user, { position: 1 }).and_call_original
+ expect_any_instance_of(Boards::Lists::MoveService).to receive(:execute).with(list)
+
+ service.execute(list)
+ end
+ end
+
+ context 'when user cannot admin list' do
+ it 'does not call Lists::MoveService to update list position' do
+ service = described_class.new(board.parent, user, position: 1)
+
+ expect(Boards::Lists::MoveService).not_to receive(:new)
+
+ service.execute(list)
+ end
+ end
+ end
+
+ shared_examples 'updating list preferences' do
+ context 'when user can read list' do
+ it 'updates list preference for user' do
+ board.parent.add_guest(user)
+ service = described_class.new(board.parent, user, collapsed: true)
+
+ service.execute(list)
+
+ expect(list.preferences_for(user).collapsed).to eq(true)
+ end
+ end
+
+ context 'when user cannot read list' do
+ it 'does not update list preference for user' do
+ service = described_class.new(board.parent, user, collapsed: true)
+
+ service.execute(list)
+
+ expect(list.preferences_for(user).collapsed).to be_nil
+ end
+ end
+ end
+
+ describe '#execute' do
+ context 'when position parameter is present' do
+ context 'for projects' do
+ it_behaves_like 'moving list' do
+ let(:project) { create(:project, :private) }
+ let(:board) { create(:board, project: project) }
+ end
+ end
+
+ context 'for groups' do
+ it_behaves_like 'moving list' do
+ let(:group) { create(:group, :private) }
+ let(:board) { create(:board, group: group) }
+ end
+ end
+ end
+
+ context 'when collapsed parameter is present' do
+ context 'for projects' do
+ it_behaves_like 'updating list preferences' do
+ let(:project) { create(:project, :private) }
+ let(:board) { create(:board, project: project) }
+ end
+ end
+
+ context 'for groups' do
+ it_behaves_like 'updating list preferences' do
+ let(:group) { create(:group, :private) }
+ let(:board) { create(:board, group: group) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb
index 9b83f65a17e..7d2491b3a49 100644
--- a/spec/services/create_snippet_service_spec.rb
+++ b/spec/services/create_snippet_service_spec.rb
@@ -34,6 +34,19 @@ describe CreateSnippetService do
expect(snippet.errors.any?).to be_falsey
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
+
+ describe "when visibility level is passed as a string" do
+ before do
+ @opts[:visibility] = 'internal'
+ @opts.delete(:visibility_level)
+ end
+
+ it "assigns the correct visibility level" do
+ snippet = create_snippet(nil, @user, @opts)
+ expect(snippet.errors.any?).to be_falsey
+ expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+ end
end
describe 'usage counter' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index f46f9633c1c..910fe3b50b7 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -212,6 +212,13 @@ describe SystemNoteService do
expect(build_note([assignee, assignee1, assignee2], [assignee, assignee1])).to eq \
"unassigned @#{assignee2.username}"
end
+
+ it 'builds a correct phrase when the locale is different' do
+ Gitlab::I18n.with_locale('pt-BR') do
+ expect(build_note([assignee, assignee1, assignee2], [assignee3])).to eq \
+ "assigned to @#{assignee3.username} and unassigned @#{assignee.username}, @#{assignee1.username}, and @#{assignee2.username}"
+ end
+ end
end
describe '.change_milestone' do
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
index 0678f54c195..19b35dca6a7 100644
--- a/spec/services/update_snippet_service_spec.rb
+++ b/spec/services/update_snippet_service_spec.rb
@@ -32,12 +32,25 @@ describe UpdateSnippetService do
expect(@snippet.visibility_level).to eq(old_visibility)
end
- it 'admins should be able to update to pubic visibility' do
+ it 'admins should be able to update to public visibility' do
old_visibility = @snippet.visibility_level
update_snippet(@project, @admin, @snippet, @opts)
expect(@snippet.visibility_level).not_to eq(old_visibility)
expect(@snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
+
+ describe "when visibility level is passed as a string" do
+ before do
+ @opts[:visibility] = 'internal'
+ @opts.delete(:visibility_level)
+ end
+
+ it "assigns the correct visibility level" do
+ update_snippet(@project, @user, @snippet, @opts)
+ expect(@snippet.errors.any?).to be_falsey
+ expect(@snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+ end
end
describe 'usage counter' do
diff --git a/spec/views/groups/edit.html.haml_spec.rb b/spec/views/groups/edit.html.haml_spec.rb
index 47804411b9d..0da3470433c 100644
--- a/spec/views/groups/edit.html.haml_spec.rb
+++ b/spec/views/groups/edit.html.haml_spec.rb
@@ -23,7 +23,7 @@ describe 'groups/edit.html.haml' do
render
expect(rendered).to have_content("Prevent sharing a project within #{test_group.name} with other groups")
- expect(rendered).to have_css('.descr', text: 'help text here')
+ expect(rendered).to have_css('.js-descr', text: 'help text here')
expect(rendered).to have_field('group_share_with_group_lock', checkbox_options)
end
end