summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb15
-rw-r--r--spec/controllers/projects/import/jira_controller_spec.rb5
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb2
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb19
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb86
-rw-r--r--spec/controllers/projects/wikis_controller_spec.rb14
-rw-r--r--spec/controllers/snippets_controller_spec.rb80
-rw-r--r--spec/factories/ci/pipelines.rb24
-rw-r--r--spec/factories/merge_requests.rb22
-rw-r--r--spec/fixtures/api/schemas/entities/discussion.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/notes.json3
-rw-r--r--spec/frontend/maintenance_mode_settings/components/app_spec.js42
-rw-r--r--spec/graphql/types/notes/note_type_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb234
-rw-r--r--spec/lib/gitlab/lets_encrypt/order_spec.rb19
-rw-r--r--spec/lib/gitlab/regex_spec.rb16
-rw-r--r--spec/migrations/fix_projects_without_prometheus_services_spec.rb42
-rw-r--r--spec/models/ci/runner_spec.rb29
-rw-r--r--spec/requests/api/runners_spec.rb94
-rw-r--r--spec/requests/api/todos_spec.rb72
-rw-r--r--spec/serializers/discussion_entity_spec.rb3
-rw-r--r--spec/services/jira_import/start_import_service_spec.rb81
-rw-r--r--spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb18
-rw-r--r--spec/support/helpers/lets_encrypt_helpers.rb10
-rw-r--r--spec/support/shared_examples/controllers/cache_control_shared_examples.rb37
-rw-r--r--spec/support/shared_examples/controllers/content_disposition_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/services/jira_import/start_import_service_shared_examples.rb9
-rw-r--r--spec/views/admin/application_settings/general.html.haml_spec.rb28
-rw-r--r--spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb7
-rw-r--r--spec/workers/pages_domain_ssl_renewal_worker_spec.rb2
30 files changed, 884 insertions, 155 deletions
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 1d844c847d6..54c2397625f 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
before do
controller.instance_variable_set(:@project, project)
@@ -34,15 +34,18 @@ describe Projects::AvatarsController do
expect(response).to have_gitlab_http_status(:ok)
expect(response.header['Content-Disposition']).to eq('inline')
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
+
+ it_behaves_like 'project cache control headers'
end
context 'when the avatar is stored in lfs' do
- it_behaves_like 'a controller that can serve LFS files' do
- let(:filename) { 'lfs_object.iso' }
- let(:filepath) { "files/lfs/#{filename}" }
- end
+ let(:filename) { 'lfs_object.iso' }
+ let(:filepath) { "files/lfs/#{filename}" }
+
+ it_behaves_like 'a controller that can serve LFS files'
+ it_behaves_like 'project cache control headers'
end
end
end
diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb
index 9d68104b755..3a29c3d4093 100644
--- a/spec/controllers/projects/import/jira_controller_spec.rb
+++ b/spec/controllers/projects/import/jira_controller_spec.rb
@@ -118,10 +118,9 @@ describe Projects::Import::JiraController do
end
it 'uses the existing import data' do
- expect(controller).not_to receive(:schedule_import)
-
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
+ expect(flash[:notice]).to eq('Jira import is already running.')
expect(response).to redirect_to(project_import_jira_path(project))
end
end
@@ -153,8 +152,6 @@ describe Projects::Import::JiraController do
end
it 'uses the existing import data' do
- expect(controller).to receive(:schedule_import).and_call_original
-
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
project.reload
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index add7778b57a..094b50322d1 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1377,7 +1377,7 @@ describe Projects::IssuesController do
it 'returns discussion json' do
get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }
- expect(json_response.first.keys).to match_array(%w[id reply_id expanded notes diff_discussion discussion_path individual_note resolvable resolved resolved_at resolved_by resolved_by_push commit_id for_commit project_id])
+ expect(json_response.first.keys).to match_array(%w[id reply_id expanded notes diff_discussion discussion_path individual_note resolvable resolved resolved_at resolved_by resolved_by_push commit_id for_commit project_id confidential])
end
it 'renders the author status html if there is a status' do
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 8cb48dca095..4a684dcfbc6 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -6,6 +6,7 @@ describe Projects::RawController do
include RepoHelpers
let(:project) { create(:project, :public, :repository) }
+ let(:inline) { nil }
describe 'GET #show' do
subject do
@@ -13,7 +14,8 @@ describe Projects::RawController do
params: {
namespace_id: project.namespace,
project_id: project,
- id: filepath
+ id: filepath,
+ inline: inline
})
end
@@ -25,10 +27,12 @@ describe Projects::RawController do
expect(response).to have_gitlab_http_status(:ok)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
+
+ it_behaves_like 'project cache control headers'
+ it_behaves_like 'content disposition headers'
end
context 'image header' do
@@ -38,15 +42,20 @@ describe Projects::RawController do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.header['Content-Disposition']).to eq('inline')
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
+
+ it_behaves_like 'project cache control headers'
+ it_behaves_like 'content disposition headers'
end
- it_behaves_like 'a controller that can serve LFS files' do
+ context 'with LFS files' do
let(:filename) { 'lfs_object.iso' }
let(:filepath) { "be93687/files/lfs/#{filename}" }
+
+ it_behaves_like 'a controller that can serve LFS files'
+ it_behaves_like 'project cache control headers'
end
context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 900569af6c6..9fa07cbc7a6 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -449,44 +449,76 @@ describe Projects::SnippetsController do
end
describe 'GET #raw' do
- let(:content) { "first line\r\nsecond line\r\nthird line" }
- let(:formatted_content) { content.gsub(/\r\n/, "\n") }
- let(:project_snippet) do
- create(
- :project_snippet, :public, :repository,
- project: project,
- author: user,
- content: content
- )
+ let(:inline) { nil }
+ let(:line_ending) { nil }
+ let(:params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: project_snippet.to_param,
+ inline: inline,
+ line_ending: line_ending
+ }
end
- let(:blob) { project_snippet.blobs.first }
- context 'CRLF line ending' do
- let(:params) do
- {
- namespace_id: project.namespace,
- project_id: project,
- id: project_snippet.to_param
- }
+ subject { get :raw, params: params }
+
+ context 'when repository is empty' do
+ let(:content) { "first line\r\nsecond line\r\nthird line" }
+ let(:formatted_content) { content.gsub(/\r\n/, "\n") }
+ let(:project_snippet) do
+ create(
+ :project_snippet, :public, :empty_repo,
+ project: project,
+ author: user,
+ content: content
+ )
end
- before do
- allow_next_instance_of(Blob) do |instance|
- allow(instance).to receive(:data).and_return(content)
+ context 'CRLF line ending' do
+ before do
+ allow_next_instance_of(Blob) do |instance|
+ allow(instance).to receive(:data).and_return(content)
+ end
end
- end
- it 'returns LF line endings by default' do
- get :raw, params: params
+ it 'returns LF line endings by default' do
+ subject
- expect(response.body).to eq(formatted_content)
+ expect(response.body).to eq(formatted_content)
+ end
+
+ context 'when line_ending parameter present' do
+ let(:line_ending) { :raw }
+
+ it 'does not convert line endings' do
+ subject
+
+ expect(response.body).to eq(content)
+ end
+ end
+ end
+ end
+
+ context 'when repository is not empty' do
+ let(:project_snippet) do
+ create(
+ :project_snippet, :public, :repository,
+ project: project,
+ author: user
+ )
end
- it 'does not convert line endings when parameter present' do
- get :raw, params: params.merge(line_ending: :raw)
+ it 'sends the blob' do
+ subject
- expect(response.body).to eq(content)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
+
+ it_behaves_like 'project cache control headers'
+ it_behaves_like 'content disposition headers'
end
end
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 6dd050af277..99d14298cd1 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -144,14 +144,12 @@ describe Projects::WikisController do
let(:id) { upload_file_to_wiki(project, user, file_name) }
- before do
- subject
- end
-
context 'when file is an image' do
let(:file_name) { 'dk.png' }
it 'delivers the image' do
+ subject
+
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
@@ -160,19 +158,27 @@ describe Projects::WikisController do
let(:file_name) { 'unsanitized.svg' }
it 'delivers the image' do
+ subject
+
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
+
+ it_behaves_like 'project cache control headers'
end
context 'when file is a pdf' do
let(:file_name) { 'git-cheat-sheet.pdf' }
it 'sets the content type to sets the content response headers' do
+ subject
+
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
+
+ it_behaves_like 'project cache control headers'
end
end
end
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index a675014a77b..3025521e189 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -501,6 +501,11 @@ describe SnippetsController do
end
describe "GET #raw" do
+ let(:inline) { nil }
+ let(:params) { { id: snippet.to_param, inline: inline } }
+
+ subject { get :raw, params: params }
+
shared_examples '200 status' do
before do
subject
@@ -511,11 +516,6 @@ describe SnippetsController do
expect(response).to have_gitlab_http_status(:ok)
end
- it 'has expected headers' do
- expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).to match(/inline/)
- end
-
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
@@ -551,12 +551,20 @@ describe SnippetsController do
shared_examples 'successful response' do
it_behaves_like '200 status'
- it_behaves_like 'CRLF line ending'
- it 'returns snippet first blob data' do
+ it 'has expected blob headers' do
subject
- expect(response.body).to eq snippet.blobs.first.data
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
+ end
+
+ it_behaves_like 'content disposition headers'
+
+ it 'sets cache_control public header based on snippet visibility' do
+ subject
+
+ expect(response.cache_control[:public]).to eq snippet.public?
end
context 'when feature flag version_snippets is disabled' do
@@ -571,12 +579,33 @@ describe SnippetsController do
subject
expect(response.body).to eq snippet.content
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
end
+
+ it_behaves_like 'content disposition headers'
+ end
+
+ context 'when snippet repository is empty' do
+ before do
+ allow_any_instance_of(Repository).to receive(:empty?).and_return(true)
+ end
+
+ it_behaves_like '200 status'
+ it_behaves_like 'CRLF line ending'
+
+ it 'returns snippet database content' do
+ subject
+
+ expect(response.body).to eq snippet.content
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+ end
+
+ it_behaves_like 'content disposition headers'
end
end
context 'when the personal snippet is private' do
- let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository, author: user) }
+ let_it_be(:snippet) { create(:personal_snippet, :private, :repository, author: user) }
context 'when signed in' do
before do
@@ -595,18 +624,13 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
- it_behaves_like 'successful response' do
- let(:snippet) { personal_snippet }
- let(:params) { { id: snippet.to_param } }
-
- subject { get :raw, params: params }
- end
+ it_behaves_like 'successful response'
end
end
context 'when not signed in' do
it 'redirects to the sign in page' do
- get :raw, params: { id: personal_snippet.to_param }
+ subject
expect(response).to redirect_to(new_user_session_path)
end
@@ -614,24 +638,19 @@ describe SnippetsController do
end
context 'when the personal snippet is internal' do
- let_it_be(:personal_snippet) { create(:personal_snippet, :internal, :repository, author: user) }
+ let_it_be(:snippet) { create(:personal_snippet, :internal, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
- it_behaves_like 'successful response' do
- let(:snippet) { personal_snippet }
- let(:params) { { id: snippet.to_param } }
-
- subject { get :raw, params: params }
- end
+ it_behaves_like 'successful response'
end
context 'when not signed in' do
it 'redirects to the sign in page' do
- get :raw, params: { id: personal_snippet.to_param }
+ subject
expect(response).to redirect_to(new_user_session_path)
end
@@ -639,26 +658,21 @@ describe SnippetsController do
end
context 'when the personal snippet is public' do
- let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository, author: user) }
+ let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
- it_behaves_like 'successful response' do
- let(:snippet) { personal_snippet }
- let(:params) { { id: snippet.to_param } }
-
- subject { get :raw, params: params }
- end
+ it_behaves_like 'successful response'
end
context 'when not signed in' do
it 'responds with status 200' do
- get :raw, params: { id: personal_snippet.to_param }
+ subject
- expect(assigns(:snippet)).to eq(personal_snippet)
+ expect(assigns(:snippet)).to eq(snippet)
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 11686ed5277..f2c342f76d0 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -99,6 +99,30 @@ FactoryBot.define do
trait :repository_source do
config_source { Ci::Pipeline.config_sources[:repository_source] }
end
+
+ trait :detached_merge_request_pipeline do
+ merge_request
+
+ source { :merge_request_event }
+ project { merge_request.source_project }
+ sha { merge_request.source_branch_sha }
+ ref { merge_request.ref_path }
+ end
+
+ trait :legacy_detached_merge_request_pipeline do
+ detached_merge_request_pipeline
+
+ ref { merge_request.source_branch }
+ end
+
+ trait :merged_result_pipeline do
+ detached_merge_request_pipeline
+
+ sha { 'test-merge-sha'}
+ ref { merge_request.merge_ref_path }
+ source_sha { merge_request.source_branch_sha }
+ target_sha { merge_request.target_branch_sha }
+ end
end
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index f717bab5f2a..08a8ede61b1 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -147,23 +147,13 @@ FactoryBot.define do
trait :with_legacy_detached_merge_request_pipeline do
after(:create) do |merge_request|
- merge_request.pipelines_for_merge_request << create(:ci_pipeline,
- source: :merge_request_event,
- merge_request: merge_request,
- project: merge_request.source_project,
- ref: merge_request.source_branch,
- sha: merge_request.source_branch_sha)
+ create(:ci_pipeline, :legacy_detached_merge_request_pipeline, merge_request: merge_request)
end
end
trait :with_detached_merge_request_pipeline do
after(:create) do |merge_request|
- merge_request.pipelines_for_merge_request << create(:ci_pipeline,
- source: :merge_request_event,
- merge_request: merge_request,
- project: merge_request.source_project,
- ref: merge_request.ref_path,
- sha: merge_request.source_branch_sha)
+ create(:ci_pipeline, :detached_merge_request_pipeline, merge_request: merge_request)
end
end
@@ -175,14 +165,12 @@ FactoryBot.define do
end
after(:create) do |merge_request, evaluator|
- merge_request.pipelines_for_merge_request << create(:ci_pipeline,
- source: :merge_request_event,
+ create(:ci_pipeline, :merged_result_pipeline,
merge_request: merge_request,
- project: merge_request.source_project,
- ref: merge_request.merge_ref_path,
sha: evaluator.merge_sha,
source_sha: evaluator.source_sha,
- target_sha: evaluator.target_sha)
+ target_sha: evaluator.target_sha
+ )
end
end
diff --git a/spec/fixtures/api/schemas/entities/discussion.json b/spec/fixtures/api/schemas/entities/discussion.json
index 16622ef6887..92863d2e084 100644
--- a/spec/fixtures/api/schemas/entities/discussion.json
+++ b/spec/fixtures/api/schemas/entities/discussion.json
@@ -55,7 +55,8 @@
"human_access": { "type": ["string", "null"] },
"toggle_award_path": { "type": "string" },
"path": { "type": "string" },
- "commands_changes": { "type": "object", "additionalProperties": true }
+ "commands_changes": { "type": "object", "additionalProperties": true },
+ "confidential": { "type": ["boolean", "null"] }
},
"required": [
"id", "attachment", "author", "created_at", "updated_at",
diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json
index 9668327adc4..d15d2e90b05 100644
--- a/spec/fixtures/api/schemas/public_api/v4/notes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/notes.json
@@ -28,7 +28,8 @@
"noteable_type": { "type": "string" },
"resolved": { "type": "boolean" },
"resolvable": { "type": "boolean" },
- "resolved_by": { "type": ["string", "null"] }
+ "resolved_by": { "type": ["string", "null"] },
+ "confidential": { "type": ["boolean", "null"] }
},
"required": [
"id", "body", "attachment", "author", "created_at", "updated_at",
diff --git a/spec/frontend/maintenance_mode_settings/components/app_spec.js b/spec/frontend/maintenance_mode_settings/components/app_spec.js
new file mode 100644
index 00000000000..a67bc63cfe1
--- /dev/null
+++ b/spec/frontend/maintenance_mode_settings/components/app_spec.js
@@ -0,0 +1,42 @@
+import { shallowMount } from '@vue/test-utils';
+import MaintenanceModeSettingsApp from '~/maintenance_mode_settings/components/app.vue';
+import { GlToggle, GlFormTextarea, GlButton } from '@gitlab/ui';
+
+describe('MaintenanceModeSettingsApp', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(MaintenanceModeSettingsApp);
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findMaintenanceModeSettingsContainer = () => wrapper.find('article');
+ const findGlToggle = () => wrapper.find(GlToggle);
+ const findGlFormTextarea = () => wrapper.find(GlFormTextarea);
+ const findGlButton = () => wrapper.find(GlButton);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the Maintenance Mode Settings container', () => {
+ expect(findMaintenanceModeSettingsContainer().exists()).toBe(true);
+ });
+
+ it('renders the GlToggle', () => {
+ expect(findGlToggle().exists()).toBe(true);
+ });
+
+ it('renders the GlFormTextarea', () => {
+ expect(findGlFormTextarea().exists()).toBe(true);
+ });
+
+ it('renders the GlButton', () => {
+ expect(findGlButton().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/graphql/types/notes/note_type_spec.rb b/spec/graphql/types/notes/note_type_spec.rb
index 809f54c120c..8cf84cd8dfd 100644
--- a/spec/graphql/types/notes/note_type_spec.rb
+++ b/spec/graphql/types/notes/note_type_spec.rb
@@ -5,7 +5,7 @@ describe GitlabSchema.types['Note'] do
it 'exposes the expected fields' do
expected_fields = [:id, :project, :author, :body, :created_at,
:updated_at, :discussion, :resolvable, :position, :user_permissions,
- :resolved_by, :resolved_at, :system, :body_html]
+ :resolved_by, :resolved_at, :system, :body_html, :confidential]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb b/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb
new file mode 100644
index 00000000000..3c3e37df200
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb
@@ -0,0 +1,234 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService, :migration, schema: 2020_02_20_115023 do
+ def service_params_for(project_id, params = {})
+ {
+ project_id: project_id,
+ active: false,
+ properties: '{}',
+ type: 'PrometheusService',
+ template: false,
+ push_events: true,
+ issues_events: true,
+ merge_requests_events: true,
+ tag_push_events: true,
+ note_events: true,
+ category: 'monitoring',
+ default: false,
+ wiki_page_events: true,
+ pipeline_events: true,
+ confidential_issues_events: true,
+ commit_events: true,
+ job_events: true,
+ confidential_note_events: true,
+ deployment_events: false
+ }.merge(params)
+ end
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:services) { table(:services) }
+ let(:clusters) { table(:clusters) }
+ let(:cluster_groups) { table(:cluster_groups) }
+ let(:clusters_applications_prometheus) { table(:clusters_applications_prometheus) }
+ let(:namespace) { namespaces.create(name: 'user', path: 'user') }
+ let(:project) { projects.create(namespace_id: namespace.id) }
+
+ let(:application_statuses) do
+ {
+ errored: -1,
+ installed: 3,
+ updated: 5
+ }
+ end
+
+ let(:cluster_types) do
+ {
+ instance_type: 1,
+ group_type: 2,
+ project_type: 3
+ }
+ end
+
+ let(:columns) do
+ %w(project_id active properties type template push_events
+ issues_events merge_requests_events tag_push_events
+ note_events category default wiki_page_events pipeline_events
+ confidential_issues_events commit_events job_events
+ confidential_note_events deployment_events)
+ end
+
+ describe '#perform' do
+ shared_examples 'fix services entries state' do
+ it 'is idempotent' do
+ expect { subject.perform(project.id, project.id + 1) }.to change { services.order(:id).map { |row| row.attributes } }
+
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+
+ context 'non prometheus services' do
+ it 'does not change them' do
+ other_type = 'SomeOtherService'
+ services.create(service_params_for(project.id, active: true, type: other_type))
+
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.where(type: other_type).order(:id).map { |row| row.attributes } }
+ end
+ end
+
+ context 'prometheus integration services do not exist' do
+ it 'creates missing services entries', :aggregate_failures do
+ expect { subject.perform(project.id, project.id + 1) }.to change { services.count }.by(1)
+ expect([service_params_for(project.id, active: true)]).to eq services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
+ end
+
+ context 'template is present for prometheus services' do
+ it 'creates missing services entries', :aggregate_failures do
+ services.create(service_params_for(nil, template: true, properties: { 'from_template' => true }.to_json))
+
+ expect { subject.perform(project.id, project.id + 1) }.to change { services.count }.by(1)
+ updated_rows = services.where(template: false).order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
+ expect([service_params_for(project.id, active: true, properties: { 'from_template' => true }.to_json)]).to eq updated_rows
+ end
+ end
+ end
+
+ context 'prometheus integration services exist' do
+ context 'in active state' do
+ it 'does not change them' do
+ services.create(service_params_for(project.id, active: true))
+
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+
+ context 'not in active state' do
+ it 'sets active attribute to true' do
+ service = services.create(service_params_for(project.id, active: false))
+
+ expect { subject.perform(project.id, project.id + 1) }.to change { service.reload.active? }.from(false).to(true)
+ end
+
+ context 'prometheus services are configured manually ' do
+ it 'does not change them' do
+ properties = '{"api_url":"http://test.dev","manual_configuration":"1"}'
+ services.create(service_params_for(project.id, properties: properties, active: false))
+
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+ end
+ end
+ end
+
+ context 'k8s cluster shared on instance level' do
+ let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:instance_type]) }
+
+ context 'with installed prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ end
+
+ it_behaves_like 'fix services entries state'
+ end
+
+ context 'with updated prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
+ end
+
+ it_behaves_like 'fix services entries state'
+ end
+
+ context 'with errored prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
+ end
+
+ it 'does not change services entries' do
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+ end
+
+ context 'k8s cluster shared on group level' do
+ let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:group_type]) }
+
+ before do
+ cluster_groups.create(cluster_id: cluster.id, group_id: project.namespace_id)
+ end
+
+ context 'with installed prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ end
+
+ it_behaves_like 'fix services entries state'
+
+ context 'second k8s cluster without application available' do
+ let(:namespace_2) { namespaces.create(name: 'namespace2', path: 'namespace2') }
+ let(:project_2) { projects.create(namespace_id: namespace_2.id) }
+
+ before do
+ cluster_2 = clusters.create(name: 'cluster2', cluster_type: cluster_types[:group_type])
+ cluster_groups.create(cluster_id: cluster_2.id, group_id: project_2.namespace_id)
+ end
+
+ it 'changed only affected services entries' do
+ expect { subject.perform(project.id, project_2.id + 1) }.to change { services.count }.by(1)
+ expect([service_params_for(project.id, active: true)]).to eq services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
+ end
+ end
+ end
+
+ context 'with updated prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
+ end
+
+ it_behaves_like 'fix services entries state'
+ end
+
+ context 'with errored prometheus application' do
+ before do
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
+ end
+
+ it 'does not change services entries' do
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+
+ context 'with missing prometheus application' do
+ it 'does not change services entries' do
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+
+ context 'with inactive service' do
+ it 'does not change services entries' do
+ services.create(service_params_for(project.id))
+
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+ end
+ end
+
+ context 'k8s cluster for single project' do
+ let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:project_type]) }
+ let(:cluster_projects) { table(:cluster_projects) }
+
+ context 'with installed prometheus application' do
+ before do
+ cluster_projects.create(cluster_id: cluster.id, project_id: project.id)
+ clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ end
+
+ it 'does not change services entries' do
+ expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/lets_encrypt/order_spec.rb b/spec/lib/gitlab/lets_encrypt/order_spec.rb
index 1a759103c44..333fce8e36a 100644
--- a/spec/lib/gitlab/lets_encrypt/order_spec.rb
+++ b/spec/lib/gitlab/lets_encrypt/order_spec.rb
@@ -38,4 +38,23 @@ describe ::Gitlab::LetsEncrypt::Order do
order.request_certificate(domain: 'example.com', private_key: private_key)
end
end
+
+ describe '#challenge_error' do
+ it 'returns error if challenge has errors' do
+ challenge = acme_challenge_double
+
+ # error just to give an example
+ error = {
+ "type" => "urn:ietf:params:acme:error:dns",
+ "detail" => "No valid IP addresses found for test.example.com",
+ "status" => 400
+ }
+
+ allow(challenge).to receive(:error).and_return(error)
+
+ acme_order = acme_order_double(authorizations: [acme_authorization_double(challenge)])
+
+ expect(described_class.new(acme_order).challenge_error).to eq(error)
+ end
+ end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index f1b5393a2d8..5a2cf2eda8b 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -13,10 +13,6 @@ describe Gitlab::Regex do
it { is_expected.not_to match('?gitlab') }
end
- shared_examples_for 'project/group name error message' do
- it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.") }
- end
-
describe '.project_name_regex' do
subject { described_class.project_name_regex }
@@ -27,18 +23,26 @@ describe Gitlab::Regex do
subject { described_class.group_name_regex }
it_behaves_like 'project/group name regex'
+
+ it 'allows parenthesis' do
+ is_expected.to match('Group One (Test)')
+ end
+
+ it 'does not start with parenthesis' do
+ is_expected.not_to match('(Invalid Group name)')
+ end
end
describe '.project_name_regex_message' do
subject { described_class.project_name_regex_message }
- it_behaves_like 'project/group name error message'
+ it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.") }
end
describe '.group_name_regex_message' do
subject { described_class.group_name_regex_message }
- it_behaves_like 'project/group name error message'
+ it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', dash, space, parenthesis. It must start with letter, digit, emoji or '_'.") }
end
describe '.environment_name_regex' do
diff --git a/spec/migrations/fix_projects_without_prometheus_services_spec.rb b/spec/migrations/fix_projects_without_prometheus_services_spec.rb
new file mode 100644
index 00000000000..ecfad313f58
--- /dev/null
+++ b/spec/migrations/fix_projects_without_prometheus_services_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+#
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200220115023_fix_projects_without_prometheus_service.rb')
+
+describe FixProjectsWithoutPrometheusService, :migration do
+ let(:namespace) { table(:namespaces).create(name: 'gitlab', path: 'gitlab-org') }
+
+ let!(:projects) do
+ [
+ table(:projects).create(namespace_id: namespace.id, name: 'foo 1'),
+ table(:projects).create(namespace_id: namespace.id, name: 'foo 2'),
+ table(:projects).create(namespace_id: namespace.id, name: 'foo 3')
+ ]
+ end
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 2)
+ end
+
+ around do |example|
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ example.call
+ end
+ end
+ end
+
+ it 'schedules jobs for ranges of projects' do
+ migrate!
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(2.minutes, projects[0].id, projects[1].id)
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(4.minutes, projects[2].id, projects[2].id)
+ end
+
+ it 'schedules jobs according to the configured batch size' do
+ expect { migrate! }.to change { BackgroundMigrationWorker.jobs.size }.by(2)
+ end
+end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 0192c8ed17d..55af292e8f3 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -838,4 +838,33 @@ describe Ci::Runner do
it { is_expected.to eq(contacted_at_stored) }
end
+
+ describe '.belonging_to_group' do
+ it 'returns the specific group runner' do
+ group = create(:group)
+ runner = create(:ci_runner, :group, groups: [group])
+ unrelated_group = create(:group)
+ create(:ci_runner, :group, groups: [unrelated_group])
+
+ expect(described_class.belonging_to_group(group.id)).to contain_exactly(runner)
+ end
+
+ context 'runner belonging to parent group' do
+ let_it_be(:parent_group) { create(:group) }
+ let_it_be(:parent_runner) { create(:ci_runner, :group, groups: [parent_group]) }
+ let_it_be(:group) { create(:group, parent: parent_group) }
+
+ context 'when include_parent option is passed' do
+ it 'returns the group runner from the parent group' do
+ expect(described_class.belonging_to_group(group.id, include_ancestors: true)).to contain_exactly(parent_runner)
+ end
+ end
+
+ context 'when include_parent option is not passed' do
+ it 'does not return the group runner from the parent group' do
+ expect(described_class.belonging_to_group(group.id)).to be_empty
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 70094ef4388..f6b257e6288 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -12,7 +12,6 @@ describe API::Runners do
let(:project2) { create(:project, creator_id: user.id) }
let(:group) { create(:group).tap { |group| group.add_owner(user) } }
- let(:group2) { create(:group).tap { |group| group.add_owner(user) } }
let!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') }
let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
@@ -734,6 +733,24 @@ describe API::Runners do
end
end
+ shared_examples_for 'unauthorized access to runners list' do
+ context 'authorized user without maintainer privileges' do
+ it "does not return group's runners" do
+ get api("/#{entity_type}/#{entity.id}/runners", user2)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'unauthorized user' do
+ it "does not return project's runners" do
+ get api("/#{entity_type}/#{entity.id}/runners")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
+
describe 'GET /projects/:id/runners' do
context 'authorized user with maintainer privileges' do
it 'returns response status and headers' do
@@ -813,20 +830,77 @@ describe API::Runners do
end
end
- context 'authorized user without maintainer privileges' do
- it "does not return project's runners" do
- get api("/projects/#{project.id}/runners", user2)
+ it_behaves_like 'unauthorized access to runners list' do
+ let(:entity_type) { 'projects' }
+ let(:entity) { project }
+ end
+ end
- expect(response).to have_gitlab_http_status(:forbidden)
+ describe 'GET /groups/:id/runners' do
+ context 'authorized user with maintainer privileges' do
+ it 'returns all runners' do
+ get api("/groups/#{group.id}/runners", user)
+
+ expect(json_response).to match_array([
+ a_hash_including('description' => 'Group runner')
+ ])
end
- end
- context 'unauthorized user' do
- it "does not return project's runners" do
- get api("/projects/#{project.id}/runners")
+ context 'filter by type' do
+ it 'returns record when valid and present' do
+ get api("/groups/#{group.id}/runners?type=group_type", user)
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(json_response).to match_array([
+ a_hash_including('description' => 'Group runner')
+ ])
+ end
+
+ it 'returns empty result when type does not match' do
+ get api("/groups/#{group.id}/runners?type=project_type", user)
+
+ expect(json_response).to be_empty
+ end
+
+ it 'does not filter by invalid type' do
+ get api("/groups/#{group.id}/runners?type=bogus", user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
+
+ context 'filter runners by status' do
+ it 'returns runners by valid status' do
+ create(:ci_runner, :group, :inactive, description: 'Inactive group runner', groups: [group])
+
+ get api("/groups/#{group.id}/runners?status=paused", user)
+
+ expect(json_response).to match_array([
+ a_hash_including('description' => 'Inactive group runner')
+ ])
+ end
+
+ it 'does not filter by invalid status' do
+ get api("/groups/#{group.id}/runners?status=bogus", user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ it 'filters runners by tag_list' do
+ create(:ci_runner, :group, description: 'Runner tagged with tag1 and tag2', groups: [group], tag_list: %w[tag1 tag2])
+ create(:ci_runner, :group, description: 'Runner tagged with tag2', groups: [group], tag_list: %w[tag1])
+
+ get api("/groups/#{group.id}/runners?tag_list=tag1,tag2", user)
+
+ expect(json_response).to match_array([
+ a_hash_including('description' => 'Runner tagged with tag1 and tag2')
+ ])
+ end
+ end
+
+ it_behaves_like 'unauthorized access to runners list' do
+ let(:entity_type) { 'groups' }
+ let(:entity) { group }
end
end
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index 7a7712c2f5d..1aa5e21dddb 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -3,23 +3,23 @@
require 'spec_helper'
describe API::Todos do
- let(:group) { create(:group) }
- let(:project_1) { create(:project, :repository, group: group) }
- let(:project_2) { create(:project) }
- let(:author_1) { create(:user) }
- let(:author_2) { create(:user) }
- let(:john_doe) { create(:user, username: 'john_doe') }
- let(:merge_request) { create(:merge_request, source_project: project_1) }
- let!(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
- let!(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) }
- let!(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) }
- let!(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) }
- let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
- let!(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') }
- let!(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') }
- let!(:award_emoji_3) { create(:award_emoji, awardable: pending_2.target, user: author_2, name: 'thumbsdown') }
-
- before do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project_1) { create(:project, :repository, group: group) }
+ let_it_be(:project_2) { create(:project) }
+ let_it_be(:author_1) { create(:user) }
+ let_it_be(:author_2) { create(:user) }
+ let_it_be(:john_doe) { create(:user, username: 'john_doe') }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project_1) }
+ let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
+ let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) }
+ let_it_be(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) }
+ let_it_be(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) }
+ let_it_be(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
+ let_it_be(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') }
+ let_it_be(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') }
+ let_it_be(:award_emoji_3) { create(:award_emoji, awardable: pending_2.target, user: author_2, name: 'thumbsdown') }
+
+ before_all do
project_1.add_developer(john_doe)
project_2.add_developer(john_doe)
end
@@ -29,7 +29,7 @@ describe API::Todos do
it 'returns authentication error' do
get api('/todos')
- expect(response.status).to eq(401)
+ expect(response).to have_gitlab_http_status(:unauthorized)
end
end
@@ -37,7 +37,7 @@ describe API::Todos do
it 'returns an array of pending todos for current user' do
get api('/todos', john_doe)
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(4)
@@ -74,7 +74,7 @@ describe API::Todos do
it 'filters based on author_id param' do
get api('/todos', john_doe), params: { author_id: author_2.id }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
@@ -87,7 +87,7 @@ describe API::Todos do
get api('/todos', john_doe), params: { type: 'MergeRequest' }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
@@ -98,7 +98,7 @@ describe API::Todos do
it 'filters based on state param' do
get api('/todos', john_doe), params: { state: 'done' }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -109,7 +109,7 @@ describe API::Todos do
it 'filters based on project_id param' do
get api('/todos', john_doe), params: { project_id: project_2.id }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -120,7 +120,7 @@ describe API::Todos do
it 'filters based on project_id param' do
get api('/todos', john_doe), params: { group_id: group.id, sort: :target_id }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(3)
@@ -131,7 +131,7 @@ describe API::Todos do
it 'filters based on action param' do
get api('/todos', john_doe), params: { action: 'mentioned' }
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
@@ -157,7 +157,7 @@ describe API::Todos do
create(:on_commit_todo, project: project_3, author: author_1, user: john_doe)
expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control)
- expect(response.status).to eq(200)
+ expect(response).to have_gitlab_http_status(:ok)
end
end
@@ -189,7 +189,7 @@ describe API::Todos do
it 'returns 404 if the todo does not belong to the current user' do
post api("/todos/#{pending_1.id}/mark_as_done", author_1)
- expect(response.status).to eq(404)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
end
@@ -225,7 +225,7 @@ describe API::Todos do
it 'creates a todo on an issuable' do
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
- expect(response.status).to eq(201)
+ expect(response).to have_gitlab_http_status(:created)
expect(json_response['project']).to be_a Hash
expect(json_response['author']).to be_a Hash
expect(json_response['target_type']).to eq(issuable.class.name)
@@ -242,13 +242,15 @@ describe API::Todos do
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
- expect(response.status).to eq(304)
+ expect(response).to have_gitlab_http_status(:not_modified)
end
it 'returns 404 if the issuable is not found' do
- post api("/projects/#{project_1.id}/#{issuable_type}/123/todo", john_doe)
+ unknown_id = 0
+
+ post api("/projects/#{project_1.id}/#{issuable_type}/#{unknown_id}/todo", john_doe)
- expect(response.status).to eq(404)
+ expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns an error if the issuable is not accessible' do
@@ -268,13 +270,17 @@ describe API::Todos do
describe 'POST :id/issuable_type/:issueable_id/todo' do
context 'for an issue' do
it_behaves_like 'an issuable', 'issues' do
- let(:issuable) { create(:issue, :confidential, author: author_1, project: project_1) }
+ let_it_be(:issuable) do
+ create(:issue, :confidential, author: author_1, project: project_1)
+ end
end
end
context 'for a merge request' do
it_behaves_like 'an issuable', 'merge_requests' do
- let(:issuable) { create(:merge_request, :simple, source_project: project_1) }
+ let_it_be(:issuable) do
+ create(:merge_request, :simple, source_project: project_1)
+ end
end
end
end
diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb
index b194623099d..4adf1dc5994 100644
--- a/spec/serializers/discussion_entity_spec.rb
+++ b/spec/serializers/discussion_entity_spec.rb
@@ -34,7 +34,8 @@ describe DiscussionEntity do
:discussion_path,
:resolved_at,
:for_commit,
- :commit_id
+ :commit_id,
+ :confidential
)
end
diff --git a/spec/services/jira_import/start_import_service_spec.rb b/spec/services/jira_import/start_import_service_spec.rb
new file mode 100644
index 00000000000..038c53b2b22
--- /dev/null
+++ b/spec/services/jira_import/start_import_service_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe JiraImport::StartImportService do
+ let_it_be(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ subject { described_class.new(user, project, '').execute }
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(jira_issue_import: false)
+ end
+
+ it_behaves_like 'responds with error', 'Jira import feature is disabled.'
+ end
+
+ context 'when feature flag enabled' do
+ before do
+ stub_feature_flags(jira_issue_import: true)
+ end
+
+ context 'when user does not have permissions to run the import' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'responds with error', 'You do not have permissions to run the import.'
+ end
+
+ context 'when user has permission to run import' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when Jira service was not setup' do
+ it_behaves_like 'responds with error', 'Jira integration not configured.'
+ end
+
+ context 'when Jira service exists' do
+ let!(:jira_service) { create(:jira_service, project: project, active: true) }
+
+ context 'when Jira project key is not provided' do
+ it_behaves_like 'responds with error', 'Unable to find Jira project to import data from.'
+ end
+
+ context 'when correct data provided' do
+ subject { described_class.new(user, project, 'some-key').execute }
+
+ context 'when import is already running' do
+ let!(:import_state) { create(:import_state, project: project, status: :started) }
+
+ it_behaves_like 'responds with error', 'Jira import is already running.'
+ end
+
+ it 'returns success response' do
+ expect(subject).to be_a(ServiceResponse)
+ expect(subject).to be_success
+ end
+
+ it 'schedules jira import' do
+ subject
+
+ expect(project.import_state.status).to eq('scheduled')
+ end
+
+ it 'creates jira import data' do
+ subject
+
+ jira_import_data = project.import_data.becomes(JiraImportData)
+ expect(jira_import_data.force_import?).to be true
+ imported_project_data = jira_import_data.projects.last
+ expect(imported_project_data.key).to eq('some-key')
+ expect(imported_project_data.scheduled_by['user_id']).to eq(user.id)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
index 9832ba95524..163276db7e6 100644
--- a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
+++ b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
@@ -163,4 +163,22 @@ describe PagesDomains::ObtainLetsEncryptCertificateService do
expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
end
end
+
+ context 'when order is invalid' do
+ let(:existing_order) do
+ create(:pages_domain_acme_order, pages_domain: pages_domain)
+ end
+
+ let!(:api_order) do
+ stub_lets_encrypt_order(existing_order.url, 'invalid')
+ end
+
+ it 'saves error to domain and deletes acme order' do
+ expect do
+ service.execute
+ end.to change { pages_domain.reload.auto_ssl_failed }.from(false).to(true)
+
+ expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
+ end
+ end
end
diff --git a/spec/support/helpers/lets_encrypt_helpers.rb b/spec/support/helpers/lets_encrypt_helpers.rb
index 2857416ad95..0f378893895 100644
--- a/spec/support/helpers/lets_encrypt_helpers.rb
+++ b/spec/support/helpers/lets_encrypt_helpers.rb
@@ -11,7 +11,8 @@ module LetsEncryptHelpers
status: 'pending',
token: 'tokenvalue',
file_content: 'hereisfilecontent',
- request_validation: true
+ request_validation: true,
+ error: nil
}.freeze
def stub_lets_encrypt_settings
@@ -43,16 +44,17 @@ module LetsEncryptHelpers
challenge
end
- def acme_authorization_double
+ def acme_authorization_double(challenge = acme_challenge_double)
authorization = instance_double('Acme::Client::Resources::Authorization')
- allow(authorization).to receive(:http).and_return(acme_challenge_double)
+ allow(authorization).to receive(:http).and_return(challenge)
+ allow(authorization).to receive(:challenges).and_return([challenge])
authorization
end
def acme_order_double(attributes = {})
acme_order = instance_double('Acme::Client::Resources::Order')
allow(acme_order).to receive_messages(ACME_ORDER_METHODS.merge(attributes))
- allow(acme_order).to receive(:authorizations).and_return([acme_authorization_double])
+ allow(acme_order).to receive(:authorizations).and_return([acme_authorization_double]) unless attributes[:authorizations]
allow(acme_order).to receive(:finalize)
acme_order
end
diff --git a/spec/support/shared_examples/controllers/cache_control_shared_examples.rb b/spec/support/shared_examples/controllers/cache_control_shared_examples.rb
new file mode 100644
index 00000000000..426d7f95222
--- /dev/null
+++ b/spec/support/shared_examples/controllers/cache_control_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'project cache control headers' do
+ before do
+ project.update(visibility_level: visibility_level)
+ end
+
+ context 'when project is public' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
+
+ it 'returns cache_control public header to true' do
+ subject
+
+ expect(response.cache_control[:public]).to be_truthy
+ end
+ end
+
+ context 'when project is private' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
+
+ it 'returns cache_control public header to true' do
+ subject
+
+ expect(response.cache_control[:public]).to be_falsey
+ end
+ end
+
+ context 'when project is internal' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
+
+ it 'returns cache_control public header to true' do
+ subject
+
+ expect(response.cache_control[:public]).to be_falsey
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/content_disposition_shared_examples.rb b/spec/support/shared_examples/controllers/content_disposition_shared_examples.rb
new file mode 100644
index 00000000000..a1ece5fcc7f
--- /dev/null
+++ b/spec/support/shared_examples/controllers/content_disposition_shared_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'content disposition headers' do
+ it 'sets content disposition to inline' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.header['Content-Disposition']).to match(/inline/)
+ end
+
+ context 'when inline param is false' do
+ let(:inline) { 'false' }
+
+ it 'sets content disposition to attachment' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.header['Content-Disposition']).to match(/attachment/)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/jira_import/start_import_service_shared_examples.rb b/spec/support/shared_examples/services/jira_import/start_import_service_shared_examples.rb
new file mode 100644
index 00000000000..c5e56ed3539
--- /dev/null
+++ b/spec/support/shared_examples/services/jira_import/start_import_service_shared_examples.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+shared_examples 'responds with error' do |message|
+ it 'returns error' do
+ expect(subject).to be_a(ServiceResponse)
+ expect(subject).to be_error
+ expect(subject.message).to eq(message)
+ end
+end
diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb
index e6a0307afd9..d8ca5dd1b49 100644
--- a/spec/views/admin/application_settings/general.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/general.html.haml_spec.rb
@@ -33,4 +33,32 @@ describe 'admin/application_settings/general.html.haml' do
end
end
end
+
+ describe 'Maintenance mode' do
+ let(:maintenance_mode_flag) { true }
+
+ before do
+ assign(:application_setting, app_settings)
+ stub_feature_flags(maintenance_mode: maintenance_mode_flag)
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ context 'when maintenance_mode feature is enabled' do
+ it 'show the Maintenance mode section' do
+ render
+
+ expect(rendered).to have_css('#js-maintenance-mode-toggle')
+ end
+ end
+
+ context 'when maintenance_mode feature is disabled' do
+ let(:maintenance_mode_flag) { false }
+
+ it 'hide the Maintenance mode section' do
+ render
+
+ expect(rendered).not_to have_css('#js-maintenance-mode-toggle')
+ end
+ end
+ end
end
diff --git a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
index 736acc40371..1349a80029b 100644
--- a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
+++ b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
@@ -21,6 +21,10 @@ describe PagesDomainSslRenewalCronWorker do
let!(:domain_without_auto_certificate) do
create(:pages_domain, :without_certificate, :without_key, project: project, auto_ssl_enabled: true)
end
+ let!(:domain_with_failed_auto_ssl) do
+ create(:pages_domain, :without_certificate, :without_key, project: project,
+ auto_ssl_enabled: true, auto_ssl_failed: true)
+ end
let!(:domain_with_expired_auto_ssl) do
create(:pages_domain, :letsencrypt, :with_expired_certificate, project: project)
@@ -34,7 +38,8 @@ describe PagesDomainSslRenewalCronWorker do
end
[domain,
- domain_with_obtained_letsencrypt].each do |domain|
+ domain_with_obtained_letsencrypt,
+ domain_with_failed_auto_ssl].each do |domain|
expect(PagesDomainSslRenewalWorker).not_to receive(:perform_async).with(domain.id)
end
diff --git a/spec/workers/pages_domain_ssl_renewal_worker_spec.rb b/spec/workers/pages_domain_ssl_renewal_worker_spec.rb
index 3552ff0823a..a35965f49b2 100644
--- a/spec/workers/pages_domain_ssl_renewal_worker_spec.rb
+++ b/spec/workers/pages_domain_ssl_renewal_worker_spec.rb
@@ -26,6 +26,8 @@ describe PagesDomainSslRenewalWorker do
shared_examples 'does nothing' do
it 'does nothing' do
expect(::PagesDomains::ObtainLetsEncryptCertificateService).not_to receive(:new)
+
+ worker.perform(domain.id)
end
end