Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-07 09:09:59 +00:00
parent a608e12045
commit dc62bfce8b
21 changed files with 485 additions and 120 deletions

View file

@ -21,6 +21,12 @@ module Emails
mail_to(to: email, subject: @message.subject_line)
end
def account_validation_email(pipeline, recipient_email)
@message = Gitlab::Email::Message::AccountValidation.new(pipeline)
mail_to(to: recipient_email, subject: @message.subject_line)
end
private
def mail_to(to:, subject:)

View file

@ -55,16 +55,23 @@
.cta_link a {
font-size: 24px;
font-family: 'Source Sans Pro', helvetica, arial, sans-serif;
color: #ffffff;
text-decoration: none;
display: inline-block;
}
.cta_link_primary a {
color: #ffffff;
border-radius: 5px;
-webkit-border-radius: 5px;
background-color: #6e49cb;
border-top: 15px solid #6e49cb;
border-bottom: 15px solid #6e49cb;
border-right: 40px solid #6e49cb;
border-left: 40px solid #6e49cb;
display: inline-block;
}
.cta_link_secondary a {
color: #6e49cb;
padding: 25px 40px 15px;
}
.footernav {

View file

@ -0,0 +1,16 @@
%tr
%td{ bgcolor: "#ffffff", height: "auto", style: "max-width: 600px; width: 100%; text-align: center; height: 200px; padding: 25px 15px; mso-line-height-rule: exactly; min-height: 40px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;", valign: "middle", width: "100%" }
= inline_image_link(@message.logo_path, { width: '150', style: 'width: 150px;' })
%h1{ style: "font-size: 40px; line-height: 46x; color: #000000; padding: 20px 0 0 0; font-weight: normal;" }
= @message.title
%tr
%td{ style: "padding: 10px 20px 30px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 18px; line-height: 24px;" }
%p{ style: "margin: 0 0 20px 0;" }
= @message.body_line1.html_safe
- @message.body_line2&.tap do |line|
%p{ style: "margin: 0 0 20px 0;" }
= line.html_safe
%tr
%td{ align: "center", style: "padding: 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" }
.cta_link.cta_link_primary= @message.cta_link
.cta_link.cta_link_secondary= @message.cta2_link

View file

@ -0,0 +1,15 @@
<%= @message.title %>
<%= @message.body_line1 %>
<%= @message.body_line2 %>
<%= @message.cta_link %>
<%= @message.cta2_link %>
<%= @message.footer_links %>
<%= @message.address %>
<%= @message.unsubscribe %>

View file

@ -20,7 +20,7 @@
- if @message.cta_text
%tr
%td{ align: "center", style: "padding: 10px 20px 80px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" }
.cta_link= @message.cta_link
.cta_link.cta_link_primary= @message.cta_link
- else
%tr
%td{ style: "padding: 10px 20px 10px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 16px; line-height: 20px;" }

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
class DropPagesDeploymentsBuildsFk < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
FK_NAME = 'fk_rails_c3a90cf29b'
def up
remove_foreign_key_if_exists(:pages_deployments, :ci_builds, name: FK_NAME)
end
def down
add_concurrent_foreign_key(
:pages_deployments,
:ci_builds,
name: FK_NAME,
column: :ci_build_id,
target_column: :id,
on_delete: :nullify
)
end
end

View file

@ -0,0 +1 @@
2630b21c7134ac539a18798f2f2b99f468e171b79e30a184f7e8cdaccd11d465

View file

@ -31056,9 +31056,6 @@ ALTER TABLE ONLY packages_nuget_dependency_link_metadata
ALTER TABLE ONLY group_deploy_keys_groups
ADD CONSTRAINT fk_rails_c3854f19f5 FOREIGN KEY (group_deploy_key_id) REFERENCES group_deploy_keys(id) ON DELETE CASCADE;
ALTER TABLE ONLY pages_deployments
ADD CONSTRAINT fk_rails_c3a90cf29b FOREIGN KEY (ci_build_id) REFERENCES ci_builds(id) ON DELETE SET NULL;
ALTER TABLE ONLY merge_request_user_mentions
ADD CONSTRAINT fk_rails_c440b9ea31 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;

View file

@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# GraphQL API removed items
# GraphQL API removed items **(FREE)**
GraphQL is a versionless API, unlike the REST API.
Occasionally, items have to be updated or removed from the GraphQL API.

View file

@ -4,7 +4,7 @@ group: Pipeline Execution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# GitLab CI/CD job token
# GitLab CI/CD job token **(FREE)**
When a pipeline job is about to run, GitLab generates a unique token and injects it as the
[`CI_JOB_TOKEN` predefined variable](../variables/predefined_variables.md).

View file

@ -76,7 +76,7 @@ If the app requires additional permissions, [the update must first be manually a
## Install the GitLab.com for Jira Cloud app for self-managed instances **(FREE SELF)**
If your GitLab instance is self-managed, you must follow some
extra steps to install the GitLab.com for Jira Cloud app.
extra steps to install the GitLab.com for Jira Cloud app, and your GitLab instance must be accessible by Jira.
Each Jira Cloud application must be installed from a single location. Jira fetches
a [manifest file](https://developer.atlassian.com/cloud/jira/platform/connect-app-descriptor/)

View file

@ -34,7 +34,7 @@ module Gitlab
end
def file_name
"#{report_type.to_s.dasherize}-report-format.json"
report_type == :api_fuzzing ? "dast-report-format.json" : "#{report_type.to_s.dasherize}-report-format.json"
end
end

View file

@ -37,3 +37,7 @@ packages_package_file_build_infos:
- table: ci_pipelines
column: pipeline_id
on_delete: async_nullify
pages_deployments:
- table: ci_builds
column: ci_build_id
on_delete: async_nullify

View file

@ -0,0 +1,96 @@
# frozen_string_literal: true
module Gitlab
module Email
module Message
class AccountValidation
include Gitlab::Email::Message::InProductMarketing::Helper
include Gitlab::Routing
attr_accessor :pipeline, :format
def initialize(pipeline, format: :html)
@pipeline = pipeline
@format = format
end
def subject_line
s_('AccountValidation|Fix your pipelines by validating your account')
end
def title
s_("AccountValidation|Looks like youll need to validate your account to use free pipeline minutes")
end
def body_line1
s_("AccountValidation|In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project.")
end
def body_line2
format_options = strong_options.merge({ learn_more_link: learn_more_link })
s_("AccountValidation|This is required to discourage and reduce the abuse on GitLab infrastructure. %{strong_start}GitLab will not charge or store your card, it will only be used for validation.%{strong_end} %{learn_more_link}").html_safe % format_options
end
def cta_text
s_('AccountValidation|Validate your account')
end
def cta2_text
s_("AccountValidation|I'll bring my own runners")
end
def logo_path
'mailers/in_product_marketing/verify-2.png'
end
def cta_link
url = project_pipeline_url(pipeline.project, pipeline)
case format
when :html
ActionController::Base.helpers.link_to cta_text, url, target: '_blank', rel: 'noopener noreferrer'
else
[cta_text, url].join(' >> ')
end
end
def cta2_link
url = 'https://docs.gitlab.com/runner/install/'
case format
when :html
ActionController::Base.helpers.link_to cta2_text, url, target: '_blank', rel: 'noopener noreferrer'
else
[cta2_text, url].join(' >> ')
end
end
def learn_more_link
link(s_('AccountValidation|Learn more.'), 'https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/')
end
def unsubscribe
parts = [
s_('AccountValidation|If you no longer wish to receive marketing emails from us,'),
s_('AccountValidation|you may %{unsubscribe_link} at any time.') % { unsubscribe_link: mailgun_unsubscribe_link }
]
case format
when :html
parts.join(' ')
else
parts.join("\n" + ' ' * 16)
end
end
private
def mailgun_unsubscribe_link
mailgun_unsubscribe_url = '%tag_unsubscribe_url%'
link(s_('AccountValidation|unsubscribe'), mailgun_unsubscribe_url)
end
end
end
end
end

View file

@ -406,7 +406,8 @@ module Gitlab
results[:projects_jira_cloud_active] = jira_integration_data_hash[:projects_jira_cloud_active]
results
rescue ActiveRecord::StatementInvalid
rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
{ projects_jira_server_active: FALLBACK, projects_jira_cloud_active: FALLBACK }
end
# rubocop: enable CodeReuse/ActiveRecord

View file

@ -56,7 +56,8 @@ module Gitlab
else
relation.count
end
rescue ActiveRecord::StatementInvalid
rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK
end
@ -66,7 +67,8 @@ module Gitlab
else
relation.distinct_count_by(column)
end
rescue ActiveRecord::StatementInvalid
rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK
end
@ -78,7 +80,8 @@ module Gitlab
yield buckets if block_given?
buckets.estimated_distinct_count
rescue ActiveRecord::StatementInvalid
rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK
# catch all rescue should be removed as a part of feature flag rollout issue
# https://gitlab.com/gitlab-org/gitlab/-/issues/285485
@ -89,7 +92,8 @@ module Gitlab
def sum(relation, column, batch_size: nil, start: nil, finish: nil)
Gitlab::Database::BatchCount.batch_sum(relation, column, batch_size: batch_size, start: start, finish: finish)
rescue ActiveRecord::StatementInvalid
rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK
end
@ -155,7 +159,8 @@ module Gitlab
query: query.to_sql,
message: e.message
)
# Raises error for dev env
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
HISTOGRAM_FALLBACK
end
# rubocop: enable CodeReuse/ActiveRecord

View file

@ -1877,6 +1877,36 @@ msgstr ""
msgid "Account: %{account}"
msgstr ""
msgid "AccountValidation|Fix your pipelines by validating your account"
msgstr ""
msgid "AccountValidation|I'll bring my own runners"
msgstr ""
msgid "AccountValidation|If you no longer wish to receive marketing emails from us,"
msgstr ""
msgid "AccountValidation|In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project."
msgstr ""
msgid "AccountValidation|Learn more."
msgstr ""
msgid "AccountValidation|Looks like youll need to validate your account to use free pipeline minutes"
msgstr ""
msgid "AccountValidation|This is required to discourage and reduce the abuse on GitLab infrastructure. %{strong_start}GitLab will not charge or store your card, it will only be used for validation.%{strong_end} %{learn_more_link}"
msgstr ""
msgid "AccountValidation|Validate your account"
msgstr ""
msgid "AccountValidation|unsubscribe"
msgstr ""
msgid "AccountValidation|you may %{unsubscribe_link} at any time."
msgstr ""
msgid "Action"
msgstr ""
@ -5418,10 +5448,10 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
msgid "Billings|To use free pipeline minutes on shared runners, youll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge or store your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgid "Billings|To use free pipeline minutes on shared runners, youll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "Billings|To use free pipeline minutes on shared runners, youll need to validate your account with a credit or debit card. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge or store your card, it will only be used for validation.%{strongEnd}"
msgid "Billings|To use free pipeline minutes on shared runners, youll need to validate your account with a credit or debit card. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd}"
msgstr ""
msgid "Billings|User successfully validated"

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Email::Message::AccountValidation do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
subject(:message) { described_class.new(pipeline) }
it 'contains the correct message', :aggregate_failures do
expect(message.subject_line).to eq 'Fix your pipelines by validating your account'
expect(message.title).to eq "Looks like youll need to validate your account to use free pipeline minutes"
expect(message.body_line1).to eq "In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project."
expect(message.body_line2).to include(
'This is required to discourage and reduce the abuse on GitLab infrastructure.',
'<b>GitLab will not charge or store your card, it will only be used for validation.</b>',
'<a href="https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/">Learn more.</a>'
)
expect(message.cta_text).to eq 'Validate your account'
expect(message.cta2_text).to eq "I'll bring my own runners"
expect(message.logo_path).to eq 'mailers/in_product_marketing/verify-2.png'
expect(message.unsubscribe).to include('%tag_unsubscribe_url%')
end
end

View file

@ -684,23 +684,50 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
it 'works when queries time out' do
allow_any_instance_of(ActiveRecord::Relation)
.to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
context 'when queries time out' do
let(:metric_method) { :count }
expect { subject }.not_to raise_error
before do
allow_any_instance_of(ActiveRecord::Relation).to receive(metric_method).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
end
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
context 'when metric calls find_in_batches' do
let(:metric_method) { :find_in_batches }
it 'raises an error for jira_usage' do
expect { described_class.jira_usage }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'does not raise an error' do
expect { subject }.not_to raise_error
end
context 'when metric calls find_in_batches' do
let(:metric_method) { :find_in_batches }
it 'does not raise an error for jira_usage' do
expect { described_class.jira_usage }.not_to raise_error
end
end
end
end
it 'includes a recording_ce_finished_at timestamp' do
expect(subject[:recording_ce_finished_at]).to be_a(Time)
end
it 'jira usage works when queries time out' do
allow_any_instance_of(ActiveRecord::Relation)
.to receive(:find_in_batches).and_raise(ActiveRecord::StatementInvalid.new(''))
expect { described_class.jira_usage }.not_to raise_error
end
end
describe '.system_usage_data_monthly' do
@ -1355,46 +1382,58 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
context 'when queries time out' do
before do
allow_any_instance_of(ActiveRecord::Relation)
.to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
allow_any_instance_of(ActiveRecord::Relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
end
it 'returns -1 for email campaign data' do
expected_data = {
"in_product_marketing_email_create_0_sent" => -1,
"in_product_marketing_email_create_0_cta_clicked" => -1,
"in_product_marketing_email_create_1_sent" => -1,
"in_product_marketing_email_create_1_cta_clicked" => -1,
"in_product_marketing_email_create_2_sent" => -1,
"in_product_marketing_email_create_2_cta_clicked" => -1,
"in_product_marketing_email_team_short_0_sent" => -1,
"in_product_marketing_email_team_short_0_cta_clicked" => -1,
"in_product_marketing_email_trial_short_0_sent" => -1,
"in_product_marketing_email_trial_short_0_cta_clicked" => -1,
"in_product_marketing_email_admin_verify_0_sent" => -1,
"in_product_marketing_email_admin_verify_0_cta_clicked" => -1,
"in_product_marketing_email_verify_0_sent" => -1,
"in_product_marketing_email_verify_0_cta_clicked" => -1,
"in_product_marketing_email_verify_1_sent" => -1,
"in_product_marketing_email_verify_1_cta_clicked" => -1,
"in_product_marketing_email_verify_2_sent" => -1,
"in_product_marketing_email_verify_2_cta_clicked" => -1,
"in_product_marketing_email_trial_0_sent" => -1,
"in_product_marketing_email_trial_0_cta_clicked" => -1,
"in_product_marketing_email_trial_1_sent" => -1,
"in_product_marketing_email_trial_1_cta_clicked" => -1,
"in_product_marketing_email_trial_2_sent" => -1,
"in_product_marketing_email_trial_2_cta_clicked" => -1,
"in_product_marketing_email_team_0_sent" => -1,
"in_product_marketing_email_team_0_cta_clicked" => -1,
"in_product_marketing_email_team_1_sent" => -1,
"in_product_marketing_email_team_1_cta_clicked" => -1,
"in_product_marketing_email_team_2_sent" => -1,
"in_product_marketing_email_team_2_cta_clicked" => -1,
"in_product_marketing_email_experience_0_sent" => -1
}
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
expect(subject).to eq(expected_data)
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'returns -1 for email campaign data' do
expected_data = {
"in_product_marketing_email_create_0_sent" => -1,
"in_product_marketing_email_create_0_cta_clicked" => -1,
"in_product_marketing_email_create_1_sent" => -1,
"in_product_marketing_email_create_1_cta_clicked" => -1,
"in_product_marketing_email_create_2_sent" => -1,
"in_product_marketing_email_create_2_cta_clicked" => -1,
"in_product_marketing_email_team_short_0_sent" => -1,
"in_product_marketing_email_team_short_0_cta_clicked" => -1,
"in_product_marketing_email_trial_short_0_sent" => -1,
"in_product_marketing_email_trial_short_0_cta_clicked" => -1,
"in_product_marketing_email_admin_verify_0_sent" => -1,
"in_product_marketing_email_admin_verify_0_cta_clicked" => -1,
"in_product_marketing_email_verify_0_sent" => -1,
"in_product_marketing_email_verify_0_cta_clicked" => -1,
"in_product_marketing_email_verify_1_sent" => -1,
"in_product_marketing_email_verify_1_cta_clicked" => -1,
"in_product_marketing_email_verify_2_sent" => -1,
"in_product_marketing_email_verify_2_cta_clicked" => -1,
"in_product_marketing_email_trial_0_sent" => -1,
"in_product_marketing_email_trial_0_cta_clicked" => -1,
"in_product_marketing_email_trial_1_sent" => -1,
"in_product_marketing_email_trial_1_cta_clicked" => -1,
"in_product_marketing_email_trial_2_sent" => -1,
"in_product_marketing_email_trial_2_cta_clicked" => -1,
"in_product_marketing_email_team_0_sent" => -1,
"in_product_marketing_email_team_0_cta_clicked" => -1,
"in_product_marketing_email_team_1_sent" => -1,
"in_product_marketing_email_team_1_cta_clicked" => -1,
"in_product_marketing_email_team_2_sent" => -1,
"in_product_marketing_email_team_2_cta_clicked" => -1,
"in_product_marketing_email_experience_0_sent" => -1
}
expect(subject).to eq(expected_data)
end
end
end

View file

@ -5,6 +5,30 @@ require 'spec_helper'
RSpec.describe Gitlab::Utils::UsageData do
include Database::DatabaseHelpers
shared_examples 'failing hardening method' do
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
stub_const("Gitlab::Utils::UsageData::FALLBACK", fallback)
allow(failing_class).to receive(failing_method).and_raise(ActiveRecord::StatementInvalid)
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'returns the fallback' do
expect(subject).to eq(fallback)
end
end
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
describe '#add_metric' do
let(:metric) { 'UuidMetric'}
@ -22,11 +46,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.count(relation, batch: false)).to eq(1)
end
it 'returns the fallback value when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15)
allow(relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
context 'when counting fails' do
subject { described_class.count(relation, batch: false) }
expect(described_class.count(relation, batch: false)).to eq(15)
let(:fallback) { 15 }
let(:failing_class) { relation }
let(:failing_method) { :count }
it_behaves_like 'failing hardening method'
end
end
@ -39,11 +66,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.distinct_count(relation, batch: false)).to eq(1)
end
it 'returns the fallback value when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15)
allow(relation).to receive(:distinct_count_by).and_raise(ActiveRecord::StatementInvalid.new(''))
context 'when counting fails' do
subject { described_class.distinct_count(relation, batch: false) }
expect(described_class.distinct_count(relation, batch: false)).to eq(15)
let(:fallback) { 15 }
let(:failing_class) { relation }
let(:failing_method) { :distinct_count_by }
it_behaves_like 'failing hardening method'
end
end
@ -155,14 +185,24 @@ RSpec.describe Gitlab::Utils::UsageData do
stub_const("Gitlab::Utils::UsageData::DISTRIBUTED_HLL_FALLBACK", 4)
end
it 'returns fallback if counter raises WRONG_CONFIGURATION_ERROR' do
expect(described_class.estimate_batch_distinct_count(relation, 'id', start: 1, finish: 0)).to eq 3
context 'when counter raises WRONG_CONFIGURATION_ERROR' do
subject { described_class.estimate_batch_distinct_count(relation, 'id', start: 1, finish: 0) }
let(:fallback) { 3 }
let(:failing_class) { Gitlab::Database::PostgresHll::BatchDistinctCounter }
let(:failing_method) { :new }
it_behaves_like 'failing hardening method'
end
it 'returns default fallback value when counting fails due to database error' do
allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(ActiveRecord::StatementInvalid.new(''))
context 'when counting fails due to database error' do
subject { described_class.estimate_batch_distinct_count(relation) }
expect(described_class.estimate_batch_distinct_count(relation)).to eq(3)
let(:fallback) { 3 }
let(:failing_class) { Gitlab::Database::PostgresHll::BatchDistinctCounter }
let(:failing_method) { :new }
it_behaves_like 'failing hardening method'
end
it 'logs error and returns DISTRIBUTED_HLL_FALLBACK value when counting raises any error', :aggregate_failures do
@ -187,13 +227,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.sum(relation, :column, batch_size: 100, start: 2, finish: 3)).to eq(1)
end
it 'returns the fallback value when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15)
allow(Gitlab::Database::BatchCount)
.to receive(:batch_sum)
.and_raise(ActiveRecord::StatementInvalid.new(''))
context 'when counting fails' do
subject { described_class.sum(relation, :column) }
expect(described_class.sum(relation, :column)).to eq(15)
let(:fallback) { 15 }
let(:failing_class) { Gitlab::Database::BatchCount }
let(:failing_method) { :batch_sum }
it_behaves_like 'failing hardening method'
end
end
@ -273,23 +314,45 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(histogram).to eq('2' => 1)
end
it 'returns fallback and logs canceled queries' do
create(:alert_management_http_integration, :active, project: project1)
context 'when query timeout' do
subject do
with_statement_timeout(0.001) do
relation = AlertManagement::HttpIntegration.select('pg_sleep(0.002)')
described_class.histogram(relation, column, buckets: 1..100)
end
end
expect(Gitlab::AppJsonLogger).to receive(:error).with(
event: 'histogram',
relation: relation.table_name,
operation: 'histogram',
operation_args: [column, 1, 100, 99],
query: kind_of(String),
message: /PG::QueryCanceled/
)
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
create(:alert_management_http_integration, :active, project: project1)
end
with_statement_timeout(0.001) do
relation = AlertManagement::HttpIntegration.select('pg_sleep(0.002)')
histogram = described_class.histogram(relation, column, buckets: 1..100)
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
expect(histogram).to eq(fallback)
it 'logs canceled queries' do
expect(Gitlab::AppJsonLogger).to receive(:error).with(
event: 'histogram',
relation: relation.table_name,
operation: 'histogram',
operation_args: [column, 1, 100, 99],
query: kind_of(String),
message: /PG::QueryCanceled/
)
subject
end
it 'returns fallback' do
expect(subject).to eq(fallback)
end
end
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises error' do
expect { subject }.to raise_error(ActiveRecord::QueryCanceled)
end
end
end
end

View file

@ -5,29 +5,11 @@ require 'email_spec'
RSpec.describe Emails::InProductMarketing do
include EmailSpec::Matchers
include Gitlab::Routing.url_helpers
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let!(:onboarding_progress) { create(:onboarding_progress, namespace: group) }
describe '#in_product_marketing_email' do
using RSpec::Parameterized::TableSyntax
let(:track) { :create }
let(:series) { 0 }
subject { Notify.in_product_marketing_email(user.id, group.id, track, series) }
include_context 'gitlab email notification'
it 'sends to the right user with a link to unsubscribe' do
aggregate_failures do
expect(subject).to deliver_to(user.notification_email_or_default)
expect(subject).to have_body_text(profile_notifications_url)
end
end
shared_examples 'has custom headers when on gitlab.com' do
context 'when on gitlab.com' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
@ -45,6 +27,30 @@ RSpec.describe Emails::InProductMarketing do
end
end
end
end
describe '#in_product_marketing_email' do
let_it_be(:group) { create(:group) }
let!(:onboarding_progress) { create(:onboarding_progress, namespace: group) }
using RSpec::Parameterized::TableSyntax
let(:track) { :create }
let(:series) { 0 }
subject { Notify.in_product_marketing_email(user.id, group.id, track, series) }
include_context 'gitlab email notification'
it_behaves_like 'has custom headers when on gitlab.com'
it 'sends to the right user with a link to unsubscribe' do
aggregate_failures do
expect(subject).to deliver_to(user.notification_email_or_default)
expect(subject).to have_body_text(profile_notifications_url)
end
end
where(:track, :series) do
:create | 0
@ -102,4 +108,35 @@ RSpec.describe Emails::InProductMarketing do
end
end
end
describe '#account_validation_email' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
subject { Notify.account_validation_email(pipeline, user.notification_email_or_default) }
it 'sends to the right user with a link to unsubscribe' do
expect(subject).to deliver_to(user.notification_email_or_default)
end
it_behaves_like 'has custom headers when on gitlab.com'
it 'has the correct subject and content' do
message = Gitlab::Email::Message::AccountValidation.new(pipeline)
cta_url = project_pipeline_url(pipeline.project, pipeline)
cta2_url = 'https://docs.gitlab.com/runner/install/'
aggregate_failures do
is_expected.to have_subject(message.subject_line)
is_expected.to have_body_text(message.title)
is_expected.to have_body_text(message.body_line1)
is_expected.to have_body_text(CGI.unescapeHTML(message.body_line2))
is_expected.to have_body_text(CGI.unescapeHTML(message.cta_link))
is_expected.to have_body_text(CGI.unescapeHTML(message.cta2_link))
is_expected.to have_body_text(cta_url)
is_expected.to have_body_text(cta2_url)
end
end
end
end