diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-06 18:08:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-06 18:08:54 +0000 |
commit | 0d6fa033121a9bef708b8f2de186c4034c61d4a3 (patch) | |
tree | 851d65a09efbffa114c9a273e590d55cfb1436ab /spec/models | |
parent | 0eb3d2f799ce4f4de87fb9fc6fd98e592323bc89 (diff) | |
download | gitlab-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.rb | 231 | ||||
-rw-r--r-- | spec/models/prometheus_alert_spec.rb | 103 | ||||
-rw-r--r-- | spec/models/push_event_spec.rb | 4 | ||||
-rw-r--r-- | spec/models/user_callout_spec.rb | 33 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 34 |
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) |