summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 18:08:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 18:08:54 +0000
commit0d6fa033121a9bef708b8f2de186c4034c61d4a3 (patch)
tree851d65a09efbffa114c9a273e590d55cfb1436ab /spec/models
parent0eb3d2f799ce4f4de87fb9fc6fd98e592323bc89 (diff)
downloadgitlab-ce-0d6fa033121a9bef708b8f2de186c4034c61d4a3.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ci/bridge_spec.rb231
-rw-r--r--spec/models/prometheus_alert_spec.rb103
-rw-r--r--spec/models/push_event_spec.rb4
-rw-r--r--spec/models/user_callout_spec.rb33
-rw-r--r--spec/models/user_spec.rb34
5 files changed, 402 insertions, 3 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index a871f9b3fe6..43c843b3420 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -4,14 +4,25 @@ require 'spec_helper'
describe Ci::Bridge do
set(:project) { create(:project) }
+ set(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) }
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:bridge) do
- create(:ci_bridge, pipeline: pipeline)
+ create(:ci_bridge, :variables, status: :created,
+ options: options,
+ pipeline: pipeline)
+ end
+
+ let(:options) do
+ { trigger: { project: 'my/project', branch: 'master' } }
end
it { is_expected.to include_module(Ci::PipelineDelegator) }
+ it 'has many sourced pipelines' do
+ expect(bridge).to have_many(:sourced_pipelines)
+ end
+
describe '#tags' do
it 'only has a bridge tag' do
expect(bridge.tags).to eq [:bridge]
@@ -41,4 +52,222 @@ describe Ci::Bridge do
expect(bridge.scoped_variables_hash.keys).to include(*variables)
end
end
+
+ describe '#inherit_status_from_downstream!' do
+ let(:downstream_pipeline) { build(:ci_pipeline, status: downstream_status) }
+
+ before do
+ bridge.status = 'pending'
+ create(:ci_sources_pipeline, pipeline: downstream_pipeline, source_job: bridge)
+ end
+
+ subject { bridge.inherit_status_from_downstream!(downstream_pipeline) }
+
+ context 'when status is not supported' do
+ (::Ci::Pipeline::AVAILABLE_STATUSES - ::Ci::Pipeline::COMPLETED_STATUSES).map(&:to_s).each do |status|
+ context "when status is #{status}" do
+ let(:downstream_status) { status }
+
+ it 'returns false' do
+ expect(subject).to eq(false)
+ end
+
+ it 'does not change the bridge status' do
+ expect { subject }.not_to change { bridge.status }.from('pending')
+ end
+ end
+ end
+ end
+
+ context 'when status is supported' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:downstream_status, :upstream_status) do
+ [
+ %w[success success],
+ *::Ci::Pipeline.completed_statuses.without(:success).map { |status| [status.to_s, 'failed'] }
+ ]
+ end
+
+ with_them do
+ it 'inherits the downstream status' do
+ expect { subject }.to change { bridge.status }.from('pending').to(upstream_status)
+ end
+ end
+ end
+ end
+
+ describe '#dependent?' do
+ subject { bridge.dependent? }
+
+ context 'when bridge has strategy depend' do
+ let(:options) { { trigger: { project: 'my/project', strategy: 'depend' } } }
+
+ it { is_expected.to be true }
+ end
+
+ context 'when bridge does not have strategy depend' do
+ it { is_expected.to be false }
+ end
+ end
+
+ describe '#yaml_variables' do
+ it 'returns YAML variables' do
+ expect(bridge.yaml_variables)
+ .to include(key: 'BRIDGE', value: 'cross', public: true)
+ end
+ end
+
+ describe '#downstream_variables' do
+ it 'returns variables that are going to be passed downstream' do
+ expect(bridge.downstream_variables)
+ .to include(key: 'BRIDGE', value: 'cross')
+ end
+
+ context 'when using variables interpolation' do
+ let(:yaml_variables) do
+ [
+ {
+ key: 'EXPANDED',
+ value: '$BRIDGE-bridge',
+ public: true
+ },
+ {
+ key: 'UPSTREAM_CI_PIPELINE_ID',
+ value: '$CI_PIPELINE_ID',
+ public: true
+ },
+ {
+ key: 'UPSTREAM_CI_PIPELINE_URL',
+ value: '$CI_PIPELINE_URL',
+ public: true
+ }
+ ]
+ end
+
+ before do
+ bridge.yaml_variables.concat(yaml_variables)
+ end
+
+ it 'correctly expands variables with interpolation' do
+ expanded_values = pipeline
+ .persisted_variables
+ .to_hash
+ .transform_keys { |key| "UPSTREAM_#{key}" }
+ .map { |key, value| { key: key, value: value } }
+ .push(key: 'EXPANDED', value: 'cross-bridge')
+
+ expect(bridge.downstream_variables)
+ .to match(a_collection_including(*expanded_values))
+ end
+ end
+
+ context 'when recursive interpolation has been used' do
+ before do
+ bridge.yaml_variables << { key: 'EXPANDED', value: '$EXPANDED', public: true }
+ end
+
+ it 'does not expand variable recursively' do
+ expect(bridge.downstream_variables)
+ .to include(key: 'EXPANDED', value: '$EXPANDED')
+ end
+ end
+ end
+
+ describe 'metadata support' do
+ it 'reads YAML variables from metadata' do
+ expect(bridge.yaml_variables).not_to be_empty
+ expect(bridge.metadata).to be_a Ci::BuildMetadata
+ expect(bridge.read_attribute(:yaml_variables)).to be_nil
+ expect(bridge.metadata.config_variables).to be bridge.yaml_variables
+ end
+
+ it 'reads options from metadata' do
+ expect(bridge.options).not_to be_empty
+ expect(bridge.metadata).to be_a Ci::BuildMetadata
+ expect(bridge.read_attribute(:options)).to be_nil
+ expect(bridge.metadata.config_options).to be bridge.options
+ end
+ end
+
+ describe '#triggers_child_pipeline?' do
+ subject { bridge.triggers_child_pipeline? }
+
+ context 'when bridge defines a downstream YAML' do
+ let(:options) do
+ {
+ trigger: {
+ include: 'path/to/child.yml'
+ }
+ }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when bridge does not define a downstream YAML' do
+ let(:options) do
+ {
+ trigger: {
+ project: project.full_path
+ }
+ }
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#yaml_for_downstream' do
+ subject { bridge.yaml_for_downstream }
+
+ context 'when bridge defines a downstream YAML' do
+ let(:options) do
+ {
+ trigger: {
+ include: 'path/to/child.yml'
+ }
+ }
+ end
+
+ let(:yaml) do
+ <<~EOY
+ ---
+ include: path/to/child.yml
+ EOY
+ end
+
+ it { is_expected.to eq yaml }
+ end
+
+ context 'when bridge does not define a downstream YAML' do
+ let(:options) { {} }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#target_ref' do
+ context 'when trigger is defined' do
+ it 'returns a ref name' do
+ expect(bridge.target_ref).to eq 'master'
+ end
+
+ context 'when using variable expansion' do
+ let(:options) { { trigger: { project: 'my/project', branch: '$BRIDGE-master' } } }
+
+ it 'correctly expands variables' do
+ expect(bridge.target_ref).to eq('cross-master')
+ end
+ end
+ end
+
+ context 'when trigger does not have project defined' do
+ let(:options) { nil }
+
+ it 'returns nil' do
+ expect(bridge.target_ref).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/prometheus_alert_spec.rb b/spec/models/prometheus_alert_spec.rb
new file mode 100644
index 00000000000..cdcdb46a6c4
--- /dev/null
+++ b/spec/models/prometheus_alert_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PrometheusAlert do
+ let_it_be(:project) { build(:project) }
+ let(:metric) { build(:prometheus_metric) }
+
+ describe '.distinct_projects' do
+ let(:project1) { create(:project) }
+ let(:project2) { create(:project) }
+
+ before do
+ create(:prometheus_alert, project: project1)
+ create(:prometheus_alert, project: project1)
+ create(:prometheus_alert, project: project2)
+ end
+
+ subject { described_class.distinct_projects.count }
+
+ it 'returns a count of all distinct projects which have an alert' do
+ expect(subject).to eq(2)
+ end
+ end
+
+ describe 'operators' do
+ it 'contains the correct equality operator' do
+ expect(described_class::OPERATORS_MAP.values).to include('==')
+ expect(described_class::OPERATORS_MAP.values).not_to include('=')
+ end
+ end
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:environment) }
+ end
+
+ describe 'project validations' do
+ let(:environment) { build(:environment, project: project) }
+ let(:metric) { build(:prometheus_metric, project: project) }
+
+ subject do
+ build(:prometheus_alert, prometheus_metric: metric, environment: environment, project: project)
+ end
+
+ it { is_expected.to validate_presence_of(:environment) }
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:prometheus_metric) }
+
+ context 'when environment and metric belongs same project' do
+ it { is_expected.to be_valid }
+ end
+
+ context 'when environment belongs to different project' do
+ let(:environment) { build(:environment) }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when metric belongs to different project' do
+ let(:metric) { build(:prometheus_metric) }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when metric is common' do
+ let(:metric) { build(:prometheus_metric, :common) }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ describe '#full_query' do
+ before do
+ subject.operator = "gt"
+ subject.threshold = 1
+ subject.prometheus_metric = metric
+ end
+
+ it 'returns the concatenated query' do
+ expect(subject.full_query).to eq("#{metric.query} > 1.0")
+ end
+ end
+
+ describe '#to_param' do
+ before do
+ subject.operator = "gt"
+ subject.threshold = 1
+ subject.prometheus_metric = metric
+ end
+
+ it 'returns the params of the prometheus alert' do
+ expect(subject.to_param).to eq(
+ "alert" => metric.title,
+ "expr" => "#{metric.query} > 1.0",
+ "for" => "5m",
+ "labels" => {
+ "gitlab" => "hook",
+ "gitlab_alert_id" => metric.id
+ })
+ end
+ end
+end
diff --git a/spec/models/push_event_spec.rb b/spec/models/push_event_spec.rb
index 5509ed87308..8682e1c797b 100644
--- a/spec/models/push_event_spec.rb
+++ b/spec/models/push_event_spec.rb
@@ -74,7 +74,7 @@ describe PushEvent do
create(:push_event_payload, event: event4, ref: 'baz', action: :removed)
create(:push_event_payload, event: event5, ref: 'baz', ref_type: :tag)
- project.repository.create_branch('bar', 'master')
+ project.repository.create_branch('bar')
create(
:merge_request,
@@ -83,7 +83,7 @@ describe PushEvent do
source_branch: 'bar'
)
- project.repository.create_branch('qux', 'master')
+ project.repository.create_branch('qux')
create(
:merge_request,
diff --git a/spec/models/user_callout_spec.rb b/spec/models/user_callout_spec.rb
index de6534b480a..a084b1ac662 100644
--- a/spec/models/user_callout_spec.rb
+++ b/spec/models/user_callout_spec.rb
@@ -17,4 +17,37 @@ describe UserCallout do
it { is_expected.to validate_presence_of(:feature_name) }
it { is_expected.to validate_uniqueness_of(:feature_name).scoped_to(:user_id).ignoring_case_sensitivity }
end
+
+ describe 'scopes' do
+ describe '.with_feature_name' do
+ let(:second_feature_name) { described_class.feature_names.keys.second }
+ let(:last_feature_name) { described_class.feature_names.keys.last }
+
+ it 'returns callout for requested feature name only' do
+ callout1 = create(:user_callout, feature_name: second_feature_name )
+ create(:user_callout, feature_name: last_feature_name )
+
+ callouts = described_class.with_feature_name(second_feature_name)
+
+ expect(callouts).to match_array([callout1])
+ end
+ end
+
+ describe '.with_dismissed_after' do
+ let(:some_feature_name) { described_class.feature_names.keys.second }
+ let(:callout_dismissed_month_ago) { create(:user_callout, feature_name: some_feature_name, dismissed_at: 1.month.ago )}
+
+ it 'does not return callouts dismissed before specified date' do
+ callouts = described_class.with_dismissed_after(15.days.ago)
+
+ expect(callouts).to match_array([])
+ end
+
+ it 'returns callouts dismissed after specified date' do
+ callouts = described_class.with_dismissed_after(2.months.ago)
+
+ expect(callouts).to match_array([callout_dismissed_month_ago])
+ end
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 855b8e3a8a7..d441f54a0fb 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -4157,6 +4157,40 @@ describe User, :do_not_mock_admin_mode do
end
end
+ describe '#dismissed_callout?' do
+ subject(:user) { create(:user) }
+
+ let(:feature_name) { UserCallout.feature_names.keys.first }
+
+ context 'when no callout dismissal record exists' do
+ it 'returns false when no ignore_dismissal_earlier_than provided' do
+ expect(user.dismissed_callout?(feature_name: feature_name)).to eq false
+ end
+
+ it 'returns false when ignore_dismissal_earlier_than provided' do
+ expect(user.dismissed_callout?(feature_name: feature_name, ignore_dismissal_earlier_than: 3.months.ago)).to eq false
+ end
+ end
+
+ context 'when dismissed callout exists' do
+ before do
+ create(:user_callout, user: user, feature_name: feature_name, dismissed_at: 4.months.ago)
+ end
+
+ it 'returns true when no ignore_dismissal_earlier_than provided' do
+ expect(user.dismissed_callout?(feature_name: feature_name)).to eq true
+ end
+
+ it 'returns true when ignore_dismissal_earlier_than is earlier than dismissed_at' do
+ expect(user.dismissed_callout?(feature_name: feature_name, ignore_dismissal_earlier_than: 6.months.ago)).to eq true
+ end
+
+ it 'returns false when ignore_dismissal_earlier_than is later than dismissed_at' do
+ expect(user.dismissed_callout?(feature_name: feature_name, ignore_dismissal_earlier_than: 3.months.ago)).to eq false
+ end
+ end
+ end
+
describe 'bots & humans' do
it 'returns corresponding users' do
human = create(:user)