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-27 21:09:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-27 21:09:21 +0300
commite0fa0638a422c3e20d4423c9bb69d79afc9c7d3d (patch)
tree9abb3c0706576bbda895fe9539a55556930606e2 /spec
parentf8d15ca65390475e356b06dedc51e10ccd179f86 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/ci/ref.rb16
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb34
-rw-r--r--spec/frontend/diffs/components/compare_versions_dropdown_spec.js (renamed from spec/javascripts/diffs/components/compare_versions_dropdown_spec.js)19
-rw-r--r--spec/helpers/notifications_helper_spec.rb1
-rw-r--r--spec/lib/gitlab/checks/snippet_check_spec.rb4
-rw-r--r--spec/lib/gitlab/git_access_snippet_spec.rb22
-rw-r--r--spec/lib/gitlab/graphql/docs/renderer_spec.rb96
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb11
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb34
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb125
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb52
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb38
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies_spec.rb15
-rw-r--r--spec/lib/gitlab/sidekiq_middleware_spec.rb6
-rw-r--r--spec/mailers/emails/pipelines_spec.rb13
-rw-r--r--spec/models/ci/ref_spec.rb11
-rw-r--r--spec/models/notification_recipient_spec.rb42
-rw-r--r--spec/models/notification_setting_spec.rb3
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/services/ci/update_ci_ref_status_service_spec.rb161
-rw-r--r--spec/services/notification_service_spec.rb81
-rw-r--r--spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/views/pipeline_status_changes_email.rb78
-rw-r--r--spec/views/notify/pipeline_failed_email.html.haml_spec.rb70
-rw-r--r--spec/views/notify/pipeline_fixed_email.html.haml_spec.rb10
-rw-r--r--spec/views/notify/pipeline_fixed_email.text.erb_spec.rb10
-rw-r--r--spec/views/notify/pipeline_success_email.html.haml_spec.rb54
-rw-r--r--spec/views/notify/pipeline_success_email.text.erb_spec.rb22
-rw-r--r--spec/workers/pipeline_notification_worker_spec.rb9
-rw-r--r--spec/workers/pipeline_update_ci_ref_status_worker_service_spec.rb18
31 files changed, 890 insertions, 175 deletions
diff --git a/spec/factories/ci/ref.rb b/spec/factories/ci/ref.rb
new file mode 100644
index 00000000000..891d8848a72
--- /dev/null
+++ b/spec/factories/ci/ref.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_ref, class: 'Ci::Ref' do
+ ref { 'master' }
+ status { :success }
+ tag { false }
+ project
+
+ before(:create) do |ref, evaluator|
+ next if ref.pipelines.exists?
+
+ ref.update!(last_updated_by_pipeline: create(:ci_pipeline, project: evaluator.project, ref: evaluator.ref, tag: evaluator.tag, status: evaluator.status))
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 1c72c54f0a1..561c0552007 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -356,7 +356,7 @@ describe 'Pipeline', :js do
end
end
- context 'test tabs' do
+ describe 'test tabs' do
let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
before do
@@ -364,21 +364,31 @@ describe 'Pipeline', :js do
wait_for_requests
end
- it 'shows badge counter in Tests tab' do
- expect(pipeline.test_reports.total_count).to eq(4)
- expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_reports.total_count.to_s)
- end
+ context 'with test reports' do
+ it 'shows badge counter in Tests tab' do
+ expect(pipeline.test_reports.total_count).to eq(4)
+ expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_reports.total_count.to_s)
+ end
+
+ it 'does not call test_report.json endpoint by default', :js do
+ expect(page).to have_selector('.js-no-tests-to-show', visible: :all)
+ end
- it 'does not call test_report.json endpoint by default', :js do
- expect(page).to have_selector('.js-no-tests-to-show', visible: :all)
+ it 'does call test_report.json endpoint when tab is selected', :js do
+ find('.js-tests-tab-link').click
+ wait_for_requests
+
+ expect(page).to have_content('Test suites')
+ expect(page).to have_selector('.js-tests-detail', visible: :all)
+ end
end
- it 'does call test_report.json endpoint when tab is selected', :js do
- find('.js-tests-tab-link').click
- wait_for_requests
+ context 'without test reports' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
- expect(page).to have_content('Test suites')
- expect(page).to have_selector('.js-tests-detail', visible: :all)
+ it 'shows nothing' do
+ expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("")
+ end
end
end
diff --git a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js b/spec/frontend/diffs/components/compare_versions_dropdown_spec.js
index e0686901483..5033bdd9044 100644
--- a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
+++ b/spec/frontend/diffs/components/compare_versions_dropdown_spec.js
@@ -2,6 +2,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import CompareVersionsDropdown from '~/diffs/components/compare_versions_dropdown.vue';
import diffsMockData from '../mock_data/merge_request_diffs';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import { TEST_HOST } from 'helpers/test_constants';
const localVue = createLocalVue();
const targetBranch = { branchName: 'tmp-wine-dev', versionIndex: -1 };
@@ -109,6 +110,24 @@ describe('CompareVersionsDropdown', () => {
expect(findLastLink().attributes('href')).toEqual(baseVersionPath);
expect(findLastLink().text()).toContain('(base)');
+ expect(findLastLink().text()).not.toContain('(HEAD)');
+ });
+
+ it('should render a correct head version link', () => {
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value: { href: `${TEST_HOST}?diff_head=true` },
+ });
+
+ createComponent({
+ baseVersionPath,
+ otherVersions: diffsMockData.slice(1),
+ targetBranch,
+ });
+
+ expect(findLastLink().attributes('href')).toEqual(baseVersionPath);
+ expect(findLastLink().text()).not.toContain('(base)');
+ expect(findLastLink().text()).toContain('(HEAD)');
});
it('should not render commits count if no showCommitsCount is passed', () => {
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index 2384c87b377..d8dcce203fe 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -21,6 +21,7 @@ describe NotificationsHelper do
describe '#notification_event_name' do
it { expect(notification_event_name(:success_pipeline)).to match('Successful pipeline') }
it { expect(notification_event_name(:failed_pipeline)).to match('Failed pipeline') }
+ it { expect(notification_event_name(:fixed_pipeline)).to match('Fixed pipeline') }
end
describe '#notification_icon_level' do
diff --git a/spec/lib/gitlab/checks/snippet_check_spec.rb b/spec/lib/gitlab/checks/snippet_check_spec.rb
index 7cb29debd1e..7cfcde7183f 100644
--- a/spec/lib/gitlab/checks/snippet_check_spec.rb
+++ b/spec/lib/gitlab/checks/snippet_check_spec.rb
@@ -19,7 +19,7 @@ describe Gitlab::Checks::SnippetCheck do
let(:newrev) { '0000000000000000000000000000000000000000' }
it 'raises an error' do
- expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can not create or delete branches.')
+ expect { subject.exec }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
end
end
@@ -27,7 +27,7 @@ describe Gitlab::Checks::SnippetCheck do
let(:oldrev) { '0000000000000000000000000000000000000000' }
it 'raises an error' do
- expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can not create or delete branches.')
+ expect { subject.exec }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
end
end
end
diff --git a/spec/lib/gitlab/git_access_snippet_spec.rb b/spec/lib/gitlab/git_access_snippet_spec.rb
index de19db38176..a68ac8ee8fe 100644
--- a/spec/lib/gitlab/git_access_snippet_spec.rb
+++ b/spec/lib/gitlab/git_access_snippet_spec.rb
@@ -26,7 +26,7 @@ describe Gitlab::GitAccessSnippet do
let(:actor) { build(:deploy_key) }
it 'does not allow push and pull access' do
- expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:authentication_mechanism])
+ expect { pull_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:authentication_mechanism])
end
end
@@ -76,8 +76,8 @@ describe Gitlab::GitAccessSnippet do
it 'blocks access when the user did not accept terms' do
message = /must accept the Terms of Service in order to perform this action/
- expect { push_access_check }.to raise_unauthorized(message)
- expect { pull_access_check }.to raise_unauthorized(message)
+ expect { push_access_check }.to raise_forbidden(message)
+ expect { pull_access_check }.to raise_forbidden(message)
end
it 'allows access when the user accepted the terms' do
@@ -101,13 +101,13 @@ describe Gitlab::GitAccessSnippet do
if Ability.allowed?(user, :update_snippet, snippet)
expect { push_access_check }.not_to raise_error
else
- expect { push_access_check }.to raise_error(described_class::UnauthorizedError)
+ expect { push_access_check }.to raise_error(described_class::ForbiddenError)
end
if Ability.allowed?(user, :read_snippet, snippet)
expect { pull_access_check }.not_to raise_error
else
- expect { pull_access_check }.to raise_error(described_class::UnauthorizedError)
+ expect { pull_access_check }.to raise_error(described_class::ForbiddenError)
end
end
end
@@ -154,7 +154,7 @@ describe Gitlab::GitAccessSnippet do
with_them do
it "respects accessibility" do
- error_class = described_class::UnauthorizedError
+ error_class = described_class::ForbiddenError
if Ability.allowed?(user, :update_snippet, snippet)
expect { push_access_check }.not_to raise_error
@@ -180,7 +180,7 @@ describe Gitlab::GitAccessSnippet do
allow(::Gitlab::Database).to receive(:read_only?).and_return(true)
allow(::Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
- expect { push_access_check }.to raise_unauthorized(/You can't push code to a read-only GitLab instance/)
+ expect { push_access_check }.to raise_forbidden(/You can't push code to a read-only GitLab instance/)
end
end
@@ -198,10 +198,10 @@ describe Gitlab::GitAccessSnippet do
it 'raises error if SnippetCheck raises error' do
expect_next_instance_of(Gitlab::Checks::SnippetCheck) do |check|
- allow(check).to receive(:exec).and_raise(Gitlab::GitAccess::UnauthorizedError, 'foo')
+ allow(check).to receive(:exec).and_raise(Gitlab::GitAccess::ForbiddenError, 'foo')
end
- expect { push_access_check }.to raise_unauthorized('foo')
+ expect { push_access_check }.to raise_forbidden('foo')
end
end
@@ -215,7 +215,7 @@ describe Gitlab::GitAccessSnippet do
raise_error(Gitlab::GitAccess::NotFoundError, Gitlab::GitAccess::ERROR_MESSAGES[:project_not_found])
end
- def raise_unauthorized(message)
- raise_error(Gitlab::GitAccess::UnauthorizedError, message)
+ def raise_forbidden(message)
+ raise_error(Gitlab::GitAccess::ForbiddenError, message)
end
end
diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
new file mode 100644
index 00000000000..5ba70bb8f0a
--- /dev/null
+++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Docs::Renderer do
+ describe '#contents' do
+ # Returns a Schema that uses the given `type`
+ def mock_schema(type)
+ query_type = Class.new(GraphQL::Schema::Object) do
+ graphql_name 'QueryType'
+
+ field :foo, type, null: true
+ end
+
+ GraphQL::Schema.define(query: query_type)
+ end
+
+ let_it_be(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/', 'default.md.haml') }
+
+ subject(:contents) do
+ described_class.new(
+ mock_schema(type).graphql_definition,
+ output_dir: nil,
+ template: template
+ ).contents
+ end
+
+ context 'A type with a field with a [Array] return type' do
+ let(:type) do
+ Class.new(GraphQL::Schema::Object) do
+ graphql_name 'ArrayTest'
+
+ field :foo, [GraphQL::STRING_TYPE], null: false, description: 'A description'
+ end
+ end
+
+ specify do
+ expectation = <<~DOC
+ ## ArrayTest
+
+ | Name | Type | Description |
+ | --- | ---- | ---------- |
+ | `foo` | String! => Array | A description |
+ DOC
+
+ is_expected.to include(expectation)
+ end
+ end
+
+ context 'A type with fields defined in reverse alphabetical order' do
+ let(:type) do
+ Class.new(GraphQL::Schema::Object) do
+ graphql_name 'OrderingTest'
+
+ field :foo, GraphQL::STRING_TYPE, null: false, description: 'A description of foo field'
+ field :bar, GraphQL::STRING_TYPE, null: false, description: 'A description of bar field'
+ end
+ end
+
+ specify do
+ expectation = <<~DOC
+ ## OrderingTest
+
+ | Name | Type | Description |
+ | --- | ---- | ---------- |
+ | `bar` | String! | A description of bar field |
+ | `foo` | String! | A description of foo field |
+ DOC
+
+ is_expected.to include(expectation)
+ end
+ end
+
+ context 'A type with a deprecated field' do
+ let(:type) do
+ Class.new(GraphQL::Schema::Object) do
+ graphql_name 'DeprecatedTest'
+
+ field :foo, GraphQL::STRING_TYPE, null: false, deprecation_reason: 'This is deprecated', description: 'A description'
+ end
+ end
+
+ specify do
+ expectation = <<~DOC
+ ## DeprecatedTest
+
+ | Name | Type | Description |
+ | --- | ---- | ---------- |
+ | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated |
+ DOC
+
+ is_expected.to include(expectation)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index db45b9c42fd..d97d76cf35e 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -192,6 +192,7 @@ ci_pipelines:
- environments
- chat_data
- source_pipeline
+- ref_status
- source_bridge
- source_job
- sourced_pipelines
@@ -359,6 +360,7 @@ project:
- ci_pipelines
- all_pipelines
- stages
+- ci_refs
- builds
- runner_projects
- runners
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index f5a6ea4d5b0..2dd45d18ee9 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -89,6 +89,17 @@ describe Gitlab::IncomingEmail do
it 'does not match emails with extra bits' do
expect(described_class.key_from_address('somereplies+somekey@example.com.someotherdomain.com')).to be nil
end
+
+ context 'when a custom wildcard address is used' do
+ let(:wildcard_address) { 'custom.address+%{key}@example.com' }
+
+ it 'finds key if email matches address pattern' do
+ key = described_class.key_from_address(
+ 'custom.address+foo@example.com', wildcard_address: wildcard_address
+ )
+ expect(key).to eq('foo')
+ end
+ end
end
context 'self.key_from_fallback_message_id' do
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb
new file mode 100644
index 00000000000..b6e47afc7e8
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::DuplicateJobs::Client, :clean_gitlab_redis_queues do
+ let(:worker_class) do
+ Class.new do
+ def self.name
+ 'TestDeduplicationWorker'
+ end
+
+ include ApplicationWorker
+
+ def perform(*args)
+ end
+ end
+ end
+
+ before do
+ stub_const('TestDeduplicationWorker', worker_class)
+ end
+
+ describe '#call' do
+ it 'adds a correct duplicate tag to the jobs', :aggregate_failures do
+ TestDeduplicationWorker.bulk_perform_async([['args1'], ['args2'], ['args1']])
+
+ job1, job2, job3 = TestDeduplicationWorker.jobs
+
+ expect(job1['duplicate-of']).to be_nil
+ expect(job2['duplicate-of']).to be_nil
+ expect(job3['duplicate-of']).to eq(job1['jid'])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
new file mode 100644
index 00000000000..2334439461e
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_redis_queues do
+ subject(:duplicate_job) do
+ described_class.new(job, queue)
+ end
+
+ let(:job) { { 'class' => 'AuthorizedProjectsWorker', 'args' => [1], 'jid' => '123' } }
+ let(:queue) { 'authorized_projects' }
+
+ let(:idempotency_key) do
+ hash = Digest::SHA256.hexdigest("#{job['class']}:#{job['args'].join('-')}")
+ "#{Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE}:duplicate:#{queue}:#{hash}"
+ end
+
+ describe '#schedule' do
+ it 'calls schedule on the strategy' do
+ expect do |block|
+ expect_next_instance_of(Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting) do |strategy|
+ expect(strategy).to receive(:schedule).with(job, &block)
+ end
+
+ duplicate_job.schedule(&block)
+ end.to yield_control
+ end
+ end
+
+ describe '#perform' do
+ it 'calls perform on the strategy' do
+ expect do |block|
+ expect_next_instance_of(Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting) do |strategy|
+ expect(strategy).to receive(:perform).with(job, &block)
+ end
+
+ duplicate_job.perform(&block)
+ end.to yield_control
+ end
+ end
+
+ describe '#check!' do
+ context 'when there was no job in the queue yet' do
+ it { expect(duplicate_job.check!).to eq('123') }
+
+ it "adds a key with ttl set to #{described_class::DUPLICATE_KEY_TTL}" do
+ expect { duplicate_job.check! }
+ .to change { read_idempotency_key_with_ttl(idempotency_key) }
+ .from([nil, -2])
+ .to(['123', be_within(1).of(described_class::DUPLICATE_KEY_TTL)])
+ end
+ end
+
+ context 'when there was already a job with same arguments in the same queue' do
+ before do
+ set_idempotency_key(idempotency_key, 'existing-key')
+ end
+
+ it { expect(duplicate_job.check!).to eq('existing-key') }
+
+ it "does not change the existing key's TTL" do
+ expect { duplicate_job.check! }
+ .not_to change { read_idempotency_key_with_ttl(idempotency_key) }
+ .from(['existing-key', -1])
+ end
+
+ it 'sets the existing jid' do
+ duplicate_job.check!
+
+ expect(duplicate_job.existing_jid).to eq('existing-key')
+ end
+ end
+ end
+
+ describe '#delete!' do
+ context "when we didn't track the definition" do
+ it { expect { duplicate_job.delete! }.not_to raise_error }
+ end
+
+ context 'when the key exists in redis' do
+ before do
+ set_idempotency_key(idempotency_key, 'existing-key')
+ end
+
+ it 'removes the key from redis' do
+ expect { duplicate_job.delete! }
+ .to change { read_idempotency_key_with_ttl(idempotency_key) }
+ .from(['existing-key', -1])
+ .to([nil, -2])
+ end
+ end
+ end
+
+ describe '#duplicate?' do
+ it "raises an error if the check wasn't performed" do
+ expect { duplicate_job.duplicate? }.to raise_error /Call `#check!` first/
+ end
+
+ it 'returns false if the existing jid equals the job jid' do
+ duplicate_job.check!
+
+ expect(duplicate_job.duplicate?).to be(false)
+ end
+
+ it 'returns false if the existing jid is different from the job jid' do
+ set_idempotency_key(idempotency_key, 'a different jid')
+ duplicate_job.check!
+
+ expect(duplicate_job.duplicate?).to be(true)
+ end
+ end
+
+ def set_idempotency_key(key, value = '1')
+ Sidekiq.redis { |r| r.set(key, value) }
+ end
+
+ def read_idempotency_key_with_ttl(key)
+ Sidekiq.redis do |redis|
+ redis.pipelined do |p|
+ p.get(key)
+ p.ttl(key)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
new file mode 100644
index 00000000000..0ea248fbcf1
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_redis_queues do
+ let(:worker_class) do
+ Class.new do
+ def self.name
+ 'TestDeduplicationWorker'
+ end
+
+ include ApplicationWorker
+
+ def perform(*args)
+ end
+ end
+ end
+
+ before do
+ stub_const('TestDeduplicationWorker', worker_class)
+ end
+
+ around do |example|
+ Sidekiq::Testing.inline! { example.run }
+ end
+
+ before(:context) do
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.add described_class
+ end
+ end
+
+ after(:context) do
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.remove described_class
+ end
+ end
+
+ describe '#call' do
+ it 'removes the stored job from redis' do
+ bare_job = { 'class' => 'TestDeduplicationWorker', 'args' => ['hello'] }
+ job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'test_deduplication')
+
+ expect(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
+ .to receive(:new).with(a_hash_including(bare_job), 'test_deduplication')
+ .and_return(job_definition).twice # once in client middleware
+ expect(job_definition).to receive(:delete!).and_call_original
+
+ TestDeduplicationWorker.perform_async('hello')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb
new file mode 100644
index 00000000000..f40e829f9a5
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting do
+ let(:fake_duplicate_job) do
+ instance_double(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
+ end
+
+ subject(:strategy) { described_class.new(fake_duplicate_job) }
+
+ describe '#schedule' do
+ it 'checks for duplicates before yielding' do
+ expect(fake_duplicate_job).to receive(:check!).ordered.and_return('a jid')
+ expect(fake_duplicate_job).to receive(:duplicate?).ordered.and_return(false)
+ expect { |b| strategy.schedule({}, &b) }.to yield_control
+ end
+
+ it 'adds the jid of the existing job to the job hash' do
+ allow(fake_duplicate_job).to receive(:check!).and_return('the jid')
+ job_hash = {}
+
+ expect(fake_duplicate_job).to receive(:duplicate?).and_return(true)
+ expect(fake_duplicate_job).to receive(:existing_jid).and_return('the jid')
+
+ strategy.schedule(job_hash) {}
+
+ expect(job_hash).to include('duplicate-of' => 'the jid')
+ end
+ end
+
+ describe '#perform' do
+ it 'deletes the lock before executing' do
+ expect(fake_duplicate_job).to receive(:delete!).ordered
+ expect { |b| strategy.perform({}, &b) }.to yield_control
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies_spec.rb
new file mode 100644
index 00000000000..6ecc2a3a5f8
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies do
+ describe '.for' do
+ it 'returns the right class for `until_executing`' do
+ expect(described_class.for(:until_executing)).to eq(described_class::UntilExecuting)
+ end
+
+ it 'raises an UnknownStrategyError when passing an unknown key' do
+ expect { described_class.for(:unknown) }.to raise_error(described_class::UnknownStrategyError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
index 19242d25e27..2f325fd5052 100644
--- a/spec/lib/gitlab/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -46,7 +46,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqMiddleware::MemoryKiller,
Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
Gitlab::SidekiqMiddleware::WorkerContext::Server,
- Gitlab::SidekiqMiddleware::AdminMode::Server
+ Gitlab::SidekiqMiddleware::AdminMode::Server,
+ Gitlab::SidekiqMiddleware::DuplicateJobs::Server
]
end
let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
@@ -117,7 +118,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqMiddleware::ClientMetrics,
Gitlab::SidekiqMiddleware::WorkerContext::Client,
Labkit::Middleware::Sidekiq::Client,
- Gitlab::SidekiqMiddleware::AdminMode::Client
+ Gitlab::SidekiqMiddleware::AdminMode::Client,
+ Gitlab::SidekiqMiddleware::DuplicateJobs::Client
]
end
diff --git a/spec/mailers/emails/pipelines_spec.rb b/spec/mailers/emails/pipelines_spec.rb
index 9996bd9a6c4..cc901da98dc 100644
--- a/spec/mailers/emails/pipelines_spec.rb
+++ b/spec/mailers/emails/pipelines_spec.rb
@@ -106,4 +106,17 @@ describe Emails::Pipelines do
let(:status_text) { 'Your pipeline has failed.' }
end
end
+
+ describe '#pipeline_fixed_email' do
+ subject { Notify.pipeline_fixed_email(pipeline, pipeline.user.try(:email)) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: ref, sha: sha) }
+ let(:ref) { 'master' }
+ let(:sha) { project.commit(ref).sha }
+
+ it_behaves_like 'correct pipeline information' do
+ let(:status) { 'been fixed' }
+ let(:status_text) { 'Your pipeline has been fixed!' }
+ end
+ end
end
diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb
new file mode 100644
index 00000000000..aa3b8cdbc3e
--- /dev/null
+++ b/spec/models/ci/ref_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::Ref do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:last_updated_by_pipeline) }
+
+ it { is_expected.to validate_inclusion_of(:status).in_array(%w[success failed fixed]) }
+ it { is_expected.to validate_presence_of(:last_updated_by_pipeline) }
+end
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index f6a36dbb3fc..05aeafaa4d4 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -176,8 +176,20 @@ describe NotificationRecipient do
)
end
- before do
- notification_setting.update!(failed_pipeline: true)
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context "when action is fixed_pipeline" do
+ let(:recipient) do
+ described_class.new(
+ user,
+ :watch,
+ custom_action: :fixed_pipeline,
+ target: target,
+ project: project
+ )
end
it 'returns true' do
@@ -185,7 +197,7 @@ describe NotificationRecipient do
end
end
- context "when action is not failed_pipeline" do
+ context "when action is not fixed_pipeline or failed_pipeline" do
let(:recipient) do
described_class.new(
user,
@@ -196,10 +208,6 @@ describe NotificationRecipient do
)
end
- before do
- notification_setting.update!(success_pipeline: true)
- end
-
it 'returns false' do
expect(recipient.suitable_notification_level?).to eq false
end
@@ -309,6 +317,26 @@ describe NotificationRecipient do
expect(recipient.suitable_notification_level?).to eq false
end
end
+
+ context 'when custom_action is fixed_pipeline and success_pipeline event is enabled' do
+ let(:recipient) do
+ described_class.new(
+ user,
+ :watch,
+ custom_action: :fixed_pipeline,
+ target: target,
+ project: project
+ )
+ end
+
+ before do
+ notification_setting.update!(success_pipeline: true)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
end
end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 094c60e3e09..9ab9ae494ec 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -110,7 +110,8 @@ RSpec.describe NotificationSetting do
:reassign_merge_request,
:merge_merge_request,
:failed_pipeline,
- :success_pipeline
+ :success_pipeline,
+ :fixed_pipeline
)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 9b1c724f0c2..e7deae38b46 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -72,6 +72,7 @@ describe Project do
it { is_expected.to have_one(:project_setting) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:ci_pipelines) }
+ it { is_expected.to have_many(:ci_refs) }
it { is_expected.to have_many(:builds) }
it { is_expected.to have_many(:build_trace_section_names)}
it { is_expected.to have_many(:runner_projects) }
diff --git a/spec/services/ci/update_ci_ref_status_service_spec.rb b/spec/services/ci/update_ci_ref_status_service_spec.rb
new file mode 100644
index 00000000000..2b069452a55
--- /dev/null
+++ b/spec/services/ci/update_ci_ref_status_service_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::UpdateCiRefStatusService do
+ describe '#call' do
+ subject { described_class.new(pipeline) }
+
+ shared_examples 'creates ci_ref' do
+ it 'creates a ci_ref with the pipeline attributes' do
+ expect do
+ expect(subject.call).to eq(true)
+ end.to change { Ci::Ref.count }.by(1)
+
+ created_ref = pipeline.reload.ref_status
+ %w[ref tag project status].each do |attr|
+ expect(created_ref[attr]).to eq(pipeline[attr])
+ end
+ end
+
+ it 'calls PipelineNotificationWorker pasing the ref_status' do
+ expect(PipelineNotificationWorker).to receive(:perform_async).with(pipeline.id, ref_status: pipeline.status)
+
+ subject.call
+ end
+ end
+
+ shared_examples 'updates ci_ref' do
+ where(:ref_status, :pipeline_status, :next_status) do
+ [
+ %w[failed success fixed],
+ %w[failed failed failed],
+ %w[success success success],
+ %w[success failed failed]
+ ]
+ end
+
+ with_them do
+ let(:ci_ref) { create(:ci_ref, status: ref_status) }
+ let(:pipeline) { create(:ci_pipeline, status: pipeline_status, project: ci_ref.project, ref: ci_ref.ref) }
+
+ it 'sets ci_ref.status to next_status' do
+ expect do
+ expect(subject.call).to eq(true)
+ expect(ci_ref.reload.status).to eq(next_status)
+ end.not_to change { Ci::Ref.count }
+ end
+
+ it 'calls PipelineNotificationWorker pasing the ref_status' do
+ expect(PipelineNotificationWorker).to receive(:perform_async).with(pipeline.id, ref_status: next_status)
+
+ subject.call
+ end
+ end
+ end
+
+ shared_examples 'does a noop' do
+ it "doesn't change ci_ref" do
+ expect do
+ expect do
+ expect(subject.call).to eq(false)
+ end.not_to change { ci_ref.reload.status }
+ end.not_to change { Ci::Ref.count }
+ end
+
+ it "doesn't call PipelineNotificationWorker" do
+ expect(PipelineNotificationWorker).not_to receive(:perform_async)
+
+ subject.call
+ end
+ end
+
+ context "ci_ref doesn't exists" do
+ let(:pipeline) { create(:ci_pipeline, :success, ref: 'new-ref') }
+
+ it_behaves_like 'creates ci_ref'
+
+ context 'when an ActiveRecord::RecordNotUnique validation is raised' do
+ let(:ci_ref) { create(:ci_ref, status: 'failed') }
+ let(:pipeline) { create(:ci_pipeline, status: :success, project: ci_ref.project, ref: ci_ref.ref) }
+
+ it 'reloads the ci_ref and retries once' do
+ subject.instance_variable_set("@ref", subject.send(:build_ref))
+
+ expect do
+ expect(subject.call).to eq(true)
+ end.not_to change { Ci::Ref.count }
+ expect(ci_ref.reload.status).to eq('fixed')
+ end
+
+ it 'raises error on multiple retries' do
+ allow_any_instance_of(Ci::Ref).to receive(:update)
+ .and_raise(ActiveRecord::RecordNotUnique)
+
+ expect { subject.call }.to raise_error(ActiveRecord::RecordNotUnique)
+ end
+ end
+ end
+
+ context 'ci_ref exists' do
+ let!(:ci_ref) { create(:ci_ref, status: 'failed') }
+ let(:pipeline) { ci_ref.pipelines.first }
+
+ it_behaves_like 'updates ci_ref'
+
+ context 'pipeline status is invalid' do
+ let!(:pipeline) { create(:ci_pipeline, :running, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) }
+
+ it_behaves_like 'does a noop'
+ end
+
+ context 'newer pipeline finished' do
+ let(:newer_pipeline) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) }
+
+ before do
+ ci_ref.update!(last_updated_by_pipeline: newer_pipeline)
+ end
+
+ it_behaves_like 'does a noop'
+ end
+
+ context 'ref is stale' do
+ let(:pipeline1) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) }
+ let(:pipeline2) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) }
+
+ it 'reloads the ref and retry' do
+ service1 = described_class.new(pipeline1)
+ service2 = described_class.new(pipeline2)
+
+ service2.send(:ref)
+ service1.call
+ expect(ci_ref.reload.status).to eq('fixed')
+ expect do
+ expect(service2.call).to eq(true)
+ # We expect 'success' in this case rather than 'fixed' because
+ # the ref is correctly reloaded on stale error.
+ expect(ci_ref.reload.status).to eq('success')
+ end.not_to change { Ci::Ref.count }
+ end
+
+ it 'aborts when a newer pipeline finished' do
+ service1 = described_class.new(pipeline1)
+ service2 = described_class.new(pipeline2)
+
+ service2.call
+ expect do
+ expect(service1.call).to eq(false)
+ expect(ci_ref.reload.status).to eq('fixed')
+ end.not_to change { Ci::Ref.count }
+ end
+ end
+
+ context 'ref exists as both tag/branch and tag' do
+ let(:pipeline) { create(:ci_pipeline, :failed, project: ci_ref.project, ref: ci_ref.ref, tag: true) }
+ let!(:branch_pipeline) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: false) }
+
+ it_behaves_like 'creates ci_ref'
+ end
+ end
+ end
+end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 07a1be6c12b..120bfc6d0ca 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -2315,6 +2315,7 @@ describe NotificationService, :mailer do
user = create_user_with_notification(:custom, 'custom_enabled')
update_custom_notification(:success_pipeline, user, resource: project)
update_custom_notification(:failed_pipeline, user, resource: project)
+ update_custom_notification(:fixed_pipeline, user, resource: project)
user
end
@@ -2322,6 +2323,7 @@ describe NotificationService, :mailer do
user = create_user_with_notification(:custom, 'custom_disabled')
update_custom_notification(:success_pipeline, user, resource: project, value: false)
update_custom_notification(:failed_pipeline, user, resource: project, value: false)
+ update_custom_notification(:fixed_pipeline, user, resource: project, value: false)
user
end
@@ -2514,6 +2516,85 @@ describe NotificationService, :mailer do
end
end
end
+
+ context 'with a fixed pipeline' do
+ let(:ref_status) { 'fixed' }
+
+ context 'when the creator has no custom notification set' do
+ let(:pipeline) { create_pipeline(u_member, :success) }
+
+ it 'emails only the creator' do
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+
+ should_only_email(u_member, kind: :bcc)
+ end
+
+ it_behaves_like 'project emails are disabled' do
+ let(:notification_target) { pipeline }
+ let(:notification_trigger) { notification.pipeline_finished(pipeline, ref_status: ref_status) }
+ end
+
+ context 'when the creator has group notification email set' do
+ let(:group_notification_email) { 'user+group@example.com' }
+
+ before do
+ group = create(:group)
+
+ project.update(group: group)
+ create(:notification_setting, user: u_member, source: group, notification_email: group_notification_email)
+ end
+
+ it 'sends to group notification email' do
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+
+ expect(email_recipients(kind: :bcc).first).to eq(group_notification_email)
+ end
+ end
+ end
+
+ context 'when the creator has watch set' do
+ before do
+ pipeline = create_pipeline(u_watcher, :success)
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+ end
+
+ it 'emails only the creator' do
+ should_only_email(u_watcher, kind: :bcc)
+ end
+ end
+
+ context 'when the creator has custom notifications, but without any set' do
+ before do
+ pipeline = create_pipeline(u_custom_notification_unset, :success)
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+ end
+
+ it 'emails only the creator' do
+ should_only_email(u_custom_notification_unset, kind: :bcc)
+ end
+ end
+
+ context 'when the creator has custom notifications disabled' do
+ before do
+ pipeline = create_pipeline(u_custom_notification_disabled, :success)
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+ end
+
+ it 'notifies nobody' do
+ should_not_email_anyone
+ end
+ end
+
+ context 'when the creator has custom notifications set' do
+ it 'emails only the creator' do
+ pipeline = create_pipeline(u_custom_notification_enabled, :success)
+
+ notification.pipeline_finished(pipeline, ref_status: ref_status)
+
+ should_only_email(u_custom_notification_enabled, kind: :bcc)
+ end
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
index 60c0ec45c71..8f5bfdacc3a 100644
--- a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb
@@ -65,9 +65,16 @@ shared_examples 'resource notes mentions migration' do |migration_class, resourc
end
shared_examples 'schedules resource mentions migration' do |resource_class, is_for_notes|
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+ end
+
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
Timecop.freeze do
+ resource_count = is_for_notes ? Note.count : resource_class.count
+ expect(resource_count).to eq 5
+
migrate!
migration = described_class::MIGRATION
diff --git a/spec/support/shared_examples/views/pipeline_status_changes_email.rb b/spec/support/shared_examples/views/pipeline_status_changes_email.rb
new file mode 100644
index 00000000000..15b4ce9c44e
--- /dev/null
+++ b/spec/support/shared_examples/views/pipeline_status_changes_email.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+shared_examples 'pipeline status changes email' do
+ include Devise::Test::ControllerHelpers
+
+ let(:user) { create(:user, developer_projects: [project]) }
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project,
+ user: user,
+ ref: project.default_branch,
+ sha: project.commit.sha,
+ status: status)
+ end
+
+ before do
+ assign(:project, project)
+ assign(:pipeline, pipeline)
+ assign(:merge_request, merge_request)
+ end
+
+ shared_examples_for 'renders the pipeline status changes email correctly' do
+ context 'pipeline with user' do
+ it 'renders the email correctly' do
+ render
+
+ expect(rendered).to have_content title
+ expect(rendered).to have_content pipeline.project.name
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
+ expect(rendered).to have_content pipeline.commit.author_name
+ expect(rendered).to have_content "##{pipeline.id}"
+ expect(rendered).to have_content pipeline.user.name
+
+ if status == :failed
+ expect(rendered).to have_content build.name
+ end
+ end
+
+ it_behaves_like 'correct pipeline information for pipelines for merge requests'
+ end
+
+ context 'pipeline without user' do
+ before do
+ pipeline.update_attribute(:user, nil)
+ end
+
+ it 'renders the email correctly' do
+ render
+
+ expect(rendered).to have_content title
+ expect(rendered).to have_content pipeline.project.name
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
+ expect(rendered).to have_content pipeline.commit.author_name
+ expect(rendered).to have_content "##{pipeline.id}"
+ expect(rendered).to have_content "by API"
+
+ if status == :failed
+ expect(rendered).to have_content build.name
+ end
+ end
+ end
+ end
+
+ context 'when the pipeline contains a failed job' do
+ let!(:build) { create(:ci_build, status: status, pipeline: pipeline, project: pipeline.project) }
+
+ it_behaves_like 'renders the pipeline status changes email correctly'
+ end
+
+ context 'when the latest failed job is a bridge job' do
+ let!(:build) { create(:ci_bridge, status: status, pipeline: pipeline, project: pipeline.project) }
+
+ it_behaves_like 'renders the pipeline status changes email correctly'
+ end
+end
diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
index a540a53c91d..80dc14b523d 100644
--- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
@@ -3,72 +3,8 @@
require 'spec_helper'
describe 'notify/pipeline_failed_email.html.haml' do
- include Devise::Test::ControllerHelpers
-
- let(:user) { create(:user, developer_projects: [project]) }
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- user: user,
- ref: project.default_branch,
- sha: project.commit.sha,
- status: :failed)
- end
-
- before do
- assign(:project, project)
- assign(:pipeline, pipeline)
- assign(:merge_request, merge_request)
- end
-
- shared_examples_for 'renders the pipeline failed email correctly' do
- context 'pipeline with user' do
- it 'renders the email correctly' do
- render
-
- expect(rendered).to have_content "Your pipeline has failed"
- expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
- expect(rendered).to have_content pipeline.commit.author_name
- expect(rendered).to have_content "##{pipeline.id}"
- expect(rendered).to have_content pipeline.user.name
- expect(rendered).to have_content build.name
- end
-
- it_behaves_like 'correct pipeline information for pipelines for merge requests'
- end
-
- context 'pipeline without user' do
- before do
- pipeline.update_attribute(:user, nil)
- end
-
- it 'renders the email correctly' do
- render
-
- expect(rendered).to have_content "Your pipeline has failed"
- expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
- expect(rendered).to have_content pipeline.commit.author_name
- expect(rendered).to have_content "##{pipeline.id}"
- expect(rendered).to have_content "by API"
- expect(rendered).to have_content build.name
- end
- end
- end
-
- context 'when the pipeline contains a failed job' do
- let!(:build) { create(:ci_build, :failed, pipeline: pipeline, project: pipeline.project) }
-
- it_behaves_like 'renders the pipeline failed email correctly'
- end
-
- context 'when the latest failed job is a bridge job' do
- let!(:build) { create(:ci_bridge, status: :failed, pipeline: pipeline, project: pipeline.project) }
-
- it_behaves_like 'renders the pipeline failed email correctly'
+ it_behaves_like 'pipeline status changes email' do
+ let(:title) { 'Your pipeline has failed' }
+ let(:status) { :failed }
end
end
diff --git a/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb b/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb
new file mode 100644
index 00000000000..382fc5ecdd3
--- /dev/null
+++ b/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'notify/pipeline_fixed_email.html.haml' do
+ it_behaves_like 'pipeline status changes email' do
+ let(:title) { 'Your pipeline has been fixed!' }
+ let(:status) { :success }
+ end
+end
diff --git a/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb b/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb
new file mode 100644
index 00000000000..ec540dc3f77
--- /dev/null
+++ b/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'notify/pipeline_fixed_email.text.erb' do
+ it_behaves_like 'pipeline status changes email' do
+ let(:title) { 'Your pipeline has been fixed!' }
+ let(:status) { :success }
+ end
+end
diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
index fbf33b7ec35..417909fd67b 100644
--- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
@@ -3,56 +3,8 @@
require 'spec_helper'
describe 'notify/pipeline_success_email.html.haml' do
- include Devise::Test::ControllerHelpers
-
- let(:user) { create(:user, developer_projects: [project]) }
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- user: user,
- ref: project.default_branch,
- sha: project.commit.sha,
- status: :success)
- end
-
- before do
- assign(:project, project)
- assign(:pipeline, pipeline)
- assign(:merge_request, merge_request)
- end
-
- context 'pipeline with user' do
- it 'renders the email correctly' do
- render
-
- expect(rendered).to have_content "Your pipeline has passed"
- expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
- expect(rendered).to have_content pipeline.commit.author_name
- expect(rendered).to have_content "##{pipeline.id}"
- expect(rendered).to have_content pipeline.user.name
- end
-
- it_behaves_like 'correct pipeline information for pipelines for merge requests'
- end
-
- context 'pipeline without user' do
- before do
- pipeline.update_attribute(:user, nil)
- end
-
- it 'renders the email correctly' do
- render
-
- expect(rendered).to have_content "Your pipeline has passed"
- expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')
- expect(rendered).to have_content pipeline.commit.author_name
- expect(rendered).to have_content "##{pipeline.id}"
- expect(rendered).to have_content "by API"
- end
+ it_behaves_like 'pipeline status changes email' do
+ let(:title) { 'Your pipeline has passed' }
+ let(:status) { :success }
end
end
diff --git a/spec/views/notify/pipeline_success_email.text.erb_spec.rb b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
index ba4633bc346..4a914cab85e 100644
--- a/spec/views/notify/pipeline_success_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
@@ -3,24 +3,8 @@
require 'spec_helper'
describe 'notify/pipeline_success_email.text.erb' do
- let(:user) { create(:user, developer_projects: [project]) }
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- let(:pipeline) do
- create(:ci_pipeline,
- :success,
- project: project,
- user: user,
- ref: project.default_branch,
- sha: project.commit.sha)
- end
-
- before do
- assign(:project, project)
- assign(:pipeline, pipeline)
- assign(:merge_request, merge_request)
+ it_behaves_like 'pipeline status changes email' do
+ let(:title) { 'Your pipeline has passed' }
+ let(:status) { :success }
end
-
- it_behaves_like 'correct pipeline information for pipelines for merge requests'
end
diff --git a/spec/workers/pipeline_notification_worker_spec.rb b/spec/workers/pipeline_notification_worker_spec.rb
index 98b0f139fe2..5defd3d5bd7 100644
--- a/spec/workers/pipeline_notification_worker_spec.rb
+++ b/spec/workers/pipeline_notification_worker_spec.rb
@@ -3,13 +3,16 @@
require 'spec_helper'
describe PipelineNotificationWorker, :mailer do
- let(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
describe '#execute' do
it 'calls NotificationService#pipeline_finished when the pipeline exists' do
- expect(NotificationService).to receive_message_chain(:new, :pipeline_finished)
+ notification_service_double = double
+ expect(notification_service_double).to receive(:pipeline_finished)
+ .with(pipeline, ref_status: 'success', recipients: ['test@gitlab.com'])
+ expect(NotificationService).to receive(:new).and_return(notification_service_double)
- subject.perform(pipeline.id)
+ subject.perform(pipeline.id, ref_status: 'success', recipients: ['test@gitlab.com'])
end
it 'does nothing when the pipeline does not exist' do
diff --git a/spec/workers/pipeline_update_ci_ref_status_worker_service_spec.rb b/spec/workers/pipeline_update_ci_ref_status_worker_service_spec.rb
new file mode 100644
index 00000000000..7228de4f895
--- /dev/null
+++ b/spec/workers/pipeline_update_ci_ref_status_worker_service_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PipelineUpdateCiRefStatusWorker do
+ let(:worker) { described_class.new }
+ let(:pipeline) { create(:ci_pipeline) }
+
+ describe '#perform' do
+ it 'updates the ci_ref status' do
+ expect(Ci::UpdateCiRefStatusService).to receive(:new)
+ .with(pipeline)
+ .and_return(double(call: true))
+
+ worker.perform(pipeline.id)
+ end
+ end
+end