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-03-03 18:08:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 18:08:08 +0300
commit946771d0b016ae92b15a60bc3290a33b94191ffe (patch)
tree64862c2433989483f5fce45d5539242577a362eb /spec
parentf1e2fca19a90a6992c2020cf8c2159cfb0b61bca (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/wiki_pages.rb2
-rw-r--r--spec/features/projects/active_tabs_spec.rb5
-rw-r--r--spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb36
-rw-r--r--spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/project.json144
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap8
-rw-r--r--spec/frontend/search_spec.js (renamed from spec/javascripts/search_spec.js)13
-rw-r--r--spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js (renamed from spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js)118
-rw-r--r--spec/lib/gitlab/checks/snippet_check_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb28
-rw-r--r--spec/lib/gitlab/sidekiq_queue_spec.rb94
-rw-r--r--spec/models/application_setting_spec.rb6
-rw-r--r--spec/models/wiki_page_spec.rb56
-rw-r--r--spec/requests/api/admin/sidekiq_spec.rb65
-rw-r--r--spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb71
-rw-r--r--spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb8
16 files changed, 577 insertions, 88 deletions
diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb
index 761ba58edb2..86571f062ba 100644
--- a/spec/factories/wiki_pages.rb
+++ b/spec/factories/wiki_pages.rb
@@ -16,7 +16,7 @@ FactoryBot.define do
page { OpenStruct.new(url_path: 'some-name') }
association :wiki, factory: :project_wiki, strategy: :build
- initialize_with { new(wiki, page, true) }
+ initialize_with { new(wiki, page) }
before(:create) do |page, evaluator|
page.attributes = evaluator.attrs
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index 41c0e583815..d7060c63d6d 100644
--- a/spec/features/projects/active_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -139,11 +139,6 @@ describe 'Project active tab' do
it_behaves_like 'page has active sub tab', _('Repository Analytics')
end
- context 'on project Analytics/Repository Analytics' do
- it_behaves_like 'page has active tab', _('Analytics')
- it_behaves_like 'page has active sub tab', _('Repository Analytics')
- end
-
context 'on project Analytics/Cycle Analytics' do
before do
click_tab(_('CI / CD Analytics'))
diff --git a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
index 5aba16597b8..c0fcd10f394 100644
--- a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
+++ b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb
@@ -14,7 +14,7 @@ describe 'Projects > Show > User sees last commit CI status' do
page.within '.commit-detail' do
expect(page).to have_content(project.commit.sha[0..6])
- expect(page).to have_selector('[aria-label="Commit: skipped"]')
+ expect(page).to have_selector('[aria-label="Pipeline: skipped"]')
end
end
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 1a9cde4571e..8a338756323 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -33,6 +33,8 @@ describe 'User views a wiki page' do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
+
+ expect(page).to have_content('Wiki was successfully updated.')
end
it 'shows the history of a page that has a path' do
@@ -62,8 +64,10 @@ describe 'User views a wiki page' do
expect(page).to have_content('Edit Page')
fill_in('Content', with: 'Updated Wiki Content')
-
click_on('Save changes')
+
+ expect(page).to have_content('Wiki was successfully updated.')
+
click_on('Page history')
page.within(:css, '.nav-text') do
@@ -132,6 +136,36 @@ describe 'User views a wiki page' do
end
end
+ context 'when a page has special characters in its title' do
+ let(:title) { '<foo> !@#$%^&*()[]{}=_+\'"\\|<>? <bar>' }
+
+ before do
+ wiki_page.update(title: title )
+ end
+
+ it 'preserves the special characters' do
+ visit(project_wiki_path(project, wiki_page))
+
+ expect(page).to have_css('.wiki-page-title', text: title)
+ expect(page).to have_css('.wiki-pages li', text: title)
+ end
+ end
+
+ context 'when a page has XSS in its title or content' do
+ let(:title) { '<script>alert("title")<script>' }
+
+ before do
+ wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar')
+ end
+
+ it 'safely displays the page' do
+ visit(project_wiki_path(project, wiki_page))
+
+ expect(page).to have_css('.wiki-page-title', text: title)
+ expect(page).to have_content('foo bar')
+ end
+ end
+
context 'when a page has XSS in its message' do
before do
wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update')
diff --git a/spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/project.json b/spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/project.json
new file mode 100644
index 00000000000..5ca803cc11f
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/project.json
@@ -0,0 +1,144 @@
+{
+ "approvals_before_merge": 0,
+ "archived": false,
+ "auto_cancel_pending_pipelines": "enabled",
+ "autoclose_referenced_issues": true,
+ "boards": [],
+ "build_allow_git_fetch": true,
+ "build_coverage_regex": null,
+ "build_timeout": 3600,
+ "ci_cd_settings": {
+ "group_runners_enabled": true
+ },
+ "ci_config_path": null,
+ "ci_pipelines": [
+ {
+ "before_sha": "0000000000000000000000000000000000000000",
+ "committed_at": null,
+ "config_source": "repository_source",
+ "created_at": "2020-02-25T12:08:40.615Z",
+ "duration": 61,
+ "external_pull_request": {
+ "created_at": "2020-02-25T12:08:40.478Z",
+ "id": 59023,
+ "project_id": 17121868,
+ "pull_request_iid": 4,
+ "source_branch": "new-branch",
+ "source_repository": "liptonshmidt/dotfiles",
+ "source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "status": "open",
+ "target_branch": "master",
+ "target_repository": "liptonshmidt/dotfiles",
+ "target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "updated_at": "2020-02-25T12:08:40.478Z"
+ },
+ "failure_reason": null,
+ "finished_at": "2020-02-25T12:09:44.464Z",
+ "id": 120842687,
+ "iid": 8,
+ "lock_version": 3,
+ "notes": [],
+ "project_id": 17121868,
+ "protected": false,
+ "ref": "new-branch",
+ "sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "source": "external_pull_request_event",
+ "source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "stages": [],
+ "started_at": "2020-02-25T12:08:42.511Z",
+ "status": "success",
+ "tag": false,
+ "target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "updated_at": "2020-02-25T12:09:44.473Z",
+ "user_id": 4087087,
+ "yaml_errors": null
+ },
+ {
+ "before_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "committed_at": null,
+ "config_source": "repository_source",
+ "created_at": "2020-02-25T12:08:37.434Z",
+ "duration": 57,
+ "external_pull_request": {
+ "created_at": "2020-02-25T12:08:40.478Z",
+ "id": 59023,
+ "project_id": 17121868,
+ "pull_request_iid": 4,
+ "source_branch": "new-branch",
+ "source_repository": "liptonshmidt/dotfiles",
+ "source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "status": "open",
+ "target_branch": "master",
+ "target_repository": "liptonshmidt/dotfiles",
+ "target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "updated_at": "2020-02-25T12:08:40.478Z"
+ },
+ "failure_reason": null,
+ "finished_at": "2020-02-25T12:09:36.557Z",
+ "id": 120842675,
+ "iid": 7,
+ "lock_version": 3,
+ "notes": [],
+ "project_id": 17121868,
+ "protected": false,
+ "ref": "new-branch",
+ "sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "source": "external_pull_request_event",
+ "source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "stages": [],
+ "started_at": "2020-02-25T12:08:38.682Z",
+ "status": "success",
+ "tag": false,
+ "target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "updated_at": "2020-02-25T12:09:36.565Z",
+ "user_id": 4087087,
+ "yaml_errors": null
+ }
+ ],
+ "custom_attributes": [],
+ "delete_error": null,
+ "description": "Vim, Tmux and others",
+ "disable_overriding_approvers_per_merge_request": null,
+ "external_authorization_classification_label": "",
+ "external_pull_requests": [
+ {
+ "created_at": "2020-02-25T12:08:40.478Z",
+ "id": 59023,
+ "project_id": 17121868,
+ "pull_request_iid": 4,
+ "source_branch": "new-branch",
+ "source_repository": "liptonshmidt/dotfiles",
+ "source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
+ "status": "open",
+ "target_branch": "master",
+ "target_repository": "liptonshmidt/dotfiles",
+ "target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
+ "updated_at": "2020-02-25T12:08:40.478Z"
+ }
+ ],
+ "external_webhook_token": "D3mVYFzZkgZ5kMfcW_wx",
+ "issues": [],
+ "labels": [],
+ "milestones": [],
+ "pipeline_schedules": [],
+ "project_feature": {
+ "builds_access_level": 20,
+ "created_at": "2020-02-25T11:20:09.925Z",
+ "forking_access_level": 20,
+ "id": 17494715,
+ "issues_access_level": 0,
+ "merge_requests_access_level": 0,
+ "pages_access_level": 20,
+ "project_id": 17121868,
+ "repository_access_level": 20,
+ "snippets_access_level": 0,
+ "updated_at": "2020-02-25T11:20:10.376Z",
+ "wiki_access_level": 0
+ },
+ "public_builds": true,
+ "releases": [],
+ "shared_runners_enabled": true,
+ "snippets": [],
+ "triggers": [],
+ "visibility_level": 20
+}
diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index 1497539a0c1..491fc20c40e 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -68,10 +68,10 @@ exports[`Repository last commit component renders commit widget 1`] = `
<gl-link-stub
class="js-commit-pipeline"
href="https://test.com/pipeline"
- title="Commit: failed"
+ title="Pipeline: failed"
>
<ci-icon-stub
- aria-label="Commit: failed"
+ aria-label="Pipeline: failed"
cssclasses=""
size="24"
status="[object Object]"
@@ -174,10 +174,10 @@ exports[`Repository last commit component renders the signature HTML as returned
<gl-link-stub
class="js-commit-pipeline"
href="https://test.com/pipeline"
- title="Commit: failed"
+ title="Pipeline: failed"
>
<ci-icon-stub
- aria-label="Commit: failed"
+ aria-label="Pipeline: failed"
cssclasses=""
size="24"
status="[object Object]"
diff --git a/spec/javascripts/search_spec.js b/spec/frontend/search_spec.js
index 32f60508fa3..af93fa88f72 100644
--- a/spec/javascripts/search_spec.js
+++ b/spec/frontend/search_spec.js
@@ -2,6 +2,8 @@ import $ from 'jquery';
import Api from '~/api';
import Search from '~/pages/search/show/search';
+jest.mock('~/api');
+
describe('Search', () => {
const fixturePath = 'search/show.html';
const searchTerm = 'some search';
@@ -19,20 +21,19 @@ describe('Search', () => {
new Search(); // eslint-disable-line no-new
});
- it('requests groups from backend when filtering', done => {
- spyOn(Api, 'groups').and.callFake(term => {
+ it('requests groups from backend when filtering', () => {
+ jest.spyOn(Api, 'groups').mockImplementation(term => {
expect(term).toBe(searchTerm);
- done();
});
+
const inputElement = fillDropdownInput('.js-search-group-dropdown');
$(inputElement).trigger('input');
});
- it('requests projects from backend when filtering', done => {
- spyOn(Api, 'projects').and.callFake(term => {
+ it('requests projects from backend when filtering', () => {
+ jest.spyOn(Api, 'projects').mockImplementation(term => {
expect(term).toBe(searchTerm);
- done();
});
const inputElement = fillDropdownInput('.js-search-project-dropdown');
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
index 81f194395ef..f364f374887 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -1,5 +1,7 @@
import Vue from 'vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { compileToFunctions } from 'vue-template-compiler';
+import { mount } from '@vue/test-utils';
+
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
import imageDiffViewer from '~/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue';
@@ -9,50 +11,52 @@ describe('ImageDiffViewer', () => {
newPath: GREEN_BOX_IMAGE_URL,
oldPath: RED_BOX_IMAGE_URL,
};
+ const allProps = {
+ ...requiredProps,
+ oldSize: 2048,
+ newSize: 1024,
+ };
+ let wrapper;
let vm;
function createComponent(props) {
const ImageDiffViewer = Vue.extend(imageDiffViewer);
- vm = mountComponent(ImageDiffViewer, props);
+ wrapper = mount(ImageDiffViewer, { propsData: props });
+ vm = wrapper.vm;
}
const triggerEvent = (eventName, el = vm.$el, clientX = 0) => {
- const event = document.createEvent('MouseEvents');
- event.initMouseEvent(
- eventName,
- true,
- true,
- window,
- 1,
+ const event = new MouseEvent(eventName, {
+ bubbles: true,
+ cancelable: true,
+ view: window,
+ detail: 1,
+ screenX: clientX,
clientX,
- 0,
- clientX,
- 0,
- false,
- false,
- false,
- false,
- 0,
- null,
- );
+ });
+
+ // JSDOM does not implement experimental APIs
+ event.pageX = clientX;
el.dispatchEvent(event);
};
- const dragSlider = (sliderElement, dragPixel = 20) => {
+ const dragSlider = (sliderElement, doc, dragPixel) => {
triggerEvent('mousedown', sliderElement);
- triggerEvent('mousemove', document.body, dragPixel);
- triggerEvent('mouseup', document.body);
+ triggerEvent('mousemove', doc.body, dragPixel);
+ triggerEvent('mouseup', doc.body);
};
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('renders image diff for replaced', done => {
- createComponent(requiredProps);
+ createComponent({ ...allProps });
+
+ vm.$nextTick(() => {
+ const metaInfoElements = vm.$el.querySelectorAll('.image-info');
- setTimeout(() => {
expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL);
@@ -66,35 +70,35 @@ describe('ImageDiffViewer', () => {
'Onion skin',
);
+ expect(metaInfoElements.length).toBe(2);
+ expect(metaInfoElements[0]).toHaveText('2.00 KiB');
+ expect(metaInfoElements[1]).toHaveText('1.00 KiB');
+
done();
});
});
it('renders image diff for new', done => {
- createComponent(
- Object.assign({}, requiredProps, {
- diffMode: 'new',
- oldPath: '',
- }),
- );
-
- setTimeout(() => {
+ createComponent({ ...allProps, diffMode: 'new', oldPath: '' });
+
+ setImmediate(() => {
+ const metaInfoElement = vm.$el.querySelector('.image-info');
+
expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
+ expect(metaInfoElement).toHaveText('1.00 KiB');
done();
});
});
it('renders image diff for deleted', done => {
- createComponent(
- Object.assign({}, requiredProps, {
- diffMode: 'deleted',
- newPath: '',
- }),
- );
-
- setTimeout(() => {
+ createComponent({ ...allProps, diffMode: 'deleted', newPath: '' });
+
+ setImmediate(() => {
+ const metaInfoElement = vm.$el.querySelector('.image-info');
+
expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL);
+ expect(metaInfoElement).toHaveText('2.00 KiB');
done();
});
@@ -105,26 +109,40 @@ describe('ImageDiffViewer', () => {
components: {
imageDiffViewer,
},
- template: `
- <image-diff-viewer diff-mode="renamed" new-path="${GREEN_BOX_IMAGE_URL}" old-path="">
+ data: {
+ ...allProps,
+ diffMode: 'renamed',
+ },
+ ...compileToFunctions(`
+ <image-diff-viewer
+ :diff-mode="diffMode"
+ :new-path="newPath"
+ :old-path="oldPath"
+ :new-size="newSize"
+ :old-size="oldSize"
+ >
<span slot="image-overlay" class="overlay">test</span>
</image-diff-viewer>
- `,
+ `),
}).$mount();
- setTimeout(() => {
+ setImmediate(() => {
+ const metaInfoElement = vm.$el.querySelector('.image-info');
+
expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
expect(vm.$el.querySelector('.overlay')).not.toBe(null);
+ expect(metaInfoElement).toHaveText('2.00 KiB');
+
done();
});
});
describe('swipeMode', () => {
beforeEach(done => {
- createComponent(requiredProps);
+ createComponent({ ...requiredProps });
- setTimeout(() => {
+ setImmediate(() => {
done();
});
});
@@ -141,9 +159,9 @@ describe('ImageDiffViewer', () => {
describe('onionSkin', () => {
beforeEach(done => {
- createComponent(requiredProps);
+ createComponent({ ...requiredProps });
- setTimeout(() => {
+ setImmediate(() => {
done();
});
});
@@ -163,7 +181,7 @@ describe('ImageDiffViewer', () => {
vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
vm.$nextTick(() => {
- dragSlider(vm.$el.querySelector('.dragger'));
+ dragSlider(vm.$el.querySelector('.dragger'), document, 20);
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dragger').style.left).toBe('20px');
diff --git a/spec/lib/gitlab/checks/snippet_check_spec.rb b/spec/lib/gitlab/checks/snippet_check_spec.rb
index 7cfcde7183f..43c69ab7042 100644
--- a/spec/lib/gitlab/checks/snippet_check_spec.rb
+++ b/spec/lib/gitlab/checks/snippet_check_spec.rb
@@ -25,10 +25,19 @@ describe Gitlab::Checks::SnippetCheck do
context 'trying to create the branch' do
let(:oldrev) { '0000000000000000000000000000000000000000' }
+ let(:ref) { 'refs/heads/feature' }
it 'raises an error' do
expect { subject.exec }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
end
+
+ context "when branch is 'master'" do
+ let(:ref) { 'refs/heads/master' }
+
+ it "allows the operation" do
+ expect { subject.exec }.not_to raise_error
+ end
+ end
end
end
end
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 312bbb58a28..a46c6579670 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -426,6 +426,10 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
expect(pipeline_with_external_pr.external_pull_request).to be_persisted
end
+
+ it 'has no import failures' do
+ expect(@project.import_failures.size).to eq 0
+ end
end
end
end
@@ -499,6 +503,30 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
end
end
+ context 'multiple pipelines reference the same external pull request' do
+ before do
+ setup_import_export_config('multi_pipeline_ref_one_external_pr')
+ expect(restored_project_json).to eq(true)
+ end
+
+ it_behaves_like 'restores project successfully',
+ issues: 0,
+ labels: 0,
+ milestones: 0,
+ ci_pipelines: 2,
+ external_pull_requests: 1,
+ import_failures: 0
+
+ it 'restores external pull request for the restored pipelines' do
+ external_pr = project.external_pull_requests.first
+
+ project.ci_pipelines.each do |pipeline_with_external_pr|
+ expect(pipeline_with_external_pr.external_pull_request).to be_persisted
+ expect(pipeline_with_external_pr.external_pull_request).to eq(external_pr)
+ end
+ end
+ end
+
context 'when post import action throw non-retriable exception' do
let(:exception) { StandardError.new('post_import_error') }
diff --git a/spec/lib/gitlab/sidekiq_queue_spec.rb b/spec/lib/gitlab/sidekiq_queue_spec.rb
new file mode 100644
index 00000000000..7a4d47563b6
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_queue_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqQueue do
+ around do |example|
+ Sidekiq::Queue.new('authorized_projects').clear
+ Sidekiq::Testing.disable!(&example)
+ Sidekiq::Queue.new('authorized_projects').clear
+ end
+
+ def add_job(user)
+ Sidekiq::Client.push(
+ 'class' => 'AuthorizedProjectsWorker',
+ 'queue' => 'authorized_projects',
+ 'args' => [user.id],
+ 'meta.user' => user.username
+ )
+ end
+
+ describe '#drop_jobs!' do
+ shared_examples 'queue processing' do
+ let(:sidekiq_queue) { described_class.new('authorized_projects') }
+ let_it_be(:sidekiq_queue_user) { create(:user) }
+
+ before do
+ add_job(create(:user))
+ add_job(sidekiq_queue_user)
+ add_job(sidekiq_queue_user)
+ end
+
+ context 'when the queue is not processed in time' do
+ before do
+ calls = 0
+
+ allow(sidekiq_queue).to receive(:job_matches?).and_wrap_original do |m, *args|
+ raise Timeout::Error if calls > 0
+
+ calls += 1
+ m.call(*args)
+ end
+ end
+
+ it 'returns a non-completion flag, the number of jobs deleted, and the remaining queue size' do
+ expect(sidekiq_queue.drop_jobs!(search_metadata, timeout: 10))
+ .to eq(completed: false,
+ deleted_jobs: timeout_deleted,
+ queue_size: 3 - timeout_deleted)
+ end
+ end
+
+ context 'when the queue is processed in time' do
+ it 'returns a completion flag, the number of jobs deleted, and the remaining queue size' do
+ expect(sidekiq_queue.drop_jobs!(search_metadata, timeout: 10))
+ .to eq(completed: true,
+ deleted_jobs: no_timeout_deleted,
+ queue_size: 3 - no_timeout_deleted)
+ end
+ end
+ end
+
+ context 'when there are no matching jobs' do
+ include_examples 'queue processing' do
+ let(:search_metadata) { { project: 1 } }
+ let(:timeout_deleted) { 0 }
+ let(:no_timeout_deleted) { 0 }
+ end
+ end
+
+ context 'when there are matching jobs' do
+ include_examples 'queue processing' do
+ let(:search_metadata) { { user: sidekiq_queue_user.username } }
+ let(:timeout_deleted) { 1 }
+ let(:no_timeout_deleted) { 2 }
+ end
+ end
+
+ context 'when there are no valid metadata keys passed' do
+ it 'raises NoMetadataError' do
+ add_job(create(:user))
+
+ expect { described_class.new('authorized_projects').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
+ .to raise_error(described_class::NoMetadataError)
+ end
+ end
+
+ context 'when the queue does not exist' do
+ it 'raises InvalidQueueError' do
+ expect { described_class.new('foo').drop_jobs!({ user: 'sidekiq_queue_user' }, timeout: 1) }
+ .to raise_error(described_class::InvalidQueueError)
+ end
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 934e26a74fe..82ece6b2b91 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -380,6 +380,12 @@ describe ApplicationSetting do
expect(subject).to be_invalid
end
+
+ it 'does not prevent from saving when gitaly timeouts were previously invalid' do
+ subject.update_column(:gitaly_timeout_default, Settings.gitlab.max_request_duration_seconds + 1)
+
+ expect(subject.reload).to be_valid
+ end
end
describe 'enforcing terms' do
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 8ed7abc968b..dd6690b4d1e 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -475,43 +475,59 @@ describe WikiPage do
end
end
- describe "#title" do
- it "replaces a hyphen to a space" do
- subject.title = "Import-existing-repositories-into-GitLab"
+ describe '#title_changed?' do
+ using RSpec::Parameterized::TableSyntax
- expect(subject.title).to eq("Import existing repositories into GitLab")
+ let(:untitled_page) { described_class.new(wiki) }
+ let(:directory_page) do
+ create_page('parent/child', 'test content')
+ wiki.find_page('parent/child')
end
- it 'unescapes html' do
- subject.title = 'foo &amp; bar'
+ where(:page, :title, :changed) do
+ :untitled_page | nil | false
+ :untitled_page | 'new title' | true
- expect(subject.title).to eq('foo & bar')
+ :new_page | nil | true
+ :new_page | 'test page' | true
+ :new_page | 'new title' | true
+
+ :existing_page | nil | false
+ :existing_page | 'test page' | false
+ :existing_page | '/test page' | false
+ :existing_page | 'new title' | true
+
+ :directory_page | nil | false
+ :directory_page | 'parent/child' | false
+ :directory_page | 'child' | false
+ :directory_page | '/child' | true
+ :directory_page | 'parent/other' | true
+ :directory_page | 'other/child' | true
+ end
+
+ with_them do
+ it 'returns the expected value' do
+ subject = public_send(page)
+ subject.title = title if title
+
+ expect(subject.title_changed?).to be(changed)
+ end
end
end
describe '#path' do
- let(:path) { 'mypath.md' }
- let(:git_page) { instance_double('Gitlab::Git::WikiPage', path: path).as_null_object }
-
it 'returns the path when persisted' do
- page = described_class.new(wiki, git_page, true)
-
- expect(page.path).to eq(path)
+ expect(existing_page.path).to eq('test-page.md')
end
it 'returns nil when not persisted' do
- page = described_class.new(wiki, git_page, false)
-
- expect(page.path).to be_nil
+ expect(new_page.path).to be_nil
end
end
describe '#directory' do
context 'when the page is at the root directory' do
- subject do
- create_page('file', 'content')
- wiki.find_page('file')
- end
+ subject { existing_page }
it 'returns an empty string' do
expect(subject.directory).to eq('')
diff --git a/spec/requests/api/admin/sidekiq_spec.rb b/spec/requests/api/admin/sidekiq_spec.rb
new file mode 100644
index 00000000000..0fb8199eec6
--- /dev/null
+++ b/spec/requests/api/admin/sidekiq_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::Admin::Sidekiq do
+ let_it_be(:admin) { create(:admin) }
+
+ describe 'DELETE /admin/sidekiq/queues/:queue_name' do
+ context 'when the user is not an admin' do
+ it 'returns a 403' do
+ delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}", create(:user))
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when the user is an admin' do
+ around do |example|
+ Sidekiq::Queue.new('authorized_projects').clear
+ Sidekiq::Testing.disable!(&example)
+ Sidekiq::Queue.new('authorized_projects').clear
+ end
+
+ def add_job(user)
+ Sidekiq::Client.push(
+ 'class' => 'AuthorizedProjectsWorker',
+ 'queue' => 'authorized_projects',
+ 'args' => [user.id],
+ 'meta.user' => user.username
+ )
+ end
+
+ context 'valid request' do
+ it 'returns info about the deleted jobs' do
+ add_job(admin)
+ add_job(admin)
+ add_job(create(:user))
+
+ delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}", admin)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq('completed' => true,
+ 'deleted_jobs' => 2,
+ 'queue_size' => 1)
+ end
+ end
+
+ context 'when no required params are provided' do
+ it 'returns a 400' do
+ delete api("/admin/sidekiq/queues/authorized_projects?user_2=#{admin.username}", admin)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context 'when the queue does not exist' do
+ it 'returns a 404' do
+ delete api("/admin/sidekiq/queues/authorized_projects_2?user=#{admin.username}", admin)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
new file mode 100644
index 00000000000..9451587fac3
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Deleting Sidekiq jobs' do
+ include GraphqlHelpers
+
+ let_it_be(:admin) { create(:admin) }
+
+ let(:variables) { { user: admin.username, queue_name: 'authorized_projects' } }
+ let(:mutation) { graphql_mutation(:admin_sidekiq_queues_delete_jobs, variables) }
+
+ def mutation_response
+ graphql_mutation_response(:admin_sidekiq_queues_delete_jobs)
+ end
+
+ context 'when the user is not an admin' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['You must be an admin to use this mutation']
+ end
+
+ context 'when the user is an admin' do
+ let(:current_user) { admin }
+
+ context 'valid request' do
+ around do |example|
+ Sidekiq::Queue.new('authorized_projects').clear
+ Sidekiq::Testing.disable!(&example)
+ Sidekiq::Queue.new('authorized_projects').clear
+ end
+
+ def add_job(user)
+ Sidekiq::Client.push(
+ 'class' => 'AuthorizedProjectsWorker',
+ 'queue' => 'authorized_projects',
+ 'args' => [user.id],
+ 'meta.user' => user.username
+ )
+ end
+
+ it 'returns info about the deleted jobs' do
+ add_job(admin)
+ add_job(admin)
+ add_job(create(:user))
+
+ post_graphql_mutation(mutation, current_user: admin)
+
+ expect(mutation_response['errors']).to be_empty
+ expect(mutation_response['result']).to eq('completed' => true,
+ 'deletedJobs' => 2,
+ 'queueSize' => 1)
+ end
+ end
+
+ context 'when no required params are provided' do
+ let(:variables) { { queue_name: 'authorized_projects' } }
+
+ it_behaves_like 'a mutation that returns errors in the response',
+ errors: ['No metadata provided']
+ end
+
+ context 'when the queue does not exist' do
+ let(:variables) { { user: admin.username, queue_name: 'authorized_projects_2' } }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['Queue authorized_projects_2 not found']
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb
index 14af98285fc..0e43f686327 100644
--- a/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/import_export/project_tree_restorer_shared_examples.rb
@@ -30,6 +30,14 @@ RSpec.shared_examples 'restores project successfully' do |**results|
expect(project.issues.size).to eq(results.fetch(:issues, 0))
end
+ it 'has ci pipelines' do
+ expect(project.ci_pipelines.size).to eq(results.fetch(:ci_pipelines, 0))
+ end
+
+ it 'has external pull requests' do
+ expect(project.external_pull_requests.size).to eq(results.fetch(:external_pull_requests, 0))
+ end
+
# This test is quarantined because the use of magic number 999 causes failure on CI
it 'does not set params that are excluded from import_export settings', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/207932#note_293724442' do
expect(project.import_type).to be_nil