Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-04 15:09:00 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-04 15:09:00 +0300
commit88a0824944720b6edaaef56376713541b9a02118 (patch)
treef5fcc4f9755f249779cda9a8f02902d734af6e7e /spec
parent7d19df2d34a9803d9f077c16315ba919b7ae2aa2 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/admin/admin_mode/workers_spec.rb77
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb66
-rw-r--r--spec/features/projects/clusters/eks_spec.rb4
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb4
-rw-r--r--spec/features/users/signup_spec.rb57
-rw-r--r--spec/frontend/jobs/store/mutations_spec.js18
-rw-r--r--spec/frontend/registry/settings/components/settings_form_spec.js96
-rw-r--r--spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap133
-rw-r--r--spec/frontend/registry/shared/components/__snapshots__/expiration_policy_form_spec.js.snap186
-rw-r--r--spec/frontend/registry/shared/components/expiration_policy_fields_spec.js (renamed from spec/frontend/registry/shared/components/expiration_policy_form_spec.js)101
-rw-r--r--spec/helpers/clusters_helper_spec.rb28
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js20
-rw-r--r--spec/javascripts/jobs/store/actions_spec.js117
-rw-r--r--spec/lib/gitlab/auth/current_user_mode_spec.rb172
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/admin_mode/client_spec.rb94
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/admin_mode/server_spec.rb72
-rw-r--r--spec/lib/gitlab/sidekiq_middleware_spec.rb6
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb20
-rw-r--r--spec/models/issue_spec.rb10
-rw-r--r--spec/models/user_spec.rb6
-rw-r--r--spec/policies/base_policy_spec.rb10
-rw-r--r--spec/services/error_tracking/issue_details_service_spec.rb2
-rw-r--r--spec/services/error_tracking/issue_latest_event_service_spec.rb2
-rw-r--r--spec/services/error_tracking/issue_update_service_spec.rb56
-rw-r--r--spec/services/projects/after_rename_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/rollback_repository_service_spec.rb2
-rw-r--r--spec/support/shared_contexts/sentry_error_tracking_shared_context.rb2
31 files changed, 910 insertions, 461 deletions
diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb
new file mode 100644
index 00000000000..e33c9d7e64c
--- /dev/null
+++ b/spec/features/admin/admin_mode/workers_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# Test an operation that triggers background jobs requiring administrative rights
+describe 'Admin mode for workers', :do_not_mock_admin_mode, :request_store, :clean_gitlab_redis_shared_state do
+ let(:user) { create(:user) }
+ let(:user_to_delete) { create(:user) }
+
+ before do
+ add_sidekiq_middleware
+
+ sign_in(user)
+ end
+
+ context 'as a regular user' do
+ it 'cannot delete user' do
+ visit admin_user_path(user_to_delete)
+
+ expect(page).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'as an admin user' do
+ let(:user) { create(:admin) }
+
+ context 'when admin mode disabled' do
+ it 'cannot delete user', :js do
+ visit admin_user_path(user_to_delete)
+
+ expect(page).to have_content('Re-authentication required')
+ end
+ end
+
+ context 'when admin mode enabled', :delete do
+ before do
+ gitlab_enable_admin_mode_sign_in(user)
+ end
+
+ it 'can delete user', :js do
+ visit admin_user_path(user_to_delete)
+ click_button 'Delete user'
+
+ page.within '.modal-dialog' do
+ find("input[name='username']").send_keys(user_to_delete.name)
+ click_button 'Delete user'
+
+ wait_for_requests
+ end
+
+ expect(page).to have_content('The user is being deleted.')
+
+ # Perform jobs while logged out so that admin mode is only enabled in job metadata
+ execute_jobs_signed_out(user)
+
+ visit admin_user_path(user_to_delete)
+
+ expect(page).to have_title('Not Found')
+ end
+ end
+ end
+
+ def add_sidekiq_middleware
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.add Gitlab::SidekiqMiddleware::AdminMode::Server
+ end
+ end
+
+ def execute_jobs_signed_out(user)
+ gitlab_sign_out
+
+ Sidekiq::Worker.drain_all
+
+ sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
+ end
+end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 3e8197588ed..954773e766d 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -2,46 +2,64 @@
require 'spec_helper'
-describe 'Admin uses repository checks' do
+describe 'Admin uses repository checks', :request_store, :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
include StubENV
+ let(:admin) { create(:admin) }
+
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- sign_in(create(:admin))
+ sign_in(admin)
end
- it 'to trigger a single check' do
- project = create(:project)
- visit_admin_project_page(project)
+ context 'when admin mode is disabled' do
+ it 'admin project page requires admin mode' do
+ project = create(:project)
+ visit_admin_project_page(project)
- page.within('.repository-check') do
- click_button 'Trigger repository check'
+ expect(page).not_to have_css('.repository-check')
+ expect(page).to have_content('Enter Admin Mode')
end
-
- expect(page).to have_content('Repository check was triggered')
end
- it 'to see a single failed repository check', :js do
- project = create(:project)
- project.update_columns(
- last_repository_check_failed: true,
- last_repository_check_at: Time.now
- )
- visit_admin_project_page(project)
+ context 'when admin mode is enabled' do
+ before do
+ gitlab_enable_admin_mode_sign_in(admin)
+ end
+
+ it 'to trigger a single check', :js do
+ project = create(:project)
+ visit_admin_project_page(project)
+
+ page.within('.repository-check') do
+ click_button 'Trigger repository check'
+ end
- page.within('.alert') do
- expect(page.text).to match(/Last repository check \(just now\) failed/)
+ expect(page).to have_content('Repository check was triggered')
end
- end
- it 'to clear all repository checks', :js do
- visit repository_admin_application_settings_path
+ it 'to see a single failed repository check', :js do
+ project = create(:project)
+ project.update_columns(
+ last_repository_check_failed: true,
+ last_repository_check_at: Time.now
+ )
+ visit_admin_project_page(project)
+
+ page.within('.alert') do
+ expect(page.text).to match(/Last repository check \(just now\) failed/)
+ end
+ end
- expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
+ it 'to clear all repository checks', :js do
+ visit repository_admin_application_settings_path
- accept_confirm { find(:link, 'Clear all repository checks').send_keys(:return) }
+ expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
- expect(page).to have_content('Started asynchronous removal of all repository check states.')
+ accept_confirm { find(:link, 'Clear all repository checks').send_keys(:return) }
+
+ expect(page).to have_content('Started asynchronous removal of all repository check states.')
+ end
end
def visit_admin_project_page(project)
diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb
index bb0072fc8dd..a856376cb4b 100644
--- a/spec/features/projects/clusters/eks_spec.rb
+++ b/spec/features/projects/clusters/eks_spec.rb
@@ -30,6 +30,10 @@ describe 'AWS EKS Cluster', :js do
it 'user sees a form to create an EKS cluster' do
expect(page).to have_content('Create new cluster on EKS')
end
+
+ it 'highlights Amazon EKS logo' do
+ expect(page).to have_css('.js-create-aws-cluster-button.active')
+ end
end
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 09cd1c6a765..0143461eadb 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -38,6 +38,10 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
click_link 'Google GKE'
end
+ it 'highlights Google GKE logo' do
+ expect(page).to have_css('.js-create-gcp-cluster-button.active')
+ end
+
context 'when user filled form with valid parameters' do
subject { submit_form }
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 3c82ae59cfa..daa987ea389 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -360,7 +360,7 @@ shared_examples 'Signup' do
InvisibleCaptcha.timestamp_enabled = true
stub_application_setting(recaptcha_enabled: true)
allow_next_instance_of(RegistrationsController) do |instance|
- allow(instance).to receive(:verify_recaptcha).and_return(false)
+ allow(instance).to receive(:verify_recaptcha).and_return(true)
end
end
@@ -368,28 +368,53 @@ shared_examples 'Signup' do
InvisibleCaptcha.timestamp_enabled = false
end
- it 'prevents from signing up' do
- visit new_user_registration_path
+ context 'when reCAPTCHA detects malicious behaviour' do
+ before do
+ allow_next_instance_of(RegistrationsController) do |instance|
+ allow(instance).to receive(:verify_recaptcha).and_return(false)
+ end
+ end
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
+ it 'prevents from signing up' do
+ visit new_user_registration_path
- if Gitlab::Experimentation.enabled?(:signup_flow)
- fill_in 'new_user_first_name', with: new_user.first_name
- fill_in 'new_user_last_name', with: new_user.last_name
- else
- fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ else
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ end
+
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.not_to change { User.count }
+ expect(page).to have_content('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
end
+ end
- fill_in 'new_user_password', with: new_user.password
+ context 'when invisible captcha detects malicious behaviour' do
+ it 'prevents from signing up' do
+ visit new_user_registration_path
- expect { click_button 'Register' }.not_to change { User.count }
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
- if Gitlab::Experimentation.enabled?(:signup_flow)
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ else
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ end
+
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content('That was a bit too quick! Please resubmit.')
- else
- expect(page).to have_content('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
end
end
end
diff --git a/spec/frontend/jobs/store/mutations_spec.js b/spec/frontend/jobs/store/mutations_spec.js
index d1ab152330e..d77690ffac0 100644
--- a/spec/frontend/jobs/store/mutations_spec.js
+++ b/spec/frontend/jobs/store/mutations_spec.js
@@ -157,17 +157,21 @@ describe('Jobs Store Mutations', () => {
});
});
- describe('STOP_POLLING_TRACE', () => {
- it('sets isTraceComplete to true', () => {
- mutations[types.STOP_POLLING_TRACE](stateCopy);
+ describe('SET_TRACE_TIMEOUT', () => {
+ it('sets the traceTimeout id', () => {
+ const id = 7;
- expect(stateCopy.isTraceComplete).toEqual(true);
+ expect(stateCopy.traceTimeout).not.toEqual(id);
+
+ mutations[types.SET_TRACE_TIMEOUT](stateCopy, id);
+
+ expect(stateCopy.traceTimeout).toEqual(id);
});
});
- describe('RECEIVE_TRACE_ERROR', () => {
- it('resets trace state and sets error to true', () => {
- mutations[types.RECEIVE_TRACE_ERROR](stateCopy);
+ describe('STOP_POLLING_TRACE', () => {
+ it('sets isTraceComplete to true', () => {
+ mutations[types.STOP_POLLING_TRACE](stateCopy);
expect(stateCopy.isTraceComplete).toEqual(true);
});
diff --git a/spec/frontend/registry/settings/components/settings_form_spec.js b/spec/frontend/registry/settings/components/settings_form_spec.js
index eefb0313a0b..2b3e529b283 100644
--- a/spec/frontend/registry/settings/components/settings_form_spec.js
+++ b/spec/frontend/registry/settings/components/settings_form_spec.js
@@ -1,7 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import Tracking from '~/tracking';
import component from '~/registry/settings/components/settings_form.vue';
-import expirationPolicyForm from '~/registry/shared/components/expiration_policy_form.vue';
+import expirationPolicyFields from '~/registry/shared/components/expiration_policy_fields.vue';
import { createStore } from '~/registry/settings/store/';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
@@ -14,14 +14,34 @@ describe('Settings Form', () => {
let store;
let dispatchSpy;
+ const GlLoadingIcon = { name: 'gl-loading-icon-stub', template: '<svg></svg>' };
+ const GlCard = {
+ name: 'gl-card-stub',
+ template: `
+ <div>
+ <slot name="header"></slot>
+ <slot></slot>
+ <slot name="footer"></slot>
+ </div>
+ `,
+ };
+
const trackingPayload = {
label: 'docker_container_retention_and_expiration_policies',
};
- const findForm = () => wrapper.find(expirationPolicyForm);
+ const findForm = () => wrapper.find({ ref: 'form-element' });
+ const findFields = () => wrapper.find(expirationPolicyFields);
+ const findCancelButton = () => wrapper.find({ ref: 'cancel-button' });
+ const findSaveButton = () => wrapper.find({ ref: 'save-button' });
+ const findLoadingIcon = (parent = wrapper) => parent.find(GlLoadingIcon);
const mountComponent = () => {
wrapper = shallowMount(component, {
+ stubs: {
+ GlCard,
+ GlLoadingIcon,
+ },
mocks: {
$toast: {
show: jest.fn(),
@@ -47,46 +67,50 @@ describe('Settings Form', () => {
let form;
beforeEach(() => {
form = findForm();
+ dispatchSpy.mockReturnValue();
});
describe('data binding', () => {
it('v-model change update the settings property', () => {
- dispatchSpy.mockReturnValue();
- form.vm.$emit('input', 'foo');
+ findFields().vm.$emit('input', 'foo');
expect(dispatchSpy).toHaveBeenCalledWith('updateSettings', { settings: 'foo' });
});
});
describe('form reset event', () => {
+ beforeEach(() => {
+ form.trigger('reset');
+ });
it('calls the appropriate function', () => {
- dispatchSpy.mockReturnValue();
- form.vm.$emit('reset');
expect(dispatchSpy).toHaveBeenCalledWith('resetSettings');
});
it('tracks the reset event', () => {
- dispatchSpy.mockReturnValue();
- form.vm.$emit('reset');
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'reset_form', trackingPayload);
});
});
describe('form submit event ', () => {
+ it('save has type submit', () => {
+ mountComponent();
+ expect(findSaveButton().attributes('type')).toBe('submit');
+ });
+
it('dispatches the saveSettings action', () => {
dispatchSpy.mockResolvedValue();
- form.vm.$emit('submit');
+ form.trigger('submit');
expect(dispatchSpy).toHaveBeenCalledWith('saveSettings');
});
it('tracks the submit event', () => {
dispatchSpy.mockResolvedValue();
- form.vm.$emit('submit');
+ form.trigger('submit');
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'submit_form', trackingPayload);
});
it('show a success toast when submit succeed', () => {
dispatchSpy.mockResolvedValue();
- form.vm.$emit('submit');
+ form.trigger('submit');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE, {
type: 'success',
@@ -96,7 +120,7 @@ describe('Settings Form', () => {
it('show an error toast when submit fails', () => {
dispatchSpy.mockRejectedValue();
- form.vm.$emit('submit');
+ form.trigger('submit');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_ERROR_MESSAGE, {
type: 'error',
@@ -105,4 +129,52 @@ describe('Settings Form', () => {
});
});
});
+
+ describe('form actions', () => {
+ describe('cancel button', () => {
+ beforeEach(() => {
+ store.commit('SET_SETTINGS', { foo: 'bar' });
+ });
+
+ it('has type reset', () => {
+ expect(findCancelButton().attributes('type')).toBe('reset');
+ });
+
+ it('is disabled when isEdited is false', () =>
+ wrapper.vm.$nextTick().then(() => {
+ expect(findCancelButton().attributes('disabled')).toBe('true');
+ }));
+
+ it('is disabled isLoading is true', () => {
+ store.commit('TOGGLE_LOADING');
+ store.commit('UPDATE_SETTINGS', { settings: { foo: 'baz' } });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findCancelButton().attributes('disabled')).toBe('true');
+ store.commit('TOGGLE_LOADING');
+ });
+ });
+
+ it('is enabled when isLoading is false and isEdited is true', () => {
+ store.commit('UPDATE_SETTINGS', { settings: { foo: 'baz' } });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findCancelButton().attributes('disabled')).toBe(undefined);
+ });
+ });
+ });
+
+ describe('when isLoading is true', () => {
+ beforeEach(() => {
+ store.commit('TOGGLE_LOADING');
+ });
+ afterEach(() => {
+ store.commit('TOGGLE_LOADING');
+ });
+
+ it('submit button is disabled and shows a spinner', () => {
+ const button = findSaveButton();
+ expect(button.attributes('disabled')).toBeTruthy();
+ expect(findLoadingIcon(button).exists()).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap
new file mode 100644
index 00000000000..c5f5ea68d9e
--- /dev/null
+++ b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap
@@ -0,0 +1,133 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Expiration Policy Form renders 1`] = `
+<div
+ class="lh-2"
+>
+ <glformgroup-stub
+ id="expiration-policy-toggle-group"
+ label="Expiration policy:"
+ label-align="right"
+ label-cols="3"
+ label-for="expiration-policy-toggle"
+ >
+ <div
+ class="d-flex align-items-start"
+ >
+ <gltoggle-stub
+ id="expiration-policy-toggle"
+ labeloff="Toggle Status: OFF"
+ labelon="Toggle Status: ON"
+ />
+
+ <span
+ class="mb-2 ml-1 lh-2"
+ >
+ Docker tag expiration policy is
+ <strong>
+ disabled
+ </strong>
+ </span>
+ </div>
+ </glformgroup-stub>
+
+ <glformgroup-stub
+ id="expiration-policy-interval-group"
+ label="Expiration interval:"
+ label-align="right"
+ label-cols="3"
+ label-for="expiration-policy-interval"
+ >
+ <glformselect-stub
+ disabled="true"
+ id="expiration-policy-interval"
+ >
+ <option
+ value="foo"
+ >
+
+ Foo
+
+ </option>
+ <option
+ value="bar"
+ >
+
+ Bar
+
+ </option>
+ </glformselect-stub>
+ </glformgroup-stub>
+ <glformgroup-stub
+ id="expiration-policy-schedule-group"
+ label="Expiration schedule:"
+ label-align="right"
+ label-cols="3"
+ label-for="expiration-policy-schedule"
+ >
+ <glformselect-stub
+ disabled="true"
+ id="expiration-policy-schedule"
+ >
+ <option
+ value="foo"
+ >
+
+ Foo
+
+ </option>
+ <option
+ value="bar"
+ >
+
+ Bar
+
+ </option>
+ </glformselect-stub>
+ </glformgroup-stub>
+ <glformgroup-stub
+ id="expiration-policy-latest-group"
+ label="Number of tags to retain:"
+ label-align="right"
+ label-cols="3"
+ label-for="expiration-policy-latest"
+ >
+ <glformselect-stub
+ disabled="true"
+ id="expiration-policy-latest"
+ >
+ <option
+ value="foo"
+ >
+
+ Foo
+
+ </option>
+ <option
+ value="bar"
+ >
+
+ Bar
+
+ </option>
+ </glformselect-stub>
+ </glformgroup-stub>
+
+ <glformgroup-stub
+ id="expiration-policy-name-matching-group"
+ invalid-feedback="The value of this input should be less than 255 characters"
+ label="Docker tags with names matching this regex pattern will expire:"
+ label-align="right"
+ label-cols="3"
+ label-for="expiration-policy-name-matching"
+ >
+ <glformtextarea-stub
+ disabled="true"
+ id="expiration-policy-name-matching"
+ placeholder=".*"
+ trim=""
+ value=""
+ />
+ </glformgroup-stub>
+</div>
+`;
diff --git a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_form_spec.js.snap b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_form_spec.js.snap
deleted file mode 100644
index b53736951e1..00000000000
--- a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_form_spec.js.snap
+++ /dev/null
@@ -1,186 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Expiration Policy Form renders 1`] = `
-<form
- class="lh-2"
->
- <div
- class="card"
- >
- <!---->
- <div
- class="card-header"
- >
-
- Tag expiration policy
-
- </div>
- <div
- class="card-body"
- >
- <!---->
- <!---->
-
- <glformgroup-stub
- id="expiration-policy-toggle-group"
- label="Expiration policy:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-toggle"
- >
- <div
- class="d-flex align-items-start"
- >
- <gltoggle-stub
- id="expiration-policy-toggle"
- labeloff="Toggle Status: OFF"
- labelon="Toggle Status: ON"
- />
-
- <span
- class="mb-2 ml-1 lh-2"
- >
- Docker tag expiration policy is
- <strong>
- disabled
- </strong>
- </span>
- </div>
- </glformgroup-stub>
-
- <glformgroup-stub
- id="expiration-policy-interval-group"
- label="Expiration interval:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-interval"
- >
- <glformselect-stub
- disabled="true"
- id="expiration-policy-interval"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </glformselect-stub>
- </glformgroup-stub>
-
- <glformgroup-stub
- id="expiration-policy-schedule-group"
- label="Expiration schedule:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-schedule"
- >
- <glformselect-stub
- disabled="true"
- id="expiration-policy-schedule"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </glformselect-stub>
- </glformgroup-stub>
-
- <glformgroup-stub
- id="expiration-policy-latest-group"
- label="Number of tags to retain:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-latest"
- >
- <glformselect-stub
- disabled="true"
- id="expiration-policy-latest"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </glformselect-stub>
- </glformgroup-stub>
-
- <glformgroup-stub
- id="expiration-policy-name-matching-group"
- invalid-feedback="The value of this input should be less than 255 characters"
- label="Docker tags with names matching this regex pattern will expire:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-name-matching"
- >
- <glformtextarea-stub
- disabled="true"
- id="expiration-policy-name-matching"
- placeholder=".*"
- trim=""
- value=""
- />
- </glformgroup-stub>
-
- </div>
- <div
- class="card-footer"
- >
- <div
- class="d-flex justify-content-end"
- >
- <glbutton-stub
- class="mr-2 d-block"
- size="md"
- type="reset"
- variant="secondary"
- >
-
- Cancel
-
- </glbutton-stub>
-
- <glbutton-stub
- class="d-flex justify-content-center align-items-center js-no-auto-disable"
- size="md"
- type="submit"
- variant="success"
- >
-
- Save expiration policy
-
- <!---->
- </glbutton-stub>
- </div>
- </div>
- <!---->
- </div>
-</form>
-`;
diff --git a/spec/frontend/registry/shared/components/expiration_policy_form_spec.js b/spec/frontend/registry/shared/components/expiration_policy_fields_spec.js
index b51519925f1..b384fd62406 100644
--- a/spec/frontend/registry/shared/components/expiration_policy_form_spec.js
+++ b/spec/frontend/registry/shared/components/expiration_policy_fields_spec.js
@@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
import stubChildren from 'helpers/stub_children';
-import component from '~/registry/shared/components/expiration_policy_form.vue';
+import component from '~/registry/shared/components/expiration_policy_fields.vue';
import { NAME_REGEX_LENGTH } from '~/registry/shared/constants';
import { formOptions } from '../mock_data';
@@ -10,22 +10,14 @@ describe('Expiration Policy Form', () => {
const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
- const GlLoadingIcon = { name: 'gl-loading-icon-stub', template: '<svg></svg>' };
-
const findFormGroup = name => wrapper.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}-group`);
const findFormElements = (name, parent = wrapper) =>
parent.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}`);
- const findCancelButton = () => wrapper.find({ ref: 'cancel-button' });
- const findSaveButton = () => wrapper.find({ ref: 'save-button' });
- const findForm = () => wrapper.find({ ref: 'form-element' });
- const findLoadingIcon = (parent = wrapper) => parent.find(GlLoadingIcon);
const mountComponent = props => {
wrapper = mount(component, {
stubs: {
...stubChildren(component),
- GlCard: false,
- GlLoadingIcon,
},
propsData: {
formOptions,
@@ -114,77 +106,20 @@ describe('Expiration Policy Form', () => {
},
);
- describe('form actions', () => {
- describe('cancel button', () => {
- it('has type reset', () => {
- mountComponent();
- expect(findCancelButton().attributes('type')).toBe('reset');
- });
-
- it('is disabled when disableCancelButton is true', () => {
- mountComponent({ disableCancelButton: true });
- return wrapper.vm.$nextTick().then(() => {
- expect(findCancelButton().attributes('disabled')).toBe('true');
- });
- });
-
- it('is disabled isLoading is true', () => {
- mountComponent({ isLoading: true });
- return wrapper.vm.$nextTick().then(() => {
- expect(findCancelButton().attributes('disabled')).toBe('true');
- });
- });
-
- it('is enabled when isLoading and disableCancelButton are false', () => {
- mountComponent({ disableCancelButton: false, isLoading: false });
- return wrapper.vm.$nextTick().then(() => {
- expect(findCancelButton().attributes('disabled')).toBe(undefined);
- });
- });
- });
-
- describe('form cancel event', () => {
- it('calls the appropriate function', () => {
- mountComponent();
- findForm().trigger('reset');
- expect(wrapper.emitted('reset')).toBeTruthy();
- });
- });
-
- it('save has type submit', () => {
- mountComponent();
- expect(findSaveButton().attributes('type')).toBe('submit');
- });
-
- describe('when isLoading is true', () => {
- beforeEach(() => {
- mountComponent({ isLoading: true });
- });
-
- it.each`
- elementName
- ${'toggle'}
- ${'interval'}
- ${'schedule'}
- ${'latest'}
- ${'name-matching'}
- `(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
- expect(findFormElements(elementName).attributes('disabled')).toBe('true');
- });
-
- it('submit button is disabled and shows a spinner', () => {
- const button = findSaveButton();
- expect(button.attributes('disabled')).toBeTruthy();
- expect(findLoadingIcon(button)).toExist();
- });
+ describe('when isLoading is true', () => {
+ beforeEach(() => {
+ mountComponent({ isLoading: true });
});
- describe('form submit event ', () => {
- it('calls the appropriate function', () => {
- mountComponent();
- findForm().trigger('submit');
- expect(wrapper.emitted('submit')).toBeTruthy();
- });
+ it.each`
+ elementName
+ ${'toggle'}
+ ${'interval'}
+ ${'schedule'}
+ ${'latest'}
+ ${'name-matching'}
+ `(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
+ expect(findFormElements(elementName).attributes('disabled')).toBe('true');
});
});
@@ -196,20 +131,20 @@ describe('Expiration Policy Form', () => {
mountComponent({ value: { name_regex: invalidString } });
});
- it('save btn is disabled', () => {
- expect(findSaveButton().attributes('disabled')).toBeTruthy();
- });
-
it('nameRegexState is false', () => {
expect(wrapper.vm.nameRegexState).toBe(false);
});
+
+ it('emit the @invalidated event', () => {
+ expect(wrapper.emitted('invalidated')).toBeTruthy();
+ });
});
it('if the user did not type validation is null', () => {
mountComponent({ value: { name_regex: '' } });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.nameRegexState).toBe(null);
- expect(findSaveButton().attributes('disabled')).toBeFalsy();
+ expect(wrapper.emitted('validated')).toBeTruthy();
});
});
diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb
index ff8394b9475..5651b899ed0 100644
--- a/spec/helpers/clusters_helper_spec.rb
+++ b/spec/helpers/clusters_helper_spec.rb
@@ -58,32 +58,4 @@ describe ClustersHelper do
it { is_expected.to eq('Create new cluster') }
end
end
-
- describe '#render_new_provider_form' do
- subject { helper.new_cluster_partial(provider: provider) }
-
- context 'GCP provider' do
- let(:provider) { 'gcp' }
-
- it { is_expected.to eq('clusters/clusters/gcp/new') }
- end
-
- context 'AWS provider' do
- let(:provider) { 'aws' }
-
- it { is_expected.to eq('clusters/clusters/aws/new') }
- end
-
- context 'other provider' do
- let(:provider) { 'other' }
-
- it { is_expected.to eq('clusters/clusters/cloud_providers/cloud_provider_selector') }
- end
-
- context 'no provider' do
- let(:provider) { nil }
-
- it { is_expected.to eq('clusters/clusters/cloud_providers/cloud_provider_selector') }
- end
- end
end
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index 0fcd6080106..31b49c45908 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -6,7 +6,6 @@ import axios from '~/lib/utils/axios_utils';
import jobApp from '~/jobs/components/job_app.vue';
import createStore from '~/jobs/store';
import * as types from '~/jobs/store/mutation_types';
-import { resetStore } from '../store/helpers';
import job from '../mock_data';
describe('Job App ', () => {
@@ -16,24 +15,29 @@ describe('Job App ', () => {
let vm;
let mock;
- const props = {
+ const initSettings = {
endpoint: `${gl.TEST_HOST}jobs/123.json`,
+ pagePath: `${gl.TEST_HOST}jobs/123`,
+ logState:
+ 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D',
+ };
+
+ const props = {
runnerHelpUrl: 'help/runner',
deploymentHelpUrl: 'help/deployment',
runnerSettingsUrl: 'settings/ci-cd/runners',
variablesSettingsUrl: 'settings/ci-cd/variables',
terminalPath: 'jobs/123/terminal',
- pagePath: `${gl.TEST_HOST}jobs/123`,
projectPath: 'user-name/project-name',
subscriptionsMoreMinutesUrl: 'https://customers.gitlab.com/buy_pipeline_minutes',
- logState:
- 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D',
};
const waitForJobReceived = () => waitForMutation(store, types.RECEIVE_JOB_SUCCESS);
const setupAndMount = ({ jobData = {}, traceData = {} } = {}) => {
- mock.onGet(props.endpoint).replyOnce(200, { ...job, ...jobData });
- mock.onGet(`${props.pagePath}/trace.json`).reply(200, traceData);
+ mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
+ mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, traceData);
+
+ store.dispatch('init', initSettings);
vm = mountComponentWithStore(Component, { props, store });
@@ -46,7 +50,6 @@ describe('Job App ', () => {
});
afterEach(() => {
- resetStore(store);
vm.$destroy();
mock.restore();
});
@@ -384,7 +387,6 @@ describe('Job App ', () => {
})
.then(done)
.catch(done.fail);
- done();
});
it('displays remaining time for a delayed job', done => {
diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js
index c0e8dbf9b22..47257688bd5 100644
--- a/spec/javascripts/jobs/store/actions_spec.js
+++ b/spec/javascripts/jobs/store/actions_spec.js
@@ -15,6 +15,7 @@ import {
scrollBottom,
requestTrace,
fetchTrace,
+ startPollingTrace,
stopPollingTrace,
receiveTraceSuccess,
receiveTraceError,
@@ -241,6 +242,50 @@ describe('Job State actions', () => {
done,
);
});
+
+ describe('when job is incomplete', () => {
+ let tracePayload;
+
+ beforeEach(() => {
+ tracePayload = {
+ html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :',
+ complete: false,
+ };
+
+ mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, tracePayload);
+ });
+
+ it('dispatches startPollingTrace', done => {
+ testAction(
+ fetchTrace,
+ null,
+ mockedState,
+ [],
+ [
+ { type: 'toggleScrollisInBottom', payload: true },
+ { type: 'receiveTraceSuccess', payload: tracePayload },
+ { type: 'startPollingTrace' },
+ ],
+ done,
+ );
+ });
+
+ it('does not dispatch startPollingTrace when timeout is non-empty', done => {
+ mockedState.traceTimeout = 1;
+
+ testAction(
+ fetchTrace,
+ null,
+ mockedState,
+ [],
+ [
+ { type: 'toggleScrollisInBottom', payload: true },
+ { type: 'receiveTraceSuccess', payload: tracePayload },
+ ],
+ done,
+ );
+ });
+ });
});
describe('error', () => {
@@ -265,16 +310,69 @@ describe('Job State actions', () => {
});
});
+ describe('startPollingTrace', () => {
+ let dispatch;
+ let commit;
+
+ beforeEach(() => {
+ jasmine.clock().install();
+
+ dispatch = jasmine.createSpy();
+ commit = jasmine.createSpy();
+
+ startPollingTrace({ dispatch, commit });
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('should save the timeout id but not call fetchTrace', () => {
+ expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 1);
+ expect(dispatch).not.toHaveBeenCalledWith('fetchTrace');
+ });
+
+ describe('after timeout has passed', () => {
+ beforeEach(() => {
+ jasmine.clock().tick(4000);
+ });
+
+ it('should clear the timeout id and fetchTrace', () => {
+ expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 0);
+ expect(dispatch).toHaveBeenCalledWith('fetchTrace');
+ });
+ });
+ });
+
describe('stopPollingTrace', () => {
+ let origTimeout;
+
+ beforeEach(() => {
+ // Can't use spyOn(window, 'clearTimeout') because this caused unrelated specs to timeout
+ // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23838#note_280277727
+ origTimeout = window.clearTimeout;
+ window.clearTimeout = jasmine.createSpy();
+ });
+
+ afterEach(() => {
+ window.clearTimeout = origTimeout;
+ });
+
it('should commit STOP_POLLING_TRACE mutation ', done => {
+ const traceTimeout = 7;
+
testAction(
stopPollingTrace,
null,
- mockedState,
- [{ type: types.STOP_POLLING_TRACE }],
+ { ...mockedState, traceTimeout },
+ [{ type: types.SET_TRACE_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_TRACE }],
[],
- done,
- );
+ )
+ .then(() => {
+ expect(window.clearTimeout).toHaveBeenCalledWith(traceTimeout);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -292,15 +390,8 @@ describe('Job State actions', () => {
});
describe('receiveTraceError', () => {
- it('should commit RECEIVE_TRACE_ERROR mutation ', done => {
- testAction(
- receiveTraceError,
- null,
- mockedState,
- [{ type: types.RECEIVE_TRACE_ERROR }],
- [],
- done,
- );
+ it('should commit stop polling trace', done => {
+ testAction(receiveTraceError, null, mockedState, [], [{ type: 'stopPollingTrace' }], done);
});
});
diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb
index 3b3db0f7315..7c2fdac6c25 100644
--- a/spec/lib/gitlab/auth/current_user_mode_spec.rb
+++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb
@@ -2,10 +2,10 @@
require 'spec_helper'
-describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
+describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode, :request_store do
include_context 'custom session'
- let(:user) { build(:user) }
+ let(:user) { build_stubbed(:user) }
subject { described_class.new(user) }
@@ -13,54 +13,66 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session])
end
- describe '#admin_mode?', :request_store do
- context 'when the user is a regular user' do
- it 'is false by default' do
- expect(subject.admin_mode?).to be(false)
- end
+ shared_examples 'admin mode cannot be enabled' do
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'cannot be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password)
+ it 'cannot be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password)
- expect(subject.admin_mode?).to be(false)
- end
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'cannot be enabled with an invalid password' do
- subject.enable_admin_mode!(password: nil)
+ it 'cannot be enabled with an invalid password' do
+ subject.enable_admin_mode!(password: nil)
- expect(subject.admin_mode?).to be(false)
- end
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'cannot be enabled with empty params' do
- subject.enable_admin_mode!
+ it 'cannot be enabled with empty params' do
+ subject.enable_admin_mode!
- expect(subject.admin_mode?).to be(false)
- end
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'disable has no effect' do
- subject.enable_admin_mode!
- subject.disable_admin_mode!
+ it 'disable has no effect' do
+ subject.enable_admin_mode!
+ subject.disable_admin_mode!
+
+ expect(subject.admin_mode?).to be(false)
+ end
+
+ context 'skipping password validation' do
+ it 'cannot be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
expect(subject.admin_mode?).to be(false)
end
- context 'skipping password validation' do
- it 'cannot be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+ it 'cannot be enabled with an invalid password' do
+ subject.enable_admin_mode!(skip_password_validation: true)
- expect(subject.admin_mode?).to be(false)
- end
+ expect(subject.admin_mode?).to be(false)
+ end
+ end
+ end
- it 'cannot be enabled with an invalid password' do
- subject.enable_admin_mode!(skip_password_validation: true)
+ describe '#admin_mode?' do
+ context 'when the user is a regular user' do
+ it_behaves_like 'admin mode cannot be enabled'
- expect(subject.admin_mode?).to be(false)
+ context 'bypassing session' do
+ it_behaves_like 'admin mode cannot be enabled' do
+ around do |example|
+ described_class.bypass_session!(user.id) { example.run }
+ end
end
end
end
context 'when the user is an admin' do
- let(:user) { build(:user, :admin) }
+ let(:user) { build_stubbed(:user, :admin) }
context 'when admin mode not requested' do
it 'is false by default' do
@@ -148,11 +160,36 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
end
end
+
+ context 'bypassing session' do
+ it 'is active by default' do
+ described_class.bypass_session!(user.id) do
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ it 'enable has no effect' do
+ described_class.bypass_session!(user.id) do
+ subject.request_admin_mode!
+ subject.enable_admin_mode!(password: user.password)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ it 'disable has no effect' do
+ described_class.bypass_session!(user.id) do
+ subject.disable_admin_mode!
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+ end
end
end
describe '#enable_admin_mode!' do
- let(:user) { build(:user, :admin) }
+ let(:user) { build_stubbed(:user, :admin) }
it 'creates a timestamp in the session' do
subject.request_admin_mode!
@@ -163,7 +200,7 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
describe '#enable_sessionless_admin_mode!' do
- let(:user) { build(:user, :admin) }
+ let(:user) { build_stubbed(:user, :admin) }
it 'enabled admin mode without password' do
subject.enable_sessionless_admin_mode!
@@ -173,7 +210,7 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
describe '#disable_admin_mode!' do
- let(:user) { build(:user, :admin) }
+ let(:user) { build_stubbed(:user, :admin) }
it 'sets the session timestamp to nil' do
subject.request_admin_mode!
@@ -183,6 +220,73 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
end
end
+ describe '.bypass_session!' do
+ context 'with a regular user' do
+ it 'admin mode is false' do
+ described_class.bypass_session!(user.id) do
+ expect(subject.admin_mode?).to be(false)
+ expect(described_class.bypass_session_admin_id).to be(user.id)
+ end
+
+ expect(described_class.bypass_session_admin_id).to be_nil
+ end
+ end
+
+ context 'with an admin user' do
+ let(:user) { build_stubbed(:user, :admin) }
+
+ it 'admin mode is true' do
+ described_class.bypass_session!(user.id) do
+ expect(subject.admin_mode?).to be(true)
+ expect(described_class.bypass_session_admin_id).to be(user.id)
+ end
+
+ expect(described_class.bypass_session_admin_id).to be_nil
+ end
+ end
+ end
+
+ describe '.with_current_request_admin_mode' do
+ context 'with a regular user' do
+ it 'user is not available inside nor outside the yielded block' do
+ described_class.with_current_admin(user) do
+ expect(described_class.current_admin).to be_nil
+ end
+
+ expect(described_class.bypass_session_admin_id).to be_nil
+ end
+ end
+
+ context 'with an admin user' do
+ let(:user) { build_stubbed(:user, :admin) }
+
+ context 'admin mode is disabled' do
+ it 'user is not available inside nor outside the yielded block' do
+ described_class.with_current_admin(user) do
+ expect(described_class.current_admin).to be_nil
+ end
+
+ expect(described_class.bypass_session_admin_id).to be_nil
+ end
+ end
+
+ context 'admin mode is enabled' do
+ before do
+ subject.request_admin_mode!
+ subject.enable_admin_mode!(password: user.password)
+ end
+
+ it 'user is available only inside the yielded block' do
+ described_class.with_current_admin(user) do
+ expect(described_class.current_admin).to be(user)
+ end
+
+ expect(described_class.current_admin).to be_nil
+ end
+ end
+ end
+ end
+
def expected_session_entry(value_matcher)
{
Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
diff --git a/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
index 5cad479ff05..4714712f733 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectFullpathInRepoConfig, :migr
let(:group) { namespaces.create!(name: 'foo', path: 'foo') }
let(:subgroup) { namespaces.create!(name: 'bar', path: 'bar', parent_id: group.id) }
- describe described_class::Storage::HashedProject do
+ describe described_class::Storage::Hashed do
let(:project) { double(id: 555) }
subject(:project_storage) { described_class.new(project) }
diff --git a/spec/lib/gitlab/sidekiq_middleware/admin_mode/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/admin_mode/client_spec.rb
new file mode 100644
index 00000000000..f6449bae8c3
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/admin_mode/client_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::AdminMode::Client, :do_not_mock_admin_mode, :request_store do
+ include AdminModeHelper
+
+ let(:worker) do
+ Class.new do
+ def perform; end
+ end
+ end
+
+ let(:job) { {} }
+ let(:queue) { :test }
+
+ it 'yields block' do
+ expect do |b|
+ subject.call(worker, job, queue, nil, &b)
+ end.to yield_control.once
+ end
+
+ context 'user is a regular user' do
+ it 'no admin mode field in payload' do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).not_to include('admin_mode_user_id')
+ end
+ end
+
+ context 'user is an administrator' do
+ let(:admin) { create(:admin) }
+
+ context 'admin mode disabled' do
+ it 'no admin mode field in payload' do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).not_to include('admin_mode_user_id')
+ end
+ end
+
+ context 'admin mode enabled' do
+ before do
+ enable_admin_mode!(admin)
+ end
+
+ context 'when sidekiq required context not set' do
+ it 'no admin mode field in payload' do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).not_to include('admin_mode_user_id')
+ end
+ end
+
+ context 'when user stored in current request' do
+ it 'has admin mode field in payload' do
+ Gitlab::Auth::CurrentUserMode.with_current_admin(admin) do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).to include('admin_mode_user_id' => admin.id)
+ end
+ end
+ end
+
+ context 'when bypassing session' do
+ it 'has admin mode field in payload' do
+ Gitlab::Auth::CurrentUserMode.bypass_session!(admin.id) do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).to include('admin_mode_user_id' => admin.id)
+ end
+ end
+ end
+ end
+ end
+
+ context 'admin mode feature disabled' do
+ before do
+ stub_feature_flags(user_mode_in_session: false)
+ end
+
+ it 'yields block' do
+ expect do |b|
+ subject.call(worker, job, queue, nil, &b)
+ end.to yield_control.once
+ end
+
+ it 'no admin mode field in payload' do
+ subject.call(worker, job, queue, nil) { nil }
+
+ expect(job).not_to include('admin_mode_user_id')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/admin_mode/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/admin_mode/server_spec.rb
new file mode 100644
index 00000000000..60475f0e403
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/admin_mode/server_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::AdminMode::Server, :do_not_mock_admin_mode, :request_store do
+ include AdminModeHelper
+
+ let(:worker) do
+ Class.new do
+ def perform; end
+ end
+ end
+
+ let(:job) { {} }
+ let(:queue) { :test }
+
+ it 'yields block' do
+ expect do |b|
+ subject.call(worker, job, queue, &b)
+ end.to yield_control.once
+ end
+
+ context 'job has no admin mode field' do
+ it 'session is not bypassed' do
+ subject.call(worker, job, queue) do
+ expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
+ end
+ end
+ end
+
+ context 'job has admin mode field' do
+ let(:admin) { create(:admin) }
+
+ context 'nil admin mode id' do
+ let(:job) { { 'admin_mode_user_id' => nil } }
+
+ it 'session is not bypassed' do
+ subject.call(worker, job, queue) do
+ expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
+ end
+ end
+ end
+
+ context 'valid admin mode id' do
+ let(:job) { { 'admin_mode_user_id' => admin.id } }
+
+ it 'session is bypassed' do
+ subject.call(worker, job, queue) do
+ expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be(admin.id)
+ end
+ end
+ end
+ end
+
+ context 'admin mode feature disabled' do
+ before do
+ stub_feature_flags(user_mode_in_session: false)
+ end
+
+ it 'yields block' do
+ expect do |b|
+ subject.call(worker, job, queue, &b)
+ end.to yield_control.once
+ end
+
+ it 'session is not bypassed' do
+ subject.call(worker, job, queue) do
+ expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
index e8dcbbd2ee1..19242d25e27 100644
--- a/spec/lib/gitlab/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -45,7 +45,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqMiddleware::ArgumentsLogger,
Gitlab::SidekiqMiddleware::MemoryKiller,
Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
- Gitlab::SidekiqMiddleware::WorkerContext::Server
+ Gitlab::SidekiqMiddleware::WorkerContext::Server,
+ Gitlab::SidekiqMiddleware::AdminMode::Server
]
end
let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
@@ -115,7 +116,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqStatus::ClientMiddleware,
Gitlab::SidekiqMiddleware::ClientMetrics,
Gitlab::SidekiqMiddleware::WorkerContext::Client,
- Labkit::Middleware::Sidekiq::Client
+ Labkit::Middleware::Sidekiq::Client,
+ Gitlab::SidekiqMiddleware::AdminMode::Client
]
end
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
index 64ab8d85807..25538db159e 100644
--- a/spec/lib/microsoft_teams/notifier_spec.rb
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -17,7 +17,7 @@ describe MicrosoftTeams::Notifier do
text: '[#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1)',
image: 'http://someimage.com'
},
- attachments: 'please fix'
+ attachments: "[GitLab](https://gitlab.com)\n\n- _Ruby_\n- **Go**\n"
}
end
@@ -31,13 +31,7 @@ describe MicrosoftTeams::Notifier do
'activityImage' => 'http://someimage.com'
},
{
- 'title' => 'Details',
- 'facts' => [
- {
- 'name' => 'Attachments',
- 'value' => 'please fix'
- }
- ]
+ text: "[GitLab](https://gitlab.com)\n\n- _Ruby_\n- **Go**\n"
}
],
'title' => 'JohnDoe4/project2',
@@ -54,4 +48,14 @@ describe MicrosoftTeams::Notifier do
expect(subject.ping(options)).to be true
end
end
+
+ describe '#body' do
+ it 'returns Markdown-based body when HTML was passed' do
+ expect(subject.send(:body, options)).to eq(body.to_json)
+ end
+
+ it 'fails when empty Hash was passed' do
+ expect { subject.send(:body, {}) }.to raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 5c3f7c09e22..5aa74134692 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -84,6 +84,16 @@ describe Issue do
end
end
+ describe '.simple_sorts' do
+ it 'includes all keys' do
+ expect(described_class.simple_sorts.keys).to include(
+ *%w(created_asc created_at_asc created_date created_desc created_at_desc
+ closest_future_date closest_future_date_asc due_date due_date_asc due_date_desc
+ id_asc id_desc relative_position relative_position_asc
+ updated_desc updated_asc updated_at_asc updated_at_desc))
+ end
+ end
+
describe '#order_by_position_and_priority' do
let(:project) { create :project }
let(:p1) { create(:label, title: 'P1', project: project, priority: 1) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 438ed6d83fa..42151d86ac0 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2985,9 +2985,9 @@ describe User, :do_not_mock_admin_mode do
end
end
- describe '#can_read_all_resources?' do
+ describe '#can_read_all_resources?', :request_store do
it 'returns false for regular user' do
- user = build(:user)
+ user = build_stubbed(:user)
expect(user.can_read_all_resources?).to be_falsy
end
@@ -2995,7 +2995,7 @@ describe User, :do_not_mock_admin_mode do
context 'for admin user' do
include_context 'custom session'
- let(:user) { build(:user, :admin) }
+ let(:user) { build_stubbed(:user, :admin) }
context 'when admin mode is disabled' do
it 'returns false' do
diff --git a/spec/policies/base_policy_spec.rb b/spec/policies/base_policy_spec.rb
index 81aee4cfcac..ae5af9e0f29 100644
--- a/spec/policies/base_policy_spec.rb
+++ b/spec/policies/base_policy_spec.rb
@@ -23,8 +23,8 @@ describe BasePolicy, :do_not_mock_admin_mode do
end
describe 'read cross project' do
- let(:current_user) { create(:user) }
- let(:user) { create(:user) }
+ let(:current_user) { build_stubbed(:user) }
+ let(:user) { build_stubbed(:user) }
subject { described_class.new(current_user, [user]) }
@@ -38,7 +38,7 @@ describe BasePolicy, :do_not_mock_admin_mode do
it { is_expected.not_to be_allowed(:read_cross_project) }
context 'for admins' do
- let(:current_user) { build(:admin) }
+ let(:current_user) { build_stubbed(:admin) }
subject { described_class.new(current_user, nil) }
@@ -56,14 +56,14 @@ describe BasePolicy, :do_not_mock_admin_mode do
end
describe 'full private access' do
- let(:current_user) { create(:user) }
+ let(:current_user) { build_stubbed(:user) }
subject { described_class.new(current_user, nil) }
it { is_expected.not_to be_allowed(:read_all_resources) }
context 'for admins' do
- let(:current_user) { build(:admin) }
+ let(:current_user) { build_stubbed(:admin) }
it 'allowed when in admin mode' do
enable_admin_mode!(current_user)
diff --git a/spec/services/error_tracking/issue_details_service_spec.rb b/spec/services/error_tracking/issue_details_service_spec.rb
index 60d6172ef64..9f217deda21 100644
--- a/spec/services/error_tracking/issue_details_service_spec.rb
+++ b/spec/services/error_tracking/issue_details_service_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
describe ErrorTracking::IssueDetailsService do
include_context 'sentry error tracking context'
+ subject { described_class.new(project, user, params) }
+
describe '#execute' do
context 'with authorized user' do
context 'when issue_details returns a detailed error' do
diff --git a/spec/services/error_tracking/issue_latest_event_service_spec.rb b/spec/services/error_tracking/issue_latest_event_service_spec.rb
index 7f53eabd717..078d7511850 100644
--- a/spec/services/error_tracking/issue_latest_event_service_spec.rb
+++ b/spec/services/error_tracking/issue_latest_event_service_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
describe ErrorTracking::IssueLatestEventService do
include_context 'sentry error tracking context'
+ subject { described_class.new(project, user) }
+
describe '#execute' do
context 'with authorized user' do
context 'when issue_latest_event returns an error event' do
diff --git a/spec/services/error_tracking/issue_update_service_spec.rb b/spec/services/error_tracking/issue_update_service_spec.rb
index ad1dafe6ccc..78388328a22 100644
--- a/spec/services/error_tracking/issue_update_service_spec.rb
+++ b/spec/services/error_tracking/issue_update_service_spec.rb
@@ -7,16 +7,19 @@ describe ErrorTracking::IssueUpdateService do
let(:arguments) { { issue_id: 1234, status: 'resolved' } }
- subject { described_class.new(project, user, arguments) }
+ subject(:update_service) { described_class.new(project, user, arguments) }
shared_examples 'does not perform close issue flow' do
it 'does not call the close issue service' do
+ update_service.execute
+
expect(issue_close_service)
- .not_to receive(:execute)
+ .not_to have_received(:execute)
end
it 'does not create system note' do
expect(SystemNoteService).not_to receive(:close_after_error_tracking_resolve)
+ update_service.execute
end
end
@@ -31,13 +34,13 @@ describe ErrorTracking::IssueUpdateService do
end
it 'returns the response' do
- expect(result).to eq(update_issue_response.merge(status: :success, closed_issue_iid: nil))
+ expect(update_service.execute).to eq(update_issue_response.merge(status: :success, closed_issue_iid: nil))
end
it 'updates any related issue' do
- expect(subject).to receive(:update_related_issue)
+ expect(update_service).to receive(:update_related_issue)
- result
+ update_service.execute
end
context 'related issue and resolving' do
@@ -48,39 +51,46 @@ describe ErrorTracking::IssueUpdateService do
let(:issue_close_service) { spy(:issue_close_service) }
before do
- allow_any_instance_of(SentryIssueFinder)
- .to receive(:execute)
- .and_return(sentry_issue)
+ allow_next_instance_of(SentryIssueFinder) do |finder|
+ allow(finder).to receive(:execute).and_return(sentry_issue)
+ end
allow(Issues::CloseService)
.to receive(:new)
.and_return(issue_close_service)
- end
- after do
- result
+ allow(issue_close_service)
+ .to receive(:execute)
+ .and_return(issue)
end
it 'closes the issue' do
+ update_service.execute
+
expect(issue_close_service)
- .to receive(:execute)
+ .to have_received(:execute)
.with(issue, system_note: false)
- .and_return(issue)
end
- it 'creates a system note' do
- expect(SystemNoteService).to receive(:close_after_error_tracking_resolve)
- end
+ context 'issues gets closed' do
+ let(:closed_issue) { create(:issue, :closed, project: project) }
- it 'returns a response with closed issue' do
- closed_issue = create(:issue, :closed, project: project)
+ before do
+ expect(issue_close_service)
+ .to receive(:execute)
+ .with(issue, system_note: false)
+ .and_return(closed_issue)
+ end
- expect(issue_close_service)
- .to receive(:execute)
- .with(issue, system_note: false)
- .and_return(closed_issue)
+ it 'creates a system note' do
+ expect(SystemNoteService).to receive(:close_after_error_tracking_resolve)
+
+ update_service.execute
+ end
- expect(result).to eq(status: :success, updated: true, closed_issue_iid: closed_issue.iid)
+ it 'returns a response with closed issue' do
+ expect(update_service.execute).to eq(status: :success, updated: true, closed_issue_iid: closed_issue.iid)
+ end
end
context 'issue is already closed' do
diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb
index bf637b70aaf..b81dd3d7e3f 100644
--- a/spec/services/projects/after_rename_service_spec.rb
+++ b/spec/services/projects/after_rename_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Projects::AfterRenameService do
let(:rugged_config) { rugged_repo(project.repository).config }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
- let(:hashed_storage) { Storage::HashedProject.new(project) }
+ let(:hashed_storage) { Storage::Hashed.new(project) }
let!(:path_before_rename) { project.path }
let!(:full_path_before_rename) { project.full_path }
let!(:path_after_rename) { "#{project.path}-renamed" }
diff --git a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
index ab9d2bdba8f..b0827f6a2ee 100644
--- a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
@@ -7,7 +7,7 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
- let(:hashed_storage) { Storage::HashedProject.new(project) }
+ let(:hashed_storage) { Storage::Hashed.new(project) }
let!(:upload) { Upload.find_by(path: file_uploader.upload_path) }
let(:file_uploader) { build(:file_uploader, project: project) }
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index 132b895fc35..71be335c11d 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -8,7 +8,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
- let(:hashed_storage) { Storage::HashedProject.new(project) }
+ let(:hashed_storage) { Storage::Hashed.new(project) }
subject(:service) { described_class.new(project: project, old_disk_path: project.disk_path) }
diff --git a/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
index c2ba9626f41..98b343371df 100644
--- a/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
@@ -7,7 +7,7 @@ describe Projects::HashedStorage::RollbackAttachmentsService do
let(:project) { create(:project, :repository, skip_disk_validation: true) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
- let(:hashed_storage) { Storage::HashedProject.new(project) }
+ let(:hashed_storage) { Storage::Hashed.new(project) }
let!(:upload) { Upload.find_by(path: file_uploader.upload_path) }
let(:file_uploader) { build(:file_uploader, project: project) }
diff --git a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
index 97c7c0af946..6dcd2ff4555 100644
--- a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
@@ -8,7 +8,7 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :repository, :wiki_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
- let(:hashed_storage) { Storage::HashedProject.new(project) }
+ let(:hashed_storage) { Storage::Hashed.new(project) }
subject(:service) { described_class.new(project: project, old_disk_path: project.disk_path) }
diff --git a/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb b/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
index ee678580fb9..f06de53f0c1 100644
--- a/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
+++ b/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
@@ -9,8 +9,6 @@ shared_context 'sentry error tracking context' do
let(:params) { {} }
let(:result) { subject.execute }
- subject { described_class.new(project, user, params) }
-
let(:error_tracking_setting) do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end