summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ability_spec.rb63
-rw-r--r--spec/models/abuse_report_spec.rb1
-rw-r--r--spec/models/alert_management/alert_spec.rb1
-rw-r--r--spec/models/application_setting/term_spec.rb4
-rw-r--r--spec/models/application_setting_spec.rb21
-rw-r--r--spec/models/audit_event_spec.rb65
-rw-r--r--spec/models/award_emoji_spec.rb69
-rw-r--r--spec/models/blob_viewer/markup_spec.rb10
-rw-r--r--spec/models/bulk_import_spec.rb6
-rw-r--r--spec/models/bulk_imports/entity_spec.rb20
-rw-r--r--spec/models/bulk_imports/file_transfer/group_config_spec.rb4
-rw-r--r--spec/models/bulk_imports/file_transfer/project_config_spec.rb4
-rw-r--r--spec/models/chat_name_spec.rb1
-rw-r--r--spec/models/chat_team_spec.rb1
-rw-r--r--spec/models/ci/build_dependencies_spec.rb18
-rw-r--r--spec/models/ci/build_spec.rb92
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb8
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb51
-rw-r--r--spec/models/ci/job_artifact_spec.rb23
-rw-r--r--spec/models/ci/job_token/project_scope_link_spec.rb18
-rw-r--r--spec/models/ci/job_token/scope_spec.rb4
-rw-r--r--spec/models/ci/pending_build_spec.rb58
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb9
-rw-r--r--spec/models/ci/pipeline_spec.rb117
-rw-r--r--spec/models/ci/runner_spec.rb14
-rw-r--r--spec/models/ci/running_build_spec.rb5
-rw-r--r--spec/models/clusters/integrations/prometheus_spec.rb8
-rw-r--r--spec/models/clusters/kubernetes_namespace_spec.rb1
-rw-r--r--spec/models/commit_spec.rb1
-rw-r--r--spec/models/compare_spec.rb10
-rw-r--r--spec/models/concerns/approvable_base_spec.rb21
-rw-r--r--spec/models/concerns/atomic_internal_id_spec.rb18
-rw-r--r--spec/models/concerns/awardable_spec.rb84
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb4
-rw-r--r--spec/models/concerns/cascading_namespace_setting_attribute_spec.rb22
-rw-r--r--spec/models/concerns/has_integrations_spec.rb12
-rw-r--r--spec/models/concerns/integrations/has_data_fields_spec.rb43
-rw-r--r--spec/models/concerns/issuable_spec.rb20
-rw-r--r--spec/models/concerns/partitioned_table_spec.rb12
-rw-r--r--spec/models/concerns/prometheus_adapter_spec.rb36
-rw-r--r--spec/models/container_repository_spec.rb55
-rw-r--r--spec/models/deploy_token_spec.rb1
-rw-r--r--spec/models/deployment_metrics_spec.rb22
-rw-r--r--spec/models/deployment_spec.rb3
-rw-r--r--spec/models/diff_discussion_spec.rb9
-rw-r--r--spec/models/diff_viewer/server_side_spec.rb1
-rw-r--r--spec/models/discussion_spec.rb18
-rw-r--r--spec/models/environment_spec.rb3
-rw-r--r--spec/models/error_tracking/error_event_spec.rb14
-rw-r--r--spec/models/error_tracking/error_spec.rb16
-rw-r--r--spec/models/event_collection_spec.rb1
-rw-r--r--spec/models/event_spec.rb14
-rw-r--r--spec/models/group_spec.rb6
-rw-r--r--spec/models/integration_spec.rb307
-rw-r--r--spec/models/integrations/asana_spec.rb10
-rw-r--r--spec/models/integrations/assembla_spec.rb6
-rw-r--r--spec/models/integrations/bamboo_spec.rb88
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb107
-rw-r--r--spec/models/integrations/base_issue_tracker_spec.rb12
-rw-r--r--spec/models/integrations/bugzilla_spec.rb15
-rw-r--r--spec/models/integrations/buildkite_spec.rb40
-rw-r--r--spec/models/integrations/campfire_spec.rb10
-rw-r--r--spec/models/integrations/confluence_spec.rb13
-rw-r--r--spec/models/integrations/custom_issue_tracker_spec.rb15
-rw-r--r--spec/models/integrations/datadog_spec.rb20
-rw-r--r--spec/models/integrations/discord_spec.rb23
-rw-r--r--spec/models/integrations/drone_ci_spec.rb58
-rw-r--r--spec/models/integrations/emails_on_push_spec.rb14
-rw-r--r--spec/models/integrations/ewm_spec.rb15
-rw-r--r--spec/models/integrations/external_wiki_spec.rb11
-rw-r--r--spec/models/integrations/flowdock_spec.rb10
-rw-r--r--spec/models/integrations/irker_spec.rb10
-rw-r--r--spec/models/integrations/jenkins_spec.rb92
-rw-r--r--spec/models/integrations/jira_spec.rb304
-rw-r--r--spec/models/integrations/mattermost_slash_commands_spec.rb30
-rw-r--r--spec/models/integrations/microsoft_teams_spec.rb57
-rw-r--r--spec/models/integrations/open_project_spec.rb13
-rw-r--r--spec/models/integrations/packagist_spec.rb10
-rw-r--r--spec/models/integrations/pipelines_email_spec.rb4
-rw-r--r--spec/models/integrations/pivotaltracker_spec.rb31
-rw-r--r--spec/models/integrations/prometheus_spec.rb (renamed from spec/models/project_services/prometheus_service_spec.rb)178
-rw-r--r--spec/models/integrations/pushover_spec.rb10
-rw-r--r--spec/models/integrations/redmine_spec.rb15
-rw-r--r--spec/models/integrations/slack_slash_commands_spec.rb8
-rw-r--r--spec/models/integrations/slack_spec.rb8
-rw-r--r--spec/models/integrations/teamcity_spec.rb84
-rw-r--r--spec/models/integrations/youtrack_spec.rb13
-rw-r--r--spec/models/internal_id_spec.rb279
-rw-r--r--spec/models/issue_spec.rb72
-rw-r--r--spec/models/label_note_spec.rb1
-rw-r--r--spec/models/lfs_file_lock_spec.rb1
-rw-r--r--spec/models/member_spec.rb297
-rw-r--r--spec/models/members/group_member_spec.rb21
-rw-r--r--spec/models/members/project_member_spec.rb13
-rw-r--r--spec/models/merge_request/cleanup_schedule_spec.rb133
-rw-r--r--spec/models/merge_request/diff_commit_user_spec.rb127
-rw-r--r--spec/models/merge_request_diff_commit_spec.rb50
-rw-r--r--spec/models/merge_request_diff_spec.rb49
-rw-r--r--spec/models/merge_request_spec.rb40
-rw-r--r--spec/models/milestone_spec.rb73
-rw-r--r--spec/models/namespace/root_storage_statistics_spec.rb1
-rw-r--r--spec/models/namespace_spec.rb85
-rw-r--r--spec/models/note_spec.rb4
-rw-r--r--spec/models/notification_setting_spec.rb1
-rw-r--r--spec/models/operations/feature_flag_spec.rb1
-rw-r--r--spec/models/packages/package_file_spec.rb7
-rw-r--r--spec/models/packages/package_spec.rb33
-rw-r--r--spec/models/plan_limits_spec.rb2
-rw-r--r--spec/models/plan_spec.rb23
-rw-r--r--spec/models/project_ci_cd_setting_spec.rb4
-rw-r--r--spec/models/project_spec.rb248
-rw-r--r--spec/models/prometheus_alert_spec.rb1
-rw-r--r--spec/models/protected_branch/push_access_level_spec.rb1
-rw-r--r--spec/models/repository_spec.rb5
-rw-r--r--spec/models/service_desk_setting_spec.rb2
-rw-r--r--spec/models/snippet_repository_spec.rb1
-rw-r--r--spec/models/snippet_spec.rb9
-rw-r--r--spec/models/terraform/state_spec.rb1
-rw-r--r--spec/models/timelog_spec.rb2
-rw-r--r--spec/models/u2f_registration_spec.rb1
-rw-r--r--spec/models/user_spec.rb111
-rw-r--r--spec/models/wiki_page_spec.rb5
122 files changed, 2625 insertions, 1862 deletions
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 4bfa953df40..e131661602e 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -328,6 +328,69 @@ RSpec.describe Ability do
end
end
+ describe '.feature_flags_readable_by_user' do
+ context 'without a user' do
+ it 'returns no feature flags' do
+ feature_flag_1 = build(:operations_feature_flag)
+ feature_flag_2 = build(:operations_feature_flag, project: build(:project, :public))
+
+ feature_flags = described_class
+ .feature_flags_readable_by_user([feature_flag_1, feature_flag_2])
+
+ expect(feature_flags).to eq([])
+ end
+ end
+
+ context 'with a user' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:feature_flag) { create(:operations_feature_flag, project: project) }
+ let(:cross_project) { create(:project) }
+ let(:cross_project_feature_flag) { create(:operations_feature_flag, project: cross_project) }
+
+ let(:other_feature_flag) { create(:operations_feature_flag) }
+ let(:all_feature_flags) do
+ [feature_flag, cross_project_feature_flag, other_feature_flag]
+ end
+
+ subject(:readable_feature_flags) do
+ described_class.feature_flags_readable_by_user(all_feature_flags, user)
+ end
+
+ before do
+ project.add_developer(user)
+ cross_project.add_developer(user)
+ end
+
+ it 'returns feature flags visible to the user' do
+ expect(readable_feature_flags).to contain_exactly(feature_flag, cross_project_feature_flag)
+ end
+
+ context 'when a user cannot read cross project and a filter is passed' do
+ before do
+ allow(described_class).to receive(:allowed?).and_call_original
+ expect(described_class).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ subject(:readable_feature_flags) do
+ read_cross_project_filter = -> (feature_flags) do
+ feature_flags.select { |flag| flag.project == project }
+ end
+ described_class.feature_flags_readable_by_user(
+ all_feature_flags, user,
+ filters: { read_cross_project: read_cross_project_filter }
+ )
+ end
+
+ it 'returns only feature flags of the specified project without checking access on others' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_feature_flag, cross_project_feature_flag)
+
+ expect(readable_feature_flags).to contain_exactly(feature_flag)
+ end
+ end
+ end
+ end
+
describe '.project_disabled_features_rules' do
let(:project) { create(:project, :wiki_disabled) }
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index a97574fa524..e87996fc1f0 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe AbuseReport do
let_it_be(:report, reload: true) { create(:abuse_report) }
let_it_be(:user, reload: true) { create(:admin) }
+
subject { report }
it { expect(subject).to be_valid }
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index 80a45b1c1be..18d486740b8 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -100,6 +100,7 @@ RSpec.describe AlertManagement::Alert do
describe 'fingerprint' do
let_it_be(:fingerprint) { 'fingerprint' }
let_it_be(:project3, refind: true) { create(:project) }
+
let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project3) }
subject { new_alert }
diff --git a/spec/models/application_setting/term_spec.rb b/spec/models/application_setting/term_spec.rb
index 51a6027698f..d9efa597352 100644
--- a/spec/models/application_setting/term_spec.rb
+++ b/spec/models/application_setting/term_spec.rb
@@ -3,9 +3,7 @@
require 'spec_helper'
RSpec.describe ApplicationSetting::Term do
- describe 'validations' do
- it { is_expected.to validate_presence_of(:terms) }
- end
+ it { is_expected.to nullify_if_blank(:terms) }
describe '.latest' do
it 'finds the latest terms' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 4e72d558b52..80471a09bbd 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -134,6 +134,14 @@ RSpec.describe ApplicationSetting do
it { is_expected.to allow_value('disabled').for(:whats_new_variant) }
it { is_expected.not_to allow_value(nil).for(:whats_new_variant) }
+ it { is_expected.not_to allow_value(['']).for(:valid_runner_registrars) }
+ it { is_expected.not_to allow_value(['OBVIOUSLY_WRONG']).for(:valid_runner_registrars) }
+ it { is_expected.not_to allow_value(%w(project project)).for(:valid_runner_registrars) }
+ it { is_expected.not_to allow_value([nil]).for(:valid_runner_registrars) }
+ it { is_expected.not_to allow_value(nil).for(:valid_runner_registrars) }
+ it { is_expected.to allow_value([]).for(:valid_runner_registrars) }
+ it { is_expected.to allow_value(%w(project group)).for(:valid_runner_registrars) }
+
context 'help_page_documentation_base_url validations' do
it { is_expected.to allow_value(nil).for(:help_page_documentation_base_url) }
it { is_expected.to allow_value('https://docs.gitlab.com').for(:help_page_documentation_base_url) }
@@ -250,6 +258,19 @@ RSpec.describe ApplicationSetting do
it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) }
end
+ context 'when mailgun_events_enabled is enabled' do
+ before do
+ setting.mailgun_events_enabled = true
+ end
+
+ it { is_expected.to validate_presence_of(:mailgun_signing_key) }
+ it { is_expected.to validate_length_of(:mailgun_signing_key).is_at_most(255) }
+ end
+
+ context 'when mailgun_events_enabled is not enabled' do
+ it { is_expected.not_to validate_presence_of(:mailgun_signing_key) }
+ end
+
context "when user accepted let's encrypt terms of service" do
before do
expect do
diff --git a/spec/models/audit_event_spec.rb b/spec/models/audit_event_spec.rb
index bc603bc5ab6..4fba5fddc92 100644
--- a/spec/models/audit_event_spec.rb
+++ b/spec/models/audit_event_spec.rb
@@ -10,6 +10,71 @@ RSpec.describe AuditEvent do
end
end
+ describe 'callbacks' do
+ describe '#parallel_persist' do
+ shared_examples 'a parallel persisted field' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:column, :details, :expected_value) do
+ :value | nil | :value
+ nil | :value | :value
+ :value | :another_value | :value
+ nil | nil | nil
+ end
+
+ with_them do
+ let(:values) { { value: value, another_value: "#{value}88" } }
+
+ let(:audit_event) do
+ build(:audit_event, name => values[column], details: { name => values[details] })
+ end
+
+ it 'sets both values to be the same', :aggregate_failures do
+ audit_event.validate
+
+ expect(audit_event[name]).to eq(values[expected_value])
+ expect(audit_event.details[name]).to eq(values[expected_value])
+ end
+ end
+ end
+
+ context 'wih author_name' do
+ let(:name) { :author_name }
+ let(:value) { 'Mary Poppins' }
+
+ it_behaves_like 'a parallel persisted field'
+ end
+
+ context 'with entity_path' do
+ let(:name) { :entity_path }
+ let(:value) { 'gitlab-org' }
+
+ it_behaves_like 'a parallel persisted field'
+ end
+
+ context 'with target_details' do
+ let(:name) { :target_details }
+ let(:value) { 'gitlab-org/gitlab' }
+
+ it_behaves_like 'a parallel persisted field'
+ end
+
+ context 'with target_type' do
+ let(:name) { :target_type }
+ let(:value) { 'Project' }
+
+ it_behaves_like 'a parallel persisted field'
+ end
+
+ context 'with target_id' do
+ let(:name) { :target_id }
+ let(:value) { 8 }
+
+ it_behaves_like 'a parallel persisted field'
+ end
+ end
+ end
+
it 'sanitizes custom_message in the details hash' do
audit_event = create(:project_audit_event, details: { target_id: 678, custom_message: '<strong>Arnold</strong>' })
diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb
index f268408c095..ebd1441f901 100644
--- a/spec/models/award_emoji_spec.rb
+++ b/spec/models/award_emoji_spec.rb
@@ -119,6 +119,36 @@ RSpec.describe AwardEmoji do
end
end
+ describe 'bumping updated at' do
+ let(:note) { create(:note_on_issue) }
+ let(:award_emoji) { build(:award_emoji, user: build(:user), awardable: note) }
+
+ it 'calls bump_updated_at on the note when saved' do
+ expect(note).to receive(:bump_updated_at)
+
+ award_emoji.save!
+ end
+
+ it 'calls bump_updated_at on the note when destroyed' do
+ expect(note).to receive(:bump_updated_at)
+
+ award_emoji.destroy!
+ end
+
+ context 'on another awardable' do
+ let(:issue) { create(:issue) }
+ let(:award_emoji) { build(:award_emoji, user: build(:user), awardable: issue) }
+
+ it 'does not error out when saved' do
+ expect { award_emoji.save! }.not_to raise_error
+ end
+
+ it 'does not error out when destroy' do
+ expect { award_emoji.destroy! }.not_to raise_error
+ end
+ end
+ end
+
describe '.award_counts_for_user' do
let(:user) { create(:user) }
@@ -141,4 +171,43 @@ RSpec.describe AwardEmoji do
expect(awards).to eq('thumbsup' => 2)
end
end
+
+ describe 'updating upvotes_count' do
+ context 'on an issue' do
+ let(:issue) { create(:issue) }
+ let(:upvote) { build(:award_emoji, :upvote, user: build(:user), awardable: issue) }
+ let(:downvote) { build(:award_emoji, :downvote, user: build(:user), awardable: issue) }
+
+ it 'updates upvotes_count on the issue when saved' do
+ expect(issue).to receive(:update_column).with(:upvotes_count, 1).once
+
+ upvote.save!
+ downvote.save!
+ end
+
+ it 'updates upvotes_count on the issue when destroyed' do
+ expect(issue).to receive(:update_column).with(:upvotes_count, 0).once
+
+ upvote.destroy!
+ downvote.destroy!
+ end
+ end
+
+ context 'on another awardable' do
+ let(:merge_request) { create(:merge_request) }
+ let(:award_emoji) { build(:award_emoji, user: build(:user), awardable: merge_request) }
+
+ it 'does not update upvotes_count on the merge_request when saved' do
+ expect(merge_request).not_to receive(:update_column)
+
+ award_emoji.save!
+ end
+
+ it 'does not update upvotes_count on the merge_request when destroyed' do
+ expect(merge_request).not_to receive(:update_column)
+
+ award_emoji.destroy!
+ end
+ end
+ end
end
diff --git a/spec/models/blob_viewer/markup_spec.rb b/spec/models/blob_viewer/markup_spec.rb
index 13b040d62d0..dae1b79dda2 100644
--- a/spec/models/blob_viewer/markup_spec.rb
+++ b/spec/models/blob_viewer/markup_spec.rb
@@ -24,15 +24,5 @@ RSpec.describe BlobViewer::Markup do
expect(subject.banzai_render_context.keys).to include(:rendered)
end
end
-
- context 'when cached_markdown_blob feature flag is disabled' do
- before do
- stub_feature_flags(cached_markdown_blob: false)
- end
-
- it 'does not set cache_key key' do
- expect(subject.banzai_render_context.keys).not_to include(:cache_key)
- end
- end
end
end
diff --git a/spec/models/bulk_import_spec.rb b/spec/models/bulk_import_spec.rb
index 1a7e1ed8119..4cfec6b20b7 100644
--- a/spec/models/bulk_import_spec.rb
+++ b/spec/models/bulk_import_spec.rb
@@ -15,4 +15,10 @@ RSpec.describe BulkImport, type: :model do
it { is_expected.to define_enum_for(:source_type).with_values(%i[gitlab]) }
end
+
+ describe '.all_human_statuses' do
+ it 'returns all human readable entity statuses' do
+ expect(described_class.all_human_statuses).to contain_exactly('created', 'started', 'finished', 'failed')
+ end
+ end
end
diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb
index d1b7125a6e6..11a3e53dd16 100644
--- a/spec/models/bulk_imports/entity_spec.rb
+++ b/spec/models/bulk_imports/entity_spec.rb
@@ -134,4 +134,24 @@ RSpec.describe BulkImports::Entity, type: :model do
expect(entity.encoded_source_full_path).to eq(expected)
end
end
+
+ describe 'scopes' do
+ describe '.by_user_id' do
+ it 'returns entities associated with specified user' do
+ user = create(:user)
+ import = create(:bulk_import, user: user)
+ entity_1 = create(:bulk_import_entity, bulk_import: import)
+ entity_2 = create(:bulk_import_entity, bulk_import: import)
+ create(:bulk_import_entity)
+
+ expect(described_class.by_user_id(user.id)).to contain_exactly(entity_1, entity_2)
+ end
+ end
+ end
+
+ describe '.all_human_statuses' do
+ it 'returns all human readable entity statuses' do
+ expect(described_class.all_human_statuses).to contain_exactly('created', 'started', 'finished', 'failed')
+ end
+ end
end
diff --git a/spec/models/bulk_imports/file_transfer/group_config_spec.rb b/spec/models/bulk_imports/file_transfer/group_config_spec.rb
index 4611a00b0cc..1e566a7b042 100644
--- a/spec/models/bulk_imports/file_transfer/group_config_spec.rb
+++ b/spec/models/bulk_imports/file_transfer/group_config_spec.rb
@@ -34,6 +34,10 @@ RSpec.describe BulkImports::FileTransfer::GroupConfig do
it 'returns a list of top level exportable relations' do
expect(subject.portable_relations).to include('milestones', 'badges', 'boards', 'labels')
end
+
+ it 'does not include skipped relations' do
+ expect(subject.portable_relations).not_to include('members')
+ end
end
describe '#top_relation_tree' do
diff --git a/spec/models/bulk_imports/file_transfer/project_config_spec.rb b/spec/models/bulk_imports/file_transfer/project_config_spec.rb
index 2995556a58d..db037528ec1 100644
--- a/spec/models/bulk_imports/file_transfer/project_config_spec.rb
+++ b/spec/models/bulk_imports/file_transfer/project_config_spec.rb
@@ -34,6 +34,10 @@ RSpec.describe BulkImports::FileTransfer::ProjectConfig do
it 'returns a list of top level exportable relations' do
expect(subject.portable_relations).to include('issues', 'labels', 'milestones', 'merge_requests')
end
+
+ it 'does not include skipped relations' do
+ expect(subject.portable_relations).not_to include('project_members', 'group_members')
+ end
end
describe '#top_relation_tree' do
diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb
index 4d77bd53158..9ed00003ac1 100644
--- a/spec/models/chat_name_spec.rb
+++ b/spec/models/chat_name_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe ChatName do
let_it_be(:chat_name) { create(:chat_name) }
+
subject { chat_name }
it { is_expected.to belong_to(:integration) }
diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb
index 08fd05324aa..2e8cdb7a316 100644
--- a/spec/models/chat_team_spec.rb
+++ b/spec/models/chat_team_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe ChatTeam do
let_it_be(:chat_team) { create(:chat_team) }
+
subject { chat_team }
# Associations
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index 331ba9953ca..cd330324840 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -55,6 +55,24 @@ RSpec.describe Ci::BuildDependencies do
end
end
end
+
+ context 'when needs refer to jobs from the same stage' do
+ let(:job) do
+ create(:ci_build,
+ pipeline: pipeline,
+ name: 'dag_job',
+ scheduling_type: :dag,
+ stage_idx: 2,
+ stage: 'deploy'
+ )
+ end
+
+ before do
+ create(:ci_build_need, build: job, name: 'staging', artifacts: true)
+ end
+
+ it { is_expected.to contain_exactly(staging) }
+ end
end
describe 'jobs from specified dependencies' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 62dec522161..0c344270e0b 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -39,6 +39,34 @@ RSpec.describe Ci::Build do
it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) }
it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
+ shared_examples 'calling proper BuildFinishedWorker' do
+ context 'when ci_build_finished_worker_namespace_changed feature flag enabled' do
+ before do
+ stub_feature_flags(ci_build_finished_worker_namespace_changed: build.project)
+ end
+
+ it 'calls Ci::BuildFinishedWorker' do
+ expect(Ci::BuildFinishedWorker).to receive(:perform_async)
+ expect(::BuildFinishedWorker).not_to receive(:perform_async)
+
+ subject
+ end
+ end
+
+ context 'when ci_build_finished_worker_namespace_changed feature flag disabled' do
+ before do
+ stub_feature_flags(ci_build_finished_worker_namespace_changed: false)
+ end
+
+ it 'calls ::BuildFinishedWorker' do
+ expect(::BuildFinishedWorker).to receive(:perform_async)
+ expect(Ci::BuildFinishedWorker).not_to receive(:perform_async)
+
+ subject
+ end
+ end
+ end
+
describe 'associations' do
it 'has a bidirectional relationship with projects' do
expect(described_class.reflect_on_association(:project).has_inverse?).to eq(:builds)
@@ -384,7 +412,7 @@ RSpec.describe Ci::Build do
context 'when there is a queuing entry already present' do
before do
- ::Ci::PendingBuild.create!(build: build, project: build.project)
+ create(:ci_pending_build, build: build, project: build.project)
end
it 'does not raise an error' do
@@ -396,7 +424,7 @@ RSpec.describe Ci::Build do
context 'when both failure scenario happen at the same time' do
before do
::Ci::Build.find(build.id).update_column(:lock_version, 100)
- ::Ci::PendingBuild.create!(build: build, project: build.project)
+ create(:ci_pending_build, build: build, project: build.project)
end
it 'raises stale object error exception' do
@@ -478,7 +506,7 @@ RSpec.describe Ci::Build do
let(:build) { create(:ci_build, :pending) }
before do
- ::Ci::PendingBuild.create!(build: build, project: build.project)
+ create(:ci_pending_build, build: build, project: build.project)
::Ci::Build.find(build.id).update_column(:lock_version, 100)
end
@@ -1323,6 +1351,7 @@ RSpec.describe Ci::Build do
end
it_behaves_like 'avoid deadlock'
+ it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to success' do
subject
@@ -1335,6 +1364,7 @@ RSpec.describe Ci::Build do
let(:event) { :drop! }
it_behaves_like 'avoid deadlock'
+ it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to failed' do
subject
@@ -1359,6 +1389,7 @@ RSpec.describe Ci::Build do
let(:event) { :cancel! }
it_behaves_like 'avoid deadlock'
+ it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to canceled' do
subject
@@ -1966,6 +1997,23 @@ RSpec.describe Ci::Build do
end
end
+ describe '#tag_list' do
+ let_it_be(:build) { create(:ci_build, tag_list: ['tag']) }
+
+ context 'when tags are preloaded' do
+ it 'does not trigger queries' do
+ build_with_tags = described_class.eager_load_tags.id_in([build]).to_a.first
+
+ expect { build_with_tags.tag_list }.not_to exceed_all_query_limit(0)
+ expect(build_with_tags.tag_list).to eq(['tag'])
+ end
+ end
+
+ context 'when tags are not preloaded' do
+ it { expect(described_class.find(build.id).tag_list).to eq(['tag']) }
+ end
+ end
+
describe '#has_tags?' do
context 'when build has tags' do
subject { create(:ci_build, tag_list: ['tag']) }
@@ -2155,15 +2203,15 @@ RSpec.describe Ci::Build do
end
it 'contains options' do
- expect(build.options).to eq(options.stringify_keys)
+ expect(build.options).to eq(options.symbolize_keys)
end
- it 'allows to access with keys' do
+ it 'allows to access with symbolized keys' do
expect(build.options[:image]).to eq('ruby:2.7')
end
- it 'allows to access with strings' do
- expect(build.options['image']).to eq('ruby:2.7')
+ it 'rejects access with string keys' do
+ expect(build.options['image']).to be_nil
end
context 'when ci_build_metadata_config is set' do
@@ -2172,7 +2220,7 @@ RSpec.describe Ci::Build do
end
it 'persist data in build metadata' do
- expect(build.metadata.read_attribute(:config_options)).to eq(options.stringify_keys)
+ expect(build.metadata.read_attribute(:config_options)).to eq(options.symbolize_keys)
end
it 'does not persist data in build' do
@@ -4476,26 +4524,12 @@ RSpec.describe Ci::Build do
it { is_expected.to include(:upload_multiple_artifacts) }
end
- context 'when artifacts exclude is defined and the is feature enabled' do
+ context 'when artifacts exclude is defined' do
let(:options) do
{ artifacts: { exclude: %w[something] } }
end
- context 'when a feature flag is enabled' do
- before do
- stub_feature_flags(ci_artifacts_exclude: true)
- end
-
- it { is_expected.to include(:artifacts_exclude) }
- end
-
- context 'when a feature flag is disabled' do
- before do
- stub_feature_flags(ci_artifacts_exclude: false)
- end
-
- it { is_expected.not_to include(:artifacts_exclude) }
- end
+ it { is_expected.to include(:artifacts_exclude) }
end
end
@@ -4712,9 +4746,9 @@ RSpec.describe Ci::Build do
describe '#read_metadata_attribute' do
let(:build) { create(:ci_build, :degenerated) }
- let(:build_options) { { "key" => "build" } }
- let(:metadata_options) { { "key" => "metadata" } }
- let(:default_options) { { "key" => "default" } }
+ let(:build_options) { { key: "build" } }
+ let(:metadata_options) { { key: "metadata" } }
+ let(:default_options) { { key: "default" } }
subject { build.send(:read_metadata_attribute, :options, :config_options, default_options) }
@@ -4749,8 +4783,8 @@ RSpec.describe Ci::Build do
describe '#write_metadata_attribute' do
let(:build) { create(:ci_build, :degenerated) }
- let(:options) { { "key" => "new options" } }
- let(:existing_options) { { "key" => "existing options" } }
+ let(:options) { { key: "new options" } }
+ let(:existing_options) { { key: "existing options" } }
subject { build.send(:write_metadata_attribute, :options, :config_options, options) }
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index a16453f3d01..b6e128c317c 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -152,14 +152,6 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
context 'default value' do
it { expect(subject).to eq('redis_trace_chunks') }
-
- context 'when dedicated_redis_trace_chunks is disabled' do
- before do
- stub_feature_flags(dedicated_redis_trace_chunks: false)
- end
-
- it { expect(subject).to eq('redis') }
- end
end
end
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index d9e9533fb26..21dab6fad60 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -102,6 +102,57 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
end
end
+ describe '#append_data' do
+ let(:initial_data) { (+'😺').force_encoding(Encoding::ASCII_8BIT) }
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: initial_data) }
+ let(:data) { data_store.data(model) }
+
+ context 'when ci_job_trace_force_encode is enabled' do
+ it 'appends ASCII data' do
+ data_store.append_data(model, +'hello world', 4)
+
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
+ end
+
+ it 'appends UTF-8 data' do
+ data_store.append_data(model, +'Résumé', 4)
+
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq("😺Résumé")
+ end
+
+ context 'when initial data is UTF-8' do
+ let(:initial_data) { +'😺' }
+
+ it 'appends ASCII data' do
+ data_store.append_data(model, +'hello world', 4)
+
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
+ end
+ end
+ end
+
+ context 'when ci_job_trace_force_encode is disabled' do
+ before do
+ stub_feature_flags(ci_job_trace_force_encode: false)
+ end
+
+ it 'appends ASCII data' do
+ data_store.append_data(model, +'hello world', 4)
+
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
+ end
+
+ it 'throws an exception when appending UTF-8 data' do
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception).and_call_original
+ expect { data_store.append_data(model, +'Résumé', 4) }.to raise_exception(Encoding::CompatibilityError)
+ end
+ end
+ end
+
describe '#delete_data' do
subject { data_store.delete_data(model) }
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 582639b105e..a94a1dd284a 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -268,6 +268,29 @@ RSpec.describe Ci::JobArtifact do
end
end
+ describe '.for_project' do
+ it 'returns artifacts only for given project(s)', :aggregate_failures do
+ artifact1 = create(:ci_job_artifact)
+ artifact2 = create(:ci_job_artifact)
+ create(:ci_job_artifact)
+
+ expect(described_class.for_project(artifact1.project)).to match_array([artifact1])
+ expect(described_class.for_project([artifact1.project, artifact2.project])).to match_array([artifact1, artifact2])
+ end
+ end
+
+ describe 'created_in_time_range' do
+ it 'returns artifacts created in given time range', :aggregate_failures do
+ artifact1 = create(:ci_job_artifact, created_at: 1.day.ago)
+ artifact2 = create(:ci_job_artifact, created_at: 1.month.ago)
+ artifact3 = create(:ci_job_artifact, created_at: 1.year.ago)
+
+ expect(described_class.created_in_time_range(from: 1.week.ago)).to match_array([artifact1])
+ expect(described_class.created_in_time_range(to: 1.week.ago)).to match_array([artifact2, artifact3])
+ expect(described_class.created_in_time_range(from: 2.months.ago, to: 1.week.ago)).to match_array([artifact2])
+ end
+ end
+
describe 'callbacks' do
describe '#schedule_background_upload' do
subject { create(:ci_job_artifact, :archive) }
diff --git a/spec/models/ci/job_token/project_scope_link_spec.rb b/spec/models/ci/job_token/project_scope_link_spec.rb
index d18495b9312..dd6a75dfd89 100644
--- a/spec/models/ci/job_token/project_scope_link_spec.rb
+++ b/spec/models/ci/job_token/project_scope_link_spec.rb
@@ -65,4 +65,22 @@ RSpec.describe Ci::JobToken::ProjectScopeLink do
expect(subject).to contain_exactly(target_link)
end
end
+
+ describe '.for_source_and_target' do
+ let_it_be(:link) { create(:ci_job_token_project_scope_link, source_project: project) }
+
+ subject { described_class.for_source_and_target(project, target_project) }
+
+ context 'when link is found' do
+ let(:target_project) { link.target_project }
+
+ it { is_expected.to eq(link) }
+ end
+
+ context 'when link is not found' do
+ let(:target_project) { create(:project) }
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb
index c731a2634f5..4b95adf8476 100644
--- a/spec/models/ci/job_token/scope_spec.rb
+++ b/spec/models/ci/job_token/scope_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::JobToken::Scope do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let(:scope) { described_class.new(project) }
@@ -29,7 +29,7 @@ RSpec.describe Ci::JobToken::Scope do
end
end
- describe 'includes?' do
+ describe '#includes?' do
subject { scope.includes?(target_project) }
context 'when param is the project defining the scope' do
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index c1d4f4b0a5e..b64f3999232 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Ci::PendingBuild do
context 'when another queuing entry exists for given build' do
before do
- described_class.create!(build: build, project: project, protected: false)
+ create(:ci_pending_build, build: build, project: project)
end
it 'returns a build id as a result' do
@@ -29,5 +29,61 @@ RSpec.describe Ci::PendingBuild do
expect(result.rows.dig(0, 0)).to eq build.id
end
end
+
+ context 'when project does not have shared runner' do
+ it 'sets instance_runners_enabled to false' do
+ described_class.upsert_from_build!(build)
+
+ expect(described_class.last.instance_runners_enabled).to be_falsey
+ end
+ end
+
+ context 'when project has shared runner' do
+ let_it_be(:runner) { create(:ci_runner, :instance) }
+
+ context 'when ci_pending_builds_maintain_shared_runners_data is enabled' do
+ it 'sets instance_runners_enabled to true' do
+ described_class.upsert_from_build!(build)
+
+ expect(described_class.last.instance_runners_enabled).to be_truthy
+ end
+
+ context 'when project is about to be deleted' do
+ before do
+ build.project.update!(pending_delete: true)
+ end
+
+ it 'sets instance_runners_enabled to false' do
+ described_class.upsert_from_build!(build)
+
+ expect(described_class.last.instance_runners_enabled).to be_falsey
+ end
+ end
+
+ context 'when builds are disabled' do
+ before do
+ build.project.project_feature.update!(builds_access_level: false)
+ end
+
+ it 'sets instance_runners_enabled to false' do
+ described_class.upsert_from_build!(build)
+
+ expect(described_class.last.instance_runners_enabled).to be_falsey
+ end
+ end
+ end
+
+ context 'when ci_pending_builds_maintain_shared_runners_data is disabled' do
+ before do
+ stub_feature_flags(ci_pending_builds_maintain_shared_runners_data: false)
+ end
+
+ it 'sets instance_runners_enabled to false' do
+ described_class.upsert_from_build!(build)
+
+ expect(described_class.last.instance_runners_enabled).to be_falsey
+ end
+ end
+ end
end
end
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index cf73460bf1e..8de3ebb18b9 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -123,8 +123,15 @@ RSpec.describe Ci::PipelineSchedule do
'*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
'*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 2.hours.in_minutes).to_i | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
'*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
+ '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 10).to_i | true | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
+ '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 8).to_i | true | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
'*/5 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 1, 1, 0) | Time.zone.local(2021, 6, 1, 1, 0)
+ '*/9 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0)
+ '*/9 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | false | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 9)
+ '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
+ '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | false | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
+ '*/5 * * * *' | '45 21 1 2 *' | (1.day.in_minutes / 5).to_i | true | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45)
+ '*/5 * * * *' | '45 21 1 2 *' | (1.day.in_minutes / 5).to_i | false | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 50)
end
with_them do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 26fc4b140c1..74a476a6422 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let_it_be(:namespace) { create_default(:namespace).freeze }
let_it_be(:project) { create_default(:project, :repository).freeze }
+ it 'paginates 15 pipeleines per page' do
+ expect(described_class.default_per_page).to eq(15)
+ end
+
it_behaves_like 'having unique enum values'
it { is_expected.to belong_to(:project) }
@@ -2768,6 +2772,41 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(control2.count).to eq(control1.count + extra_update_queries + extra_generic_commit_status_validation_queries)
end
end
+
+ context 'when the first try cannot get an exclusive lock' do
+ let(:retries) { 1 }
+
+ subject(:cancel_running) { pipeline.cancel_running(retries: retries) }
+
+ before do
+ build = create(:ci_build, :running, pipeline: pipeline)
+
+ allow(pipeline.cancelable_statuses).to receive(:find_in_batches).and_yield([build])
+
+ call_count = 0
+ allow(build).to receive(:cancel).and_wrap_original do |original, *args|
+ call_count >= retries ? raise(ActiveRecord::StaleObjectError) : original.call(*args)
+
+ call_count += 1
+ end
+ end
+
+ it 'retries again and cancels the build' do
+ cancel_running
+
+ expect(latest_status).to contain_exactly('canceled')
+ end
+
+ context 'when the retries parameter is 0' do
+ let(:retries) { 0 }
+
+ it 'raises error' do
+ expect do
+ cancel_running
+ end.to raise_error(ActiveRecord::StaleObjectError)
+ end
+ end
+ end
end
describe '#retry_failed' do
@@ -2854,7 +2893,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'builds hook data once' do
- create(:pipelines_email_service)
+ create(:pipelines_email_integration)
expect(Gitlab::DataBuilder::Pipeline).to receive(:build).once.and_call_original
@@ -3772,16 +3811,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it 'can generate a codequality report' do
expect(subject).to be_truthy
end
-
- context 'when feature is disabled' do
- before do
- stub_feature_flags(codequality_mr_diff: false)
- end
-
- it 'can not generate a codequality report' do
- expect(subject).to be_falsey
- end
- end
end
end
@@ -4355,16 +4384,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#base_and_ancestors' do
- subject { pipeline.base_and_ancestors(same_project: same_project) }
+ describe '#self_and_upstreams' do
+ subject(:self_and_upstreams) { pipeline.self_and_upstreams }
let_it_be(:pipeline) { create(:ci_pipeline, :created) }
- let(:same_project) { false }
-
context 'when pipeline is not child nor parent' do
it 'returns just the pipeline itself' do
- expect(subject).to contain_exactly(pipeline)
+ expect(self_and_upstreams).to contain_exactly(pipeline)
end
end
@@ -4378,7 +4405,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns parent and self' do
- expect(subject).to contain_exactly(parent, pipeline)
+ expect(self_and_upstreams).to contain_exactly(parent, pipeline)
end
end
@@ -4390,7 +4417,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns self' do
- expect(subject).to contain_exactly(pipeline)
+ expect(self_and_upstreams).to contain_exactly(pipeline)
end
end
@@ -4406,11 +4433,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns self, parent and ancestor' do
- expect(subject).to contain_exactly(ancestor, parent, pipeline)
+ expect(self_and_upstreams).to contain_exactly(ancestor, parent, pipeline)
end
end
- context 'when pipeline is a triggered pipeline' do
+ context 'when pipeline is a triggered pipeline from a different project' do
let_it_be(:pipeline) { create(:ci_pipeline, :created) }
let(:upstream) { create(:ci_pipeline, project: create(:project)) }
@@ -4419,18 +4446,41 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
create_source_pipeline(upstream, pipeline)
end
- context 'same_project: false' do
- it 'returns upstream and self' do
- expect(subject).to contain_exactly(pipeline, upstream)
- end
+ it 'returns upstream and self' do
+ expect(self_and_upstreams).to contain_exactly(pipeline, upstream)
end
+ end
+ end
- context 'same_project: true' do
- let(:same_project) { true }
+ describe '#self_and_ancestors' do
+ subject(:self_and_ancestors) { pipeline.self_and_ancestors }
- it 'returns self' do
- expect(subject).to contain_exactly(pipeline)
- end
+ context 'when pipeline is child' do
+ let(:pipeline) { create(:ci_pipeline, :created) }
+ let(:parent) { create(:ci_pipeline) }
+ let(:sibling) { create(:ci_pipeline) }
+
+ before do
+ create_source_pipeline(parent, pipeline)
+ create_source_pipeline(parent, sibling)
+ end
+
+ it 'returns parent and self' do
+ expect(self_and_ancestors).to contain_exactly(parent, pipeline)
+ end
+ end
+
+ context 'when pipeline is a triggered pipeline from a different project' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+
+ let(:upstream) { create(:ci_pipeline, project: create(:project)) }
+
+ before do
+ create_source_pipeline(upstream, pipeline)
+ end
+
+ it 'returns only self' do
+ expect(self_and_ancestors).to contain_exactly(pipeline)
end
end
end
@@ -4468,15 +4518,18 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when the parent pipeline has a dependent upstream pipeline' do
- let!(:upstream_bridge) do
- create_bridge(create(:ci_pipeline, project: create(:project)), parent_pipeline, true)
- end
+ let(:upstream_pipeline) { create(:ci_pipeline, project: create(:project)) }
+ let!(:upstream_bridge) { create_bridge(upstream_pipeline, parent_pipeline, true) }
+
+ let(:upstream_upstream_pipeline) { create(:ci_pipeline, project: create(:project)) }
+ let!(:upstream_upstream_bridge) { create_bridge(upstream_upstream_pipeline, upstream_pipeline, true) }
it 'marks all source bridges as pending' do
reset_bridge
expect(bridge.reload).to be_pending
expect(upstream_bridge.reload).to be_pending
+ expect(upstream_upstream_bridge.reload).to be_pending
end
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 61f80bd43b1..ffc8ab4cf8b 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1086,6 +1086,18 @@ RSpec.describe Ci::Runner do
expect(matchers.map(&:tag_list)).to match_array([%w[tag1 tag2], %w[tag3 tag4]])
end
end
+
+ context 'with runner_ids' do
+ before do
+ create_list(:ci_runner, 2)
+ end
+
+ it 'includes runner_ids' do
+ expect(matchers.size).to eq(1)
+
+ expect(matchers.first.runner_ids).to match_array(described_class.all.pluck(:id))
+ end
+ end
end
describe '#runner_matcher' do
@@ -1095,6 +1107,8 @@ RSpec.describe Ci::Runner do
subject(:matcher) { runner.runner_matcher }
+ it { expect(matcher.runner_ids).to eq([runner.id]) }
+
it { expect(matcher.runner_type).to eq(runner.runner_type) }
it { expect(matcher.public_projects_minutes_cost_factor).to eq(runner.public_projects_minutes_cost_factor) }
diff --git a/spec/models/ci/running_build_spec.rb b/spec/models/ci/running_build_spec.rb
index 589e5a86f4d..629861e35b8 100644
--- a/spec/models/ci/running_build_spec.rb
+++ b/spec/models/ci/running_build_spec.rb
@@ -21,10 +21,7 @@ RSpec.describe Ci::RunningBuild do
context 'when another queuing entry exists for given build' do
before do
- described_class.create!(build: build,
- project: project,
- runner: runner,
- runner_type: runner.runner_type)
+ create(:ci_running_build, build: build, project: project, runner: runner)
end
it 'returns a build id as a result' do
diff --git a/spec/models/clusters/integrations/prometheus_spec.rb b/spec/models/clusters/integrations/prometheus_spec.rb
index 680786189ad..e529c751889 100644
--- a/spec/models/clusters/integrations/prometheus_spec.rb
+++ b/spec/models/clusters/integrations/prometheus_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Clusters::Integrations::Prometheus do
let(:cluster) { create(:cluster, :with_installed_helm) }
- it 'deactivates prometheus_service' do
+ it 'deactivates prometheus_integration' do
expect(Clusters::Applications::DeactivateServiceWorker)
.to receive(:perform_async).with(cluster.id, 'prometheus')
@@ -35,7 +35,7 @@ RSpec.describe Clusters::Integrations::Prometheus do
let(:enabled) { true }
context 'when no change to enabled status' do
- it 'does not touch project services' do
+ it 'does not touch project integrations' do
integration # ensure integration exists before we set the expectations
expect(Clusters::Applications::DeactivateServiceWorker)
@@ -51,7 +51,7 @@ RSpec.describe Clusters::Integrations::Prometheus do
context 'when enabling' do
let(:enabled) { false }
- it 'deactivates prometheus_service' do
+ it 'deactivates prometheus_integration' do
expect(Clusters::Applications::ActivateServiceWorker)
.to receive(:perform_async).with(cluster.id, 'prometheus')
@@ -62,7 +62,7 @@ RSpec.describe Clusters::Integrations::Prometheus do
context 'when disabling' do
let(:enabled) { true }
- it 'activates prometheus_service' do
+ it 'activates prometheus_integration' do
expect(Clusters::Applications::DeactivateServiceWorker)
.to receive(:perform_async).with(cluster.id, 'prometheus')
diff --git a/spec/models/clusters/kubernetes_namespace_spec.rb b/spec/models/clusters/kubernetes_namespace_spec.rb
index 3b903fe34f9..e70cd15baca 100644
--- a/spec/models/clusters/kubernetes_namespace_spec.rb
+++ b/spec/models/clusters/kubernetes_namespace_spec.rb
@@ -62,6 +62,7 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
describe 'namespace uniqueness validation' do
let_it_be(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+
let(:kubernetes_namespace) { build(:cluster_kubernetes_namespace, cluster: cluster, namespace: 'my-namespace') }
subject { kubernetes_namespace }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 8ffc198fc4d..63fe6923630 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Commit do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet, :repository) }
let_it_be(:project_snippet) { create(:project_snippet, :repository) }
+
let(:commit) { project.commit }
describe 'modules' do
diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb
index d395aa359e5..86bab569ab0 100644
--- a/spec/models/compare_spec.rb
+++ b/spec/models/compare_spec.rb
@@ -13,7 +13,15 @@ RSpec.describe Compare do
let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, start_commit.id, head_commit.id) }
- subject { described_class.new(raw_compare, project) }
+ subject(:compare) { described_class.new(raw_compare, project) }
+
+ describe '#cache_key' do
+ subject { compare.cache_key }
+
+ it { is_expected.to include(project) }
+ it { is_expected.to include(:compare) }
+ it { is_expected.to include(compare.diff_refs.hash) }
+ end
describe '#start_commit' do
it 'returns raw compare base commit' do
diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb
index a9e944cf220..c7ea2631a24 100644
--- a/spec/models/concerns/approvable_base_spec.rb
+++ b/spec/models/concerns/approvable_base_spec.rb
@@ -59,4 +59,25 @@ RSpec.describe ApprovableBase do
end
end
end
+
+ describe '.not_approved_by_users_with_usernames' do
+ subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
+
+ let!(:merge_request2) { create(:merge_request) }
+ let!(:merge_request3) { create(:merge_request) }
+ let!(:merge_request4) { create(:merge_request) }
+ let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+
+ before do
+ create(:approval, merge_request: merge_request, user: user)
+ create(:approval, merge_request: merge_request2, user: user2)
+ create(:approval, merge_request: merge_request2, user: user3)
+ create(:approval, merge_request: merge_request4, user: user3)
+ end
+
+ it 'has the merge request that is not approved at all and not approved by either user' do
+ expect(subject).to contain_exactly(merge_request3, merge_request4)
+ end
+ end
end
diff --git a/spec/models/concerns/atomic_internal_id_spec.rb b/spec/models/concerns/atomic_internal_id_spec.rb
index 35b0f107676..b803e699b25 100644
--- a/spec/models/concerns/atomic_internal_id_spec.rb
+++ b/spec/models/concerns/atomic_internal_id_spec.rb
@@ -240,18 +240,12 @@ RSpec.describe AtomicInternalId do
end
describe '.with_project_iid_supply' do
- let(:iid) { 100 }
-
- it 'wraps generate and track_greatest in a concurrency-safe lock' do
- expect_next_instance_of(InternalId::InternalIdGenerator) do |g|
- expect(g).to receive(:with_lock).and_call_original
- expect(g.record).to receive(:last_value).and_return(iid)
- expect(g).to receive(:track_greatest).with(iid + 4)
- end
-
- ::Milestone.with_project_iid_supply(milestone.project) do |supply|
- 4.times { supply.next_value }
- end
+ it 'supplies a stream of iid values' do
+ expect do
+ ::Milestone.with_project_iid_supply(milestone.project) do |supply|
+ 4.times { supply.next_value }
+ end
+ end.to change { InternalId.find_by(project: milestone.project, usage: :milestones)&.last_value.to_i }.by(4)
end
end
end
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
index b80b6ec95e2..fcd0d0c05f4 100644
--- a/spec/models/concerns/awardable_spec.rb
+++ b/spec/models/concerns/awardable_spec.rb
@@ -3,64 +3,64 @@
require 'spec_helper'
RSpec.describe Awardable do
- let!(:issue) { create(:issue) }
- let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) }
+ let!(:note) { create(:note) }
+ let!(:award_emoji) { create(:award_emoji, :downvote, awardable: note) }
describe "Associations" do
- subject { build(:issue) }
+ subject { build(:note) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
end
describe "ClassMethods" do
- let!(:issue2) { create(:issue) }
- let!(:award_emoji2) { create(:award_emoji, awardable: issue2) }
+ let!(:note2) { create(:note) }
+ let!(:award_emoji2) { create(:award_emoji, awardable: note2) }
describe "orders" do
it "orders on upvotes" do
- expect(Issue.order_upvotes_desc.to_a).to eq [issue2, issue]
+ expect(Note.order_upvotes_desc.to_a).to eq [note2, note]
end
it "orders on downvotes" do
- expect(Issue.order_downvotes_desc.to_a).to eq [issue, issue2]
+ expect(Note.order_downvotes_desc.to_a).to eq [note, note2]
end
end
describe "#awarded" do
it "filters by user and emoji name" do
- expect(Issue.awarded(award_emoji.user, "thumbsup")).to be_empty
- expect(Issue.awarded(award_emoji.user, "thumbsdown")).to eq [issue]
- expect(Issue.awarded(award_emoji2.user, "thumbsup")).to eq [issue2]
- expect(Issue.awarded(award_emoji2.user, "thumbsdown")).to be_empty
+ expect(Note.awarded(award_emoji.user, "thumbsup")).to be_empty
+ expect(Note.awarded(award_emoji.user, "thumbsdown")).to eq [note]
+ expect(Note.awarded(award_emoji2.user, "thumbsup")).to eq [note2]
+ expect(Note.awarded(award_emoji2.user, "thumbsdown")).to be_empty
end
it "filters by user and any emoji" do
- issue3 = create(:issue)
- create(:award_emoji, awardable: issue3, name: "star", user: award_emoji.user)
- create(:award_emoji, awardable: issue3, name: "star", user: award_emoji2.user)
+ note3 = create(:note)
+ create(:award_emoji, awardable: note3, name: "star", user: award_emoji.user)
+ create(:award_emoji, awardable: note3, name: "star", user: award_emoji2.user)
- expect(Issue.awarded(award_emoji.user)).to contain_exactly(issue, issue3)
- expect(Issue.awarded(award_emoji2.user)).to contain_exactly(issue2, issue3)
+ expect(Note.awarded(award_emoji.user)).to contain_exactly(note, note3)
+ expect(Note.awarded(award_emoji2.user)).to contain_exactly(note2, note3)
end
end
describe "#not_awarded" do
- it "returns issues not awarded by user" do
- expect(Issue.not_awarded(award_emoji.user)).to eq [issue2]
- expect(Issue.not_awarded(award_emoji2.user)).to eq [issue]
+ it "returns notes not awarded by user" do
+ expect(Note.not_awarded(award_emoji.user)).to eq [note2]
+ expect(Note.not_awarded(award_emoji2.user)).to eq [note]
end
end
end
describe "#upvotes" do
it "counts the number of upvotes" do
- expect(issue.upvotes).to be 0
+ expect(note.upvotes).to be 0
end
end
describe "#downvotes" do
it "counts the number of downvotes" do
- expect(issue.downvotes).to be 1
+ expect(note.downvotes).to be 1
end
end
@@ -68,67 +68,67 @@ RSpec.describe Awardable do
let(:user) { create(:user) }
before do
- issue.project.add_guest(user)
+ note.project.add_guest(user)
end
it 'is truthy when the user is allowed to award emoji' do
- expect(issue.user_can_award?(user)).to be_truthy
+ expect(note.user_can_award?(user)).to be_truthy
end
it 'is falsy when the project is archived' do
- issue.project.update!(archived: true)
+ note.project.update!(archived: true)
- expect(issue.user_can_award?(user)).to be_falsy
+ expect(note.user_can_award?(user)).to be_falsy
end
end
describe 'querying award_emoji on an Awardable' do
- let(:issue) { create(:issue) }
+ let(:note) { create(:note) }
it 'sorts in ascending fashion' do
- create_list(:award_emoji, 3, awardable: issue)
+ create_list(:award_emoji, 3, awardable: note)
- expect(issue.award_emoji).to eq issue.award_emoji.sort_by(&:id)
+ expect(note.award_emoji).to eq note.award_emoji.sort_by(&:id)
end
end
describe "#grouped_awards" do
context 'default award emojis' do
- let(:issue_without_downvote) { create(:issue) }
- let(:issue_with_downvote) do
- issue_with_downvote = create(:issue)
- create(:award_emoji, :downvote, awardable: issue_with_downvote)
- issue_with_downvote
+ let(:note_without_downvote) { create(:note) }
+ let(:note_with_downvote) do
+ note_with_downvote = create(:note)
+ create(:award_emoji, :downvote, awardable: note_with_downvote)
+ note_with_downvote
end
it "includes unused thumbs buttons by default" do
- expect(issue_without_downvote.grouped_awards.keys.sort).to eq %w(thumbsdown thumbsup)
+ expect(note_without_downvote.grouped_awards.keys.sort).to eq %w(thumbsdown thumbsup)
end
it "doesn't include unused thumbs buttons when disabled in project" do
- issue_without_downvote.project.show_default_award_emojis = false
+ note_without_downvote.project.show_default_award_emojis = false
- expect(issue_without_downvote.grouped_awards.keys.sort).to be_empty
+ expect(note_without_downvote.grouped_awards.keys.sort).to be_empty
end
it "includes unused thumbs buttons when enabled in project" do
- issue_without_downvote.project.show_default_award_emojis = true
+ note_without_downvote.project.show_default_award_emojis = true
- expect(issue_without_downvote.grouped_awards.keys.sort).to eq %w(thumbsdown thumbsup)
+ expect(note_without_downvote.grouped_awards.keys.sort).to eq %w(thumbsdown thumbsup)
end
it "doesn't include unused thumbs buttons in summary" do
- expect(issue_without_downvote.grouped_awards(with_thumbs: false).keys).to be_empty
+ expect(note_without_downvote.grouped_awards(with_thumbs: false).keys).to be_empty
end
it "includes used thumbs buttons when disabled in project" do
- issue_with_downvote.project.show_default_award_emojis = false
+ note_with_downvote.project.show_default_award_emojis = false
- expect(issue_with_downvote.grouped_awards.keys).to eq %w(thumbsdown)
+ expect(note_with_downvote.grouped_awards.keys).to eq %w(thumbsdown)
end
it "includes used thumbs buttons in summary" do
- expect(issue_with_downvote.grouped_awards(with_thumbs: false).keys).to eq %w(thumbsdown)
+ expect(note_with_downvote.grouped_awards(with_thumbs: false).keys).to eq %w(thumbsdown)
end
end
end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 33a4c8eac41..1c1efab2889 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
it 'returns false when the local version was bumped' do
- allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2)
+ stub_application_setting(local_markdown_version: 2)
thing.cached_markdown_version = cache_version
is_expected.to be_falsy
@@ -88,7 +88,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
it 'returns true when the cached version is just right' do
- allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2)
+ stub_application_setting(local_markdown_version: 2)
thing.cached_markdown_version = cache_version + 2
is_expected.to be_truthy
diff --git a/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb b/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
index 02cd8557231..e8f2b18e662 100644
--- a/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
+++ b/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
@@ -17,18 +17,6 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
describe '#delayed_project_removal' do
subject(:delayed_project_removal) { subgroup_settings.delayed_project_removal }
- context 'when the feature is disabled' do
- before do
- stub_feature_flags(cascading_namespace_settings: false)
-
- group_settings.update!(delayed_project_removal: true)
- end
-
- it 'does not cascade' do
- expect(delayed_project_removal).to eq(nil)
- end
- end
-
context 'when there is no parent' do
context 'and the value is not nil' do
before do
@@ -192,16 +180,6 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
end
end
- context 'when the feature is disabled' do
- before do
- stub_feature_flags(cascading_namespace_settings: false)
-
- group_settings.update!(delayed_project_removal: true)
- end
-
- it_behaves_like 'not locked'
- end
-
context 'when attribute is locked by self' do
before do
subgroup_settings.update!(lock_delayed_project_removal: true)
diff --git a/spec/models/concerns/has_integrations_spec.rb b/spec/models/concerns/has_integrations_spec.rb
index 6e55a1c8b01..6b3f75bfcfd 100644
--- a/spec/models/concerns/has_integrations_spec.rb
+++ b/spec/models/concerns/has_integrations_spec.rb
@@ -7,14 +7,14 @@ RSpec.describe HasIntegrations do
let_it_be(:project_2) { create(:project) }
let_it_be(:project_3) { create(:project) }
let_it_be(:project_4) { create(:project) }
- let_it_be(:instance_integration) { create(:jira_service, :instance) }
+ let_it_be(:instance_integration) { create(:jira_integration, :instance) }
before do
- create(:jira_service, project: project_1, inherit_from_id: instance_integration.id)
- create(:jira_service, project: project_2, inherit_from_id: nil)
- create(:jira_service, group: create(:group), project: nil, inherit_from_id: nil)
- create(:jira_service, project: project_3, inherit_from_id: nil)
- create(:slack_service, project: project_4, inherit_from_id: nil)
+ create(:jira_integration, project: project_1, inherit_from_id: instance_integration.id)
+ create(:jira_integration, project: project_2, inherit_from_id: nil)
+ create(:jira_integration, group: create(:group), project: nil, inherit_from_id: nil)
+ create(:jira_integration, project: project_3, inherit_from_id: nil)
+ create(:integrations_slack, project: project_4, inherit_from_id: nil)
end
describe '.with_custom_integration_for' do
diff --git a/spec/models/concerns/integrations/has_data_fields_spec.rb b/spec/models/concerns/integrations/has_data_fields_spec.rb
index 54e0ac9c5a5..b28fef571c6 100644
--- a/spec/models/concerns/integrations/has_data_fields_spec.rb
+++ b/spec/models/concerns/integrations/has_data_fields_spec.rb
@@ -84,7 +84,7 @@ RSpec.describe Integrations::HasDataFields do
context 'when data are stored in data_fields' do
let(:service) do
- create(:jira_service, url: url, username: username)
+ create(:jira_integration, url: url, username: username)
end
it_behaves_like 'data fields'
@@ -111,45 +111,52 @@ RSpec.describe Integrations::HasDataFields do
end
context 'when data are stored in properties' do
- let(:service) { create(:jira_service, :without_properties_callback, properties: properties) }
+ let(:integration) { create(:jira_integration, :without_properties_callback, properties: properties) }
- it_behaves_like 'data fields'
+ it_behaves_like 'data fields' do
+ let(:service) { integration }
+ end
describe '{arg}_was?' do
it 'returns nil when the property has not been assigned a new value' do
- service.username = 'new_username'
- service.validate
- expect(service.url_was).to be_nil
+ integration.username = 'new_username'
+ integration.validate
+
+ expect(integration.url_was).to be_nil
end
it 'returns initial value when the property has been assigned a different value' do
- service.url = 'http://example.com'
- service.validate
- expect(service.url_was).to eq('http://url.com')
+ integration.url = 'http://example.com'
+ integration.validate
+
+ expect(integration.url_was).to eq('http://url.com')
end
it 'returns initial value when the property has been re-assigned the same value' do
- service.url = 'http://url.com'
- service.validate
- expect(service.url_was).to eq('http://url.com')
+ integration.url = 'http://url.com'
+ integration.validate
+
+ expect(integration.url_was).to eq('http://url.com')
end
end
end
context 'when data are stored in both properties and data_fields' do
- let(:service) do
- create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |integration|
+ let(:integration) do
+ create(:jira_integration, :without_properties_callback, active: false, properties: properties).tap do |integration|
create(:jira_tracker_data, properties.merge(integration: integration))
end
end
- it_behaves_like 'data fields'
+ it_behaves_like 'data fields' do
+ let(:service) { integration }
+ end
describe '{arg}_was?' do
it 'returns nil' do
- service.url = 'http://example.com'
- service.validate
- expect(service.url_was).to be_nil
+ integration.url = 'http://example.com'
+ integration.validate
+ expect(integration.url_was).to be_nil
end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 7b100b7a6f3..071e0dcba44 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -535,6 +535,26 @@ RSpec.describe Issuable do
merge_request.to_hook_data(user, old_associations: { assignees: [user] })
end
end
+
+ context 'incident severity is updated' do
+ let(:issue) { create(:incident) }
+
+ before do
+ issue.update!(issuable_severity_attributes: { severity: 'low' })
+ expect(Gitlab::HookData::IssuableBuilder)
+ .to receive(:new).with(issue).and_return(builder)
+ end
+
+ it 'delegates to Gitlab::HookData::IssuableBuilder#build' do
+ expect(builder).to receive(:build).with(
+ user: user,
+ changes: hash_including(
+ 'severity' => %w(unknown low)
+ ))
+
+ issue.to_hook_data(user, old_associations: { severity: 'unknown' })
+ end
+ end
end
describe '#labels_array' do
diff --git a/spec/models/concerns/partitioned_table_spec.rb b/spec/models/concerns/partitioned_table_spec.rb
index 3343b273ba2..c37fb81a1cf 100644
--- a/spec/models/concerns/partitioned_table_spec.rb
+++ b/spec/models/concerns/partitioned_table_spec.rb
@@ -14,6 +14,16 @@ RSpec.describe PartitionedTable do
end
end
+ context 'with keyword arguments passed to the strategy' do
+ subject { my_class.partitioned_by(key, strategy: :monthly, retain_for: 3.months) }
+
+ it 'passes the keyword arguments to the strategy' do
+ expect(Gitlab::Database::Partitioning::MonthlyStrategy).to receive(:new).with(my_class, key, retain_for: 3.months).and_call_original
+
+ subject
+ end
+ end
+
it 'assigns the MonthlyStrategy as the partitioning strategy' do
subject
@@ -27,7 +37,7 @@ RSpec.describe PartitionedTable do
end
it 'registers itself with the PartitionCreator' do
- expect(Gitlab::Database::Partitioning::PartitionCreator).to receive(:register).with(my_class)
+ expect(Gitlab::Database::Partitioning::PartitionManager).to receive(:register).with(my_class)
subject
end
diff --git a/spec/models/concerns/prometheus_adapter_spec.rb b/spec/models/concerns/prometheus_adapter_spec.rb
index 235e505c6e9..01c987a1d92 100644
--- a/spec/models/concerns/prometheus_adapter_spec.rb
+++ b/spec/models/concerns/prometheus_adapter_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
let(:project) { create(:prometheus_project) }
- let(:service) { project.prometheus_service }
+ let(:integration) { project.prometheus_integration }
let(:described_class) do
Class.new do
@@ -29,10 +29,10 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
context 'with valid data' do
- subject { service.query(:validate, query) }
+ subject { integration.query(:validate, query) }
before do
- stub_reactive_cache(service, validation_respone, validation_query, query)
+ stub_reactive_cache(integration, validation_respone, validation_query, query)
end
it 'returns query data' do
@@ -49,10 +49,10 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
context 'with valid data' do
- subject { service.query(:environment, environment) }
+ subject { integration.query(:environment, environment) }
before do
- stub_reactive_cache(service, prometheus_data, environment_query, environment.id)
+ stub_reactive_cache(integration, prometheus_data, environment_query, environment.id)
end
it 'returns reactive data' do
@@ -66,11 +66,11 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
let(:prometheus_client) { double(:prometheus_client, label_values: nil) }
context 'with valid data' do
- subject { service.query(:matched_metrics) }
+ subject { integration.query(:matched_metrics) }
before do
- allow(service).to receive(:prometheus_client).and_return(prometheus_client)
- synchronous_reactive_cache(service)
+ allow(integration).to receive(:prometheus_client).and_return(prometheus_client)
+ synchronous_reactive_cache(integration)
end
it 'returns reactive data' do
@@ -89,10 +89,10 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
context 'with valid data' do
- subject { service.query(:deployment, deployment) }
+ subject { integration.query(:deployment, deployment) }
before do
- stub_reactive_cache(service, prometheus_data, deployment_query, deployment.id)
+ stub_reactive_cache(integration, prometheus_data, deployment_query, deployment.id)
end
it 'returns reactive data' do
@@ -111,10 +111,10 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
context 'with valid data' do
- subject { service.query(:additional_metrics_environment, environment, *time_window) }
+ subject { integration.query(:additional_metrics_environment, environment, *time_window) }
before do
- stub_reactive_cache(service, prometheus_data, additional_metrics_environment_query, environment.id, *time_window)
+ stub_reactive_cache(integration, prometheus_data, additional_metrics_environment_query, environment.id, *time_window)
end
it 'returns reactive data' do
@@ -128,21 +128,21 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
let(:environment) { create(:environment, slug: 'env-slug') }
before do
- service.manual_configuration = true
- service.active = true
+ integration.manual_configuration = true
+ integration.active = true
end
subject do
- service.calculate_reactive_cache(environment_query.name, environment.id)
+ integration.calculate_reactive_cache(environment_query.name, environment.id)
end
around do |example|
freeze_time { example.run }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
- service.active = false
+ integration.active = false
end
it { is_expected.to be_nil }
@@ -168,7 +168,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
describe '#build_query_args' do
- subject { service.build_query_args(*args) }
+ subject { integration.build_query_args(*args) }
context 'when active record models are included' do
let(:args) { [double(:environment, id: 12)] }
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index 3232a559d0b..a53db07cc59 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -281,6 +281,16 @@ RSpec.describe ContainerRepository do
expect(repository.name).to be_empty
end
end
+
+ context 'when repository already exists' do
+ let(:path) { project.full_path + '/some/image' }
+
+ it 'returns the existing repository' do
+ container_repository = create(:container_repository, project: project, name: 'some/image')
+
+ expect(repository.id).to eq(container_repository.id)
+ end
+ end
end
describe '.build_root_repository' do
@@ -311,13 +321,18 @@ RSpec.describe ContainerRepository do
end
context 'with a subgroup' do
- let(:test_group) { create(:group) }
- let(:another_project) { create(:project, path: 'test', group: test_group) }
+ let_it_be(:test_group) { create(:group) }
+ let_it_be(:another_project) { create(:project, path: 'test', group: test_group) }
+ let_it_be(:project3) { create(:project, path: 'test3', group: test_group, container_registry_enabled: false) }
- let(:another_repository) do
+ let_it_be(:another_repository) do
create(:container_repository, name: 'my_image', project: another_project)
end
+ let_it_be(:repository3) do
+ create(:container_repository, name: 'my_image3', project: project3)
+ end
+
before do
group.parent = test_group
group.save!
@@ -331,40 +346,6 @@ RSpec.describe ContainerRepository do
it { is_expected.to eq([]) }
end
-
- context 'with read_container_registry_access_level disabled' do
- before do
- stub_feature_flags(read_container_registry_access_level: false)
- end
-
- context 'in a group' do
- let(:test_group) { group }
-
- it { is_expected.to contain_exactly(repository) }
- end
-
- context 'with a subgroup' do
- let(:test_group) { create(:group) }
- let(:another_project) { create(:project, path: 'test', group: test_group) }
-
- let(:another_repository) do
- create(:container_repository, name: 'my_image', project: another_project)
- end
-
- before do
- group.parent = test_group
- group.save!
- end
-
- it { is_expected.to contain_exactly(repository, another_repository) }
- end
-
- context 'group without container_repositories' do
- let(:test_group) { create(:group) }
-
- it { is_expected.to eq([]) }
- end
- end
end
describe '.search_by_name' do
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index dfc37f9e661..c9f7895a616 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -242,6 +242,7 @@ RSpec.describe DeployToken do
context 'and when the token is of group type' do
let_it_be(:group) { create(:group) }
+
let(:deploy_token) { create(:deploy_token, :group) }
before do
diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb
index fadfc1b63ac..c804e20d66d 100644
--- a/spec/models/deployment_metrics_spec.rb
+++ b/spec/models/deployment_metrics_spec.rb
@@ -15,35 +15,35 @@ RSpec.describe DeploymentMetrics do
context 'when deployment is success' do
let(:deployment) { create(:deployment, :success) }
- context 'without a monitoring service' do
+ context 'without a monitoring integration' do
it { is_expected.to be_falsy }
end
- context 'with a Prometheus Service' do
- let(:prometheus_service) { instance_double(PrometheusService, can_query?: true, configured?: true) }
+ context 'with a Prometheus integration' do
+ let(:prometheus_integration) { instance_double(::Integrations::Prometheus, can_query?: true, configured?: true) }
before do
- allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
+ allow(deployment.project).to receive(:find_or_initialize_integration).with('prometheus').and_return prometheus_integration
end
it { is_expected.to be_truthy }
end
- context 'with a Prometheus Service that cannot query' do
- let(:prometheus_service) { instance_double(PrometheusService, configured?: true, can_query?: false) }
+ context 'with a Prometheus integration that cannot query' do
+ let(:prometheus_integration) { instance_double(::Integrations::Prometheus, configured?: true, can_query?: false) }
before do
- allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
+ allow(deployment.project).to receive(:find_or_initialize_integration).with('prometheus').and_return prometheus_integration
end
it { is_expected.to be_falsy }
end
- context 'with a Prometheus Service that is not configured' do
- let(:prometheus_service) { instance_double(PrometheusService, configured?: false, can_query?: false) }
+ context 'with a Prometheus integration that is not configured' do
+ let(:prometheus_integration) { instance_double(::Integrations::Prometheus, configured?: false, can_query?: false) }
before do
- allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
+ allow(deployment.project).to receive(:find_or_initialize_integration).with('prometheus').and_return prometheus_integration
end
it { is_expected.to be_falsy }
@@ -64,7 +64,7 @@ RSpec.describe DeploymentMetrics do
describe '#metrics' do
let(:deployment) { create(:deployment, :success) }
- let(:prometheus_adapter) { instance_double(PrometheusService, can_query?: true, configured?: true) }
+ let(:prometheus_adapter) { instance_double(::Integrations::Prometheus, can_query?: true, configured?: true) }
let(:deployment_metrics) { described_class.new(deployment.project, deployment) }
subject { deployment_metrics.metrics }
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index bcd237cbd38..a0e5e9cbfe4 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -80,6 +80,7 @@ RSpec.describe Deployment do
let_it_be(:staging) { create(:environment, :staging, project: project) }
let_it_be(:other_project) { create(:project, :repository) }
let_it_be(:other_production) { create(:environment, :production, project: other_project) }
+
let(:environment_name) { production.name }
context 'when deployment belongs to the environment' do
@@ -488,6 +489,7 @@ RSpec.describe Deployment do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:commits) { project.repository.commits('master', limit: 2) }
let_it_be(:deployments) { commits.reverse.map { |commit| create(:deployment, project: project, sha: commit.id) } }
+
let(:sha) { commits.map(&:id) }
it 'finds the latest deployment with sha' do
@@ -823,6 +825,7 @@ RSpec.describe Deployment do
describe '#update_merge_request_metrics!' do
let_it_be(:project) { create(:project, :repository) }
+
let(:environment) { build(:environment, environment_tier, project: project) }
let!(:deployment) { create(:deployment, :success, project: project, environment: environment) }
let!(:merge_request) { create(:merge_request, :simple, :merged_last_month, project: project) }
diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb
index 2a2663149d0..998204626d3 100644
--- a/spec/models/diff_discussion_spec.rb
+++ b/spec/models/diff_discussion_spec.rb
@@ -126,4 +126,13 @@ RSpec.describe DiffDiscussion do
end
end
end
+
+ describe '#cache_key' do
+ it 'returns the cache key with the position sha' do
+ notes_sha = Digest::SHA1.hexdigest("#{diff_note.id}")
+ position_sha = Digest::SHA1.hexdigest(diff_note.position.to_json)
+
+ expect(subject.cache_key).to eq("#{described_class::CACHE_VERSION}:#{diff_note.latest_cached_markdown_version}:#{subject.id}:#{notes_sha}:#{diff_note.updated_at}::#{position_sha}")
+ end
+ end
end
diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb
index 686dd1249be..28660b0d4b9 100644
--- a/spec/models/diff_viewer/server_side_spec.rb
+++ b/spec/models/diff_viewer/server_side_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe DiffViewer::ServerSide do
let_it_be(:project) { create(:project, :repository) }
+
let(:commit) { project.commit_by(oid: '570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
let!(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 021940be0c2..2b33de96e04 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -51,4 +51,22 @@ RSpec.describe Discussion do
expect(policy).to be_a(NotePolicy)
end
end
+
+ describe '#cache_key' do
+ let(:notes_sha) { Digest::SHA1.hexdigest("#{first_note.id}:#{second_note.id}:#{third_note.id}") }
+
+ it 'returns the cache key with ID and latest updated note updated at' do
+ expect(subject.cache_key).to eq("#{described_class::CACHE_VERSION}:#{third_note.latest_cached_markdown_version}:#{subject.id}:#{notes_sha}:#{third_note.updated_at}:")
+ end
+
+ context 'when discussion is resolved' do
+ before do
+ subject.resolve!(first_note.author)
+ end
+
+ it 'returns the cache key with resolved at' do
+ expect(subject.cache_key).to eq("#{described_class::CACHE_VERSION}:#{third_note.latest_cached_markdown_version}:#{subject.id}:#{notes_sha}:#{third_note.updated_at}:#{subject.resolved_at}")
+ end
+ end
+ end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index ff4c8ae950d..18a172b72d7 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -219,6 +219,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
+
let(:environments) { Environment.all }
before_all do
@@ -760,6 +761,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
describe '#last_visible_pipeline' do
let(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
+
let(:environment) { create(:environment, project: project) }
let(:commit) { project.commit }
@@ -1462,6 +1464,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:environment, reload: true) { create(:environment, project: project) }
+
let!(:deployment) { create(:deployment, project: project, environment: environment, deployable: build) }
let!(:build) { create(:ci_build, :running, project: project, environment: environment) }
diff --git a/spec/models/error_tracking/error_event_spec.rb b/spec/models/error_tracking/error_event_spec.rb
new file mode 100644
index 00000000000..331661f88cc
--- /dev/null
+++ b/spec/models/error_tracking/error_event_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ErrorTracking::ErrorEvent, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:error) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:description) }
+ it { is_expected.to validate_presence_of(:occurred_at) }
+ end
+end
diff --git a/spec/models/error_tracking/error_spec.rb b/spec/models/error_tracking/error_spec.rb
new file mode 100644
index 00000000000..8591802d15c
--- /dev/null
+++ b/spec/models/error_tracking/error_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ErrorTracking::Error, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:events) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_presence_of(:description) }
+ it { is_expected.to validate_presence_of(:actor) }
+ end
+end
diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb
index aca2a8c3a2f..107447c9630 100644
--- a/spec/models/event_collection_spec.rb
+++ b/spec/models/event_collection_spec.rb
@@ -28,6 +28,7 @@ RSpec.describe EventCollection do
let_it_be(:closed_issue_event) { create(:closed_issue_event, project: project, author: user) }
let_it_be(:wiki_page_event) { create(:wiki_page_event, project: project) }
let_it_be(:design_event) { create(:design_event, project: project) }
+
let(:push_events) { push_event_payloads.map(&:event) }
it 'returns an Array of events', :aggregate_failures do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 949e8ec0a72..fc229dcaa22 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -268,6 +268,7 @@ RSpec.describe Event do
let(:design) { create(:design, issue: issue, project: project) }
let(:note_on_commit) { create(:note_on_commit, project: project) }
let(:note_on_issue) { create(:note_on_issue, noteable: issue, project: project) }
+ let(:confidential_note) { create(:note, noteable: issue, project: project, confidential: true) }
let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project) }
let(:note_on_project_snippet) { create(:note_on_project_snippet, author: author, noteable: project_snippet, project: project) }
let(:note_on_personal_snippet) { create(:note_on_personal_snippet, author: author, noteable: personal_snippet, project: nil) }
@@ -399,6 +400,16 @@ RSpec.describe Event do
include_examples 'visible to assignee and author', true
end
+ context 'confidential note' do
+ let(:target) { confidential_note }
+
+ include_examples 'visibility examples' do
+ let(:visibility) { visible_to_none_except(:member) }
+ end
+
+ include_examples 'visible to author', true
+ end
+
context 'private project' do
let(:project) { private_project }
let(:target) { note_on_issue }
@@ -967,14 +978,13 @@ RSpec.describe Event do
describe '#action_name' do
it 'handles all valid design events' do
- created, updated, destroyed, archived = %i[created updated destroyed archived].map do |trait|
+ created, updated, destroyed = %i[created updated destroyed].map do |trait|
build(:design_event, trait).action_name
end
expect(created).to eq('uploaded')
expect(updated).to eq('revised')
expect(destroyed).to eq('deleted')
- expect(archived).to eq('archived')
end
it 'handles correct push_action' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 8f4bc43c38a..0a08b15a1eb 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -565,11 +565,11 @@ RSpec.describe Group do
describe '.without_integration' do
let(:another_group) { create(:group) }
- let(:instance_integration) { build(:jira_service, :instance) }
+ let(:instance_integration) { build(:jira_integration, :instance) }
before do
- create(:jira_service, group: group, project: nil)
- create(:slack_service, group: another_group, project: nil)
+ create(:jira_integration, group: group, project: nil)
+ create(:integrations_slack, group: another_group, project: nil)
end
it 'returns groups without integration' do
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index d4ea3e5d08a..ab4027170b2 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -9,11 +9,12 @@ RSpec.describe Integration do
let_it_be(:project) { create(:project, group: group) }
describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to belong_to :group }
- it { is_expected.to have_one :service_hook }
- it { is_expected.to have_one :jira_tracker_data }
- it { is_expected.to have_one :issue_tracker_data }
+ it { is_expected.to belong_to(:project).inverse_of(:integrations) }
+ it { is_expected.to belong_to(:group).inverse_of(:integrations) }
+ it { is_expected.to have_one(:service_hook).inverse_of(:integration).with_foreign_key(:service_id) }
+ it { is_expected.to have_one(:issue_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::IssueTrackerData') }
+ it { is_expected.to have_one(:jira_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::JiraTrackerData') }
+ it { is_expected.to have_one(:open_project_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::OpenProjectTrackerData') }
end
describe 'validations' do
@@ -68,9 +69,9 @@ RSpec.describe Integration do
describe 'Scopes' do
describe '.by_type' do
- let!(:service1) { create(:jira_service) }
- let!(:service2) { create(:jira_service) }
- let!(:service3) { create(:redmine_service) }
+ let!(:service1) { create(:jira_integration) }
+ let!(:service2) { create(:jira_integration) }
+ let!(:service3) { create(:redmine_integration) }
subject { described_class.by_type(type) }
@@ -88,8 +89,8 @@ RSpec.describe Integration do
end
describe '.for_group' do
- let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) }
- let!(:service2) { create(:jira_service) }
+ let!(:service1) { create(:jira_integration, project_id: nil, group_id: group.id) }
+ let!(:service2) { create(:jira_integration) }
it 'returns the right group service' do
expect(described_class.for_group(group)).to match_array([service1])
@@ -139,67 +140,38 @@ RSpec.describe Integration do
end
end
- describe "Test Button" do
- let(:service) { build(:service, project: project) }
-
- describe '#can_test?' do
- subject { service.can_test? }
-
- context 'when repository is not empty' do
- let(:project) { build(:project, :repository) }
-
- it { is_expected.to be true }
- end
-
- context 'when repository is empty' do
- let(:project) { build(:project) }
-
- it { is_expected.to be true }
- end
-
- context 'when instance-level service' do
- Integration.available_services_types.each do |service_type|
- let(:service) do
- described_class.send(:integration_type_to_model, service_type).new(instance: true)
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
- context 'when group-level service' do
- Integration.available_services_types.each do |service_type|
- let(:service) do
- described_class.send(:integration_type_to_model, service_type).new(group_id: group.id)
- end
+ describe '#testable?' do
+ context 'when integration is project-level' do
+ subject { build(:service, project: project) }
- it { is_expected.to be_falsey }
- end
- end
+ it { is_expected.to be_testable }
end
- describe '#test' do
- let(:data) { 'test' }
+ context 'when integration is not project-level' do
+ subject { build(:service, project: nil) }
- context 'when repository is not empty' do
- let(:project) { build(:project, :repository) }
+ it { is_expected.not_to be_testable }
+ end
+ end
- it 'test runs execute' do
- expect(service).to receive(:execute).with(data)
+ describe '#test' do
+ let(:integration) { build(:service, project: project) }
+ let(:data) { 'test' }
- service.test(data)
- end
- end
+ it 'calls #execute' do
+ expect(integration).to receive(:execute).with(data)
- context 'when repository is empty' do
- let(:project) { build(:project) }
+ integration.test(data)
+ end
- it 'test runs execute' do
- expect(service).to receive(:execute).with(data)
+ it 'returns a result' do
+ result = 'foo'
+ allow(integration).to receive(:execute).with(data).and_return(result)
- service.test(data)
- end
- end
+ expect(integration.test(data)).to eq(
+ success: true,
+ result: result
+ )
end
end
@@ -234,26 +206,30 @@ RSpec.describe Integration do
end
describe '.find_or_initialize_non_project_specific_integration' do
- let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) }
- let!(:service2) { create(:jira_service) }
+ let!(:integration_1) { create(:jira_integration, project_id: nil, group_id: group.id) }
+ let!(:integration_2) { create(:jira_integration) }
- it 'returns the right service' do
- expect(Integration.find_or_initialize_non_project_specific_integration('jira', group_id: group)).to eq(service1)
+ it 'returns the right integration' do
+ expect(Integration.find_or_initialize_non_project_specific_integration('jira', group_id: group))
+ .to eq(integration_1)
end
- it 'does not create a new service' do
- expect { Integration.find_or_initialize_non_project_specific_integration('redmine', group_id: group) }.not_to change { Integration.count }
+ it 'does not create a new integration' do
+ expect { Integration.find_or_initialize_non_project_specific_integration('redmine', group_id: group) }
+ .not_to change(Integration, :count)
end
end
describe '.find_or_initialize_all_non_project_specific' do
shared_examples 'service instances' do
it 'returns the available service instances' do
- expect(Integration.find_or_initialize_all_non_project_specific(Integration.for_instance).map(&:to_param)).to match_array(Integration.available_services_names(include_project_specific: false))
+ expect(Integration.find_or_initialize_all_non_project_specific(Integration.for_instance).map(&:to_param))
+ .to match_array(Integration.available_integration_names(include_project_specific: false))
end
it 'does not create service instances' do
- expect { Integration.find_or_initialize_all_non_project_specific(Integration.for_instance) }.not_to change { Integration.count }
+ expect { Integration.find_or_initialize_all_non_project_specific(Integration.for_instance) }
+ .not_to change(Integration, :count)
end
end
@@ -262,7 +238,7 @@ RSpec.describe Integration do
context 'with all existing instances' do
before do
Integration.insert_all(
- Integration.available_services_types(include_project_specific: false).map { |type| { instance: true, type: type } }
+ Integration.available_integration_types(include_project_specific: false).map { |type| { instance: true, type: type } }
)
end
@@ -280,7 +256,7 @@ RSpec.describe Integration do
context 'with a few existing instances' do
before do
- create(:jira_service, :instance)
+ create(:jira_integration, :instance)
end
it_behaves_like 'service instances'
@@ -290,13 +266,15 @@ RSpec.describe Integration do
describe 'template' do
shared_examples 'retrieves service templates' do
it 'returns the available service templates' do
- expect(Integration.find_or_create_templates.pluck(:type)).to match_array(Integration.available_services_types(include_project_specific: false))
+ expect(Integration.find_or_create_templates.pluck(:type)).to match_array(Integration.available_integration_types(include_project_specific: false))
end
end
describe '.find_or_create_templates' do
it 'creates service templates' do
- expect { Integration.find_or_create_templates }.to change { Integration.count }.from(0).to(Integration.available_services_names(include_project_specific: false).size)
+ total = Integration.available_integration_names(include_project_specific: false).size
+
+ expect { Integration.find_or_create_templates }.to change(Integration, :count).from(0).to(total)
end
it_behaves_like 'retrieves service templates'
@@ -304,7 +282,7 @@ RSpec.describe Integration do
context 'with all existing templates' do
before do
Integration.insert_all(
- Integration.available_services_types(include_project_specific: false).map { |type| { template: true, type: type } }
+ Integration.available_integration_types(include_project_specific: false).map { |type| { template: true, type: type } }
)
end
@@ -326,11 +304,13 @@ RSpec.describe Integration do
context 'with a few existing templates' do
before do
- create(:jira_service, :template)
+ create(:jira_integration, :template)
end
it 'creates the rest of the service templates' do
- expect { Integration.find_or_create_templates }.to change { Integration.count }.from(1).to(Integration.available_services_names(include_project_specific: false).size)
+ total = Integration.available_integration_names(include_project_specific: false).size
+
+ expect { Integration.find_or_create_templates }.to change(Integration, :count).from(1).to(total)
end
it_behaves_like 'retrieves service templates'
@@ -339,36 +319,36 @@ RSpec.describe Integration do
describe '.build_from_integration' do
context 'when integration is invalid' do
- let(:integration) do
- build(:prometheus_service, :template, active: true, properties: {})
+ let(:template_integration) do
+ build(:prometheus_integration, :template, active: true, properties: {})
.tap { |integration| integration.save!(validate: false) }
end
- it 'sets service to inactive' do
- service = described_class.build_from_integration(integration, project_id: project.id)
+ it 'sets integration to inactive' do
+ integration = described_class.build_from_integration(template_integration, project_id: project.id)
- expect(service).to be_valid
- expect(service.active).to be false
+ expect(integration).to be_valid
+ expect(integration.active).to be false
end
end
context 'when integration is an instance-level integration' do
- let(:integration) { create(:jira_service, :instance) }
+ let(:instance_integration) { create(:jira_integration, :instance) }
it 'sets inherit_from_id from integration' do
- service = described_class.build_from_integration(integration, project_id: project.id)
+ integration = described_class.build_from_integration(instance_integration, project_id: project.id)
- expect(service.inherit_from_id).to eq(integration.id)
+ expect(integration.inherit_from_id).to eq(instance_integration.id)
end
end
context 'when integration is a group-level integration' do
- let(:integration) { create(:jira_service, group: group, project: nil) }
+ let(:group_integration) { create(:jira_integration, group: group, project: nil) }
it 'sets inherit_from_id from integration' do
- service = described_class.build_from_integration(integration, project_id: project.id)
+ integration = described_class.build_from_integration(group_integration, project_id: project.id)
- expect(service.inherit_from_id).to eq(integration.id)
+ expect(integration.inherit_from_id).to eq(group_integration.id)
end
end
@@ -418,7 +398,7 @@ RSpec.describe Integration do
context 'when data are stored in properties' do
let(:properties) { data_params }
let!(:integration) do
- create(:jira_service, :without_properties_callback, template: true, properties: properties.merge(additional: 'something'))
+ create(:jira_integration, :without_properties_callback, template: true, properties: properties.merge(additional: 'something'))
end
it_behaves_like 'service creation from an integration'
@@ -426,7 +406,7 @@ RSpec.describe Integration do
context 'when data are stored in separated fields' do
let(:integration) do
- create(:jira_service, :template, data_params.merge(properties: {}))
+ create(:jira_integration, :template, data_params.merge(properties: {}))
end
it_behaves_like 'service creation from an integration'
@@ -435,7 +415,7 @@ RSpec.describe Integration do
context 'when data are stored in both properties and separated fields' do
let(:properties) { data_params }
let(:integration) do
- create(:jira_service, :without_properties_callback, active: true, template: true, properties: properties).tap do |integration|
+ create(:jira_integration, :without_properties_callback, active: true, template: true, properties: properties).tap do |integration|
create(:jira_tracker_data, data_params.merge(integration: integration))
end
end
@@ -459,39 +439,41 @@ RSpec.describe Integration do
describe 'is prefilled for projects pushover service' do
it "has all fields prefilled" do
- service = project.find_or_initialize_service('pushover')
-
- expect(service.template).to eq(false)
- expect(service.device).to eq('MyDevice')
- expect(service.sound).to eq('mic')
- expect(service.priority).to eq(4)
- expect(service.api_key).to eq('123456789')
+ integration = project.find_or_initialize_integration('pushover')
+
+ expect(integration).to have_attributes(
+ template: eq(false),
+ device: eq('MyDevice'),
+ sound: eq('mic'),
+ priority: eq(4),
+ api_key: eq('123456789')
+ )
end
end
end
end
describe '.default_integration' do
- context 'with an instance-level service' do
- let_it_be(:instance_service) { create(:jira_service, :instance) }
+ context 'with an instance-level integration' do
+ let_it_be(:instance_integration) { create(:jira_integration, :instance) }
- it 'returns the instance service' do
- expect(described_class.default_integration('JiraService', project)).to eq(instance_service)
+ it 'returns the instance integration' do
+ expect(described_class.default_integration('JiraService', project)).to eq(instance_integration)
end
- it 'returns nil for nonexistent service type' do
+ it 'returns nil for nonexistent integration type' do
expect(described_class.default_integration('HipchatService', project)).to eq(nil)
end
- context 'with a group service' do
- let_it_be(:group_service) { create(:jira_service, group_id: group.id, project_id: nil) }
+ context 'with a group integration' do
+ let_it_be(:group_integration) { create(:jira_integration, group_id: group.id, project_id: nil) }
- it 'returns the group service for a project' do
- expect(described_class.default_integration('JiraService', project)).to eq(group_service)
+ it 'returns the group integration for a project' do
+ expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
end
- it 'returns the instance service for a group' do
- expect(described_class.default_integration('JiraService', group)).to eq(instance_service)
+ it 'returns the instance integration for a group' do
+ expect(described_class.default_integration('JiraService', group)).to eq(instance_integration)
end
context 'with a subgroup' do
@@ -499,27 +481,27 @@ RSpec.describe Integration do
let!(:project) { create(:project, group: subgroup) }
- it 'returns the closest group service for a project' do
- expect(described_class.default_integration('JiraService', project)).to eq(group_service)
+ it 'returns the closest group integration for a project' do
+ expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
end
- it 'returns the closest group service for a subgroup' do
- expect(described_class.default_integration('JiraService', subgroup)).to eq(group_service)
+ it 'returns the closest group integration for a subgroup' do
+ expect(described_class.default_integration('JiraService', subgroup)).to eq(group_integration)
end
- context 'having a service with custom settings' do
- let!(:subgroup_service) { create(:jira_service, group_id: subgroup.id, project_id: nil) }
+ context 'having a integration with custom settings' do
+ let!(:subgroup_integration) { create(:jira_integration, group_id: subgroup.id, project_id: nil) }
- it 'returns the closest group service for a project' do
- expect(described_class.default_integration('JiraService', project)).to eq(subgroup_service)
+ it 'returns the closest group integration for a project' do
+ expect(described_class.default_integration('JiraService', project)).to eq(subgroup_integration)
end
end
- context 'having a service inheriting settings' do
- let!(:subgroup_service) { create(:jira_service, group_id: subgroup.id, project_id: nil, inherit_from_id: group_service.id) }
+ context 'having a integration inheriting settings' do
+ let!(:subgroup_integration) { create(:jira_integration, group_id: subgroup.id, project_id: nil, inherit_from_id: group_integration.id) }
- it 'returns the closest group service which does not inherit from its parent for a project' do
- expect(described_class.default_integration('JiraService', project)).to eq(group_service)
+ it 'returns the closest group integration which does not inherit from its parent for a project' do
+ expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
end
end
end
@@ -528,10 +510,10 @@ RSpec.describe Integration do
end
describe '.create_from_active_default_integrations' do
- context 'with an active service template' do
- let_it_be(:template_integration) { create(:prometheus_service, :template, api_url: 'https://prometheus.template.com/') }
+ context 'with an active integration template' do
+ let_it_be(:template_integration) { create(:prometheus_integration, :template, api_url: 'https://prometheus.template.com/') }
- it 'creates a service from the template' do
+ it 'creates an integration from the template' do
described_class.create_from_active_default_integrations(project, :project_id, with_templates: true)
expect(project.reload.integrations.size).to eq(1)
@@ -540,9 +522,9 @@ RSpec.describe Integration do
end
context 'with an active instance-level integration' do
- let!(:instance_integration) { create(:prometheus_service, :instance, api_url: 'https://prometheus.instance.com/') }
+ let!(:instance_integration) { create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') }
- it 'creates a service from the instance-level integration' do
+ it 'creates an integration from the instance-level integration' do
described_class.create_from_active_default_integrations(project, :project_id, with_templates: true)
expect(project.reload.integrations.size).to eq(1)
@@ -551,7 +533,7 @@ RSpec.describe Integration do
end
context 'passing a group' do
- it 'creates a service from the instance-level integration' do
+ it 'creates an integration from the instance-level integration' do
described_class.create_from_active_default_integrations(group, :group_id)
expect(group.reload.integrations.size).to eq(1)
@@ -561,9 +543,9 @@ RSpec.describe Integration do
end
context 'with an active group-level integration' do
- let!(:group_integration) { create(:prometheus_service, group: group, project: nil, api_url: 'https://prometheus.group.com/') }
+ let!(:group_integration) { create(:prometheus_integration, group: group, project: nil, api_url: 'https://prometheus.group.com/') }
- it 'creates a service from the group-level integration' do
+ it 'creates an integration from the group-level integration' do
described_class.create_from_active_default_integrations(project, :project_id, with_templates: true)
expect(project.reload.integrations.size).to eq(1)
@@ -574,7 +556,7 @@ RSpec.describe Integration do
context 'passing a group' do
let!(:subgroup) { create(:group, parent: group) }
- it 'creates a service from the group-level integration' do
+ it 'creates an integration from the group-level integration' do
described_class.create_from_active_default_integrations(subgroup, :group_id)
expect(subgroup.reload.integrations.size).to eq(1)
@@ -584,11 +566,11 @@ RSpec.describe Integration do
end
context 'with an active subgroup' do
- let!(:subgroup_integration) { create(:prometheus_service, group: subgroup, project: nil, api_url: 'https://prometheus.subgroup.com/') }
+ let!(:subgroup_integration) { create(:prometheus_integration, group: subgroup, project: nil, api_url: 'https://prometheus.subgroup.com/') }
let!(:subgroup) { create(:group, parent: group) }
let(:project) { create(:project, group: subgroup) }
- it 'creates a service from the subgroup-level integration' do
+ it 'creates an integration from the subgroup-level integration' do
described_class.create_from_active_default_integrations(project, :project_id, with_templates: true)
expect(project.reload.integrations.size).to eq(1)
@@ -601,7 +583,7 @@ RSpec.describe Integration do
context 'traversal queries' do
shared_examples 'correct ancestor order' do
- it 'creates a service from the subgroup-level integration' do
+ it 'creates an integration from the subgroup-level integration' do
described_class.create_from_active_default_integrations(sub_subgroup, :group_id)
sub_subgroup.reload
@@ -611,10 +593,10 @@ RSpec.describe Integration do
expect(sub_subgroup.integrations.first.inherit_from_id).to eq(subgroup_integration.id)
end
- context 'having a service inheriting settings' do
- let!(:subgroup_integration) { create(:prometheus_service, group: subgroup, project: nil, inherit_from_id: group_integration.id, api_url: 'https://prometheus.subgroup.com/') }
+ context 'having an integration inheriting settings' do
+ let!(:subgroup_integration) { create(:prometheus_integration, group: subgroup, project: nil, inherit_from_id: group_integration.id, api_url: 'https://prometheus.subgroup.com/') }
- it 'creates a service from the group-level integration' do
+ it 'creates an integration from the group-level integration' do
described_class.create_from_active_default_integrations(sub_subgroup, :group_id)
sub_subgroup.reload
@@ -656,11 +638,11 @@ RSpec.describe Integration do
let_it_be(:subgroup2) { create(:group, parent: group) }
let_it_be(:project1) { create(:project, group: subgroup1) }
let_it_be(:project2) { create(:project, group: subgroup2) }
- let_it_be(:group_integration) { create(:prometheus_service, group: group, project: nil) }
- let_it_be(:subgroup_integration1) { create(:prometheus_service, group: subgroup1, project: nil, inherit_from_id: group_integration.id) }
- let_it_be(:subgroup_integration2) { create(:prometheus_service, group: subgroup2, project: nil) }
- let_it_be(:project_integration1) { create(:prometheus_service, group: nil, project: project1, inherit_from_id: group_integration.id) }
- let_it_be(:project_integration2) { create(:prometheus_service, group: nil, project: project2, inherit_from_id: subgroup_integration2.id) }
+ let_it_be(:group_integration) { create(:prometheus_integration, group: group, project: nil) }
+ let_it_be(:subgroup_integration1) { create(:prometheus_integration, group: subgroup1, project: nil, inherit_from_id: group_integration.id) }
+ let_it_be(:subgroup_integration2) { create(:prometheus_integration, group: subgroup2, project: nil) }
+ let_it_be(:project_integration1) { create(:prometheus_integration, group: nil, project: project1, inherit_from_id: group_integration.id) }
+ let_it_be(:project_integration2) { create(:prometheus_integration, group: nil, project: project2, inherit_from_id: subgroup_integration2.id) }
it 'returns the groups and projects inheriting from integration ancestors', :aggregate_failures do
expect(described_class.inherited_descendants_from_self_or_ancestors_from(group_integration)).to eq([subgroup_integration1, project_integration1])
@@ -669,11 +651,8 @@ RSpec.describe Integration do
end
describe '.integration_name_to_model' do
- it 'returns the model for the given service name', :aggregate_failures do
+ it 'returns the model for the given service name' do
expect(described_class.integration_name_to_model('asana')).to eq(Integrations::Asana)
- # TODO We can remove this test when all models have been namespaced:
- # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60968#note_570994955
- expect(described_class.integration_name_to_model('prometheus')).to eq(PrometheusService)
end
it 'raises an error if service name is invalid' do
@@ -897,37 +876,37 @@ RSpec.describe Integration do
end
end
- describe '.available_services_names' do
+ describe '.available_integration_names' do
it 'calls the right methods' do
- expect(described_class).to receive(:services_names).and_call_original
- expect(described_class).to receive(:dev_services_names).and_call_original
- expect(described_class).to receive(:project_specific_services_names).and_call_original
+ expect(described_class).to receive(:integration_names).and_call_original
+ expect(described_class).to receive(:dev_integration_names).and_call_original
+ expect(described_class).to receive(:project_specific_integration_names).and_call_original
- described_class.available_services_names
+ described_class.available_integration_names
end
- it 'does not call project_specific_services_names with include_project_specific false' do
- expect(described_class).to receive(:services_names).and_call_original
- expect(described_class).to receive(:dev_services_names).and_call_original
- expect(described_class).not_to receive(:project_specific_services_names)
+ it 'does not call project_specific_integration_names with include_project_specific false' do
+ expect(described_class).to receive(:integration_names).and_call_original
+ expect(described_class).to receive(:dev_integration_names).and_call_original
+ expect(described_class).not_to receive(:project_specific_integration_names)
- described_class.available_services_names(include_project_specific: false)
+ described_class.available_integration_names(include_project_specific: false)
end
- it 'does not call dev_services_names with include_dev false' do
- expect(described_class).to receive(:services_names).and_call_original
- expect(described_class).not_to receive(:dev_services_names)
- expect(described_class).to receive(:project_specific_services_names).and_call_original
+ it 'does not call dev_integration_names with include_dev false' do
+ expect(described_class).to receive(:integration_names).and_call_original
+ expect(described_class).not_to receive(:dev_integration_names)
+ expect(described_class).to receive(:project_specific_integration_names).and_call_original
- described_class.available_services_names(include_dev: false)
+ described_class.available_integration_names(include_dev: false)
end
- it { expect(described_class.available_services_names).to include('jenkins') }
+ it { expect(described_class.available_integration_names).to include('jenkins') }
end
- describe '.project_specific_services_names' do
+ describe '.project_specific_integration_names' do
it do
- expect(described_class.project_specific_services_names)
+ expect(described_class.project_specific_integration_names)
.to include(*described_class::PROJECT_SPECIFIC_INTEGRATION_NAMES)
end
end
diff --git a/spec/models/integrations/asana_spec.rb b/spec/models/integrations/asana_spec.rb
index 4473478910a..f7e7eb1b0ae 100644
--- a/spec/models/integrations/asana_spec.rb
+++ b/spec/models/integrations/asana_spec.rb
@@ -3,11 +3,6 @@
require 'spec_helper'
RSpec.describe Integrations::Asana do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
context 'active' do
before do
@@ -42,13 +37,12 @@ RSpec.describe Integrations::Asana do
allow(@asana).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
api_key: 'verySecret',
restrict_to_branch: 'master'
)
end
- it 'calls Asana service to create a story' do
+ it 'calls Asana integration to create a story' do
data = create_data_for_commits("Message from commit. related to ##{gid}")
expected_message = "#{data[:user_name]} pushed to branch #{data[:ref]} of #{project.full_name} ( #{data[:commits][0][:url]} ): #{data[:commits][0][:message]}"
@@ -59,7 +53,7 @@ RSpec.describe Integrations::Asana do
@asana.execute(data)
end
- it 'calls Asana service to create a story and close a task' do
+ it 'calls Asana integration to create a story and close a task' do
data = create_data_for_commits('fix #456789')
d1 = double('Asana::Resources::Task')
expect(d1).to receive(:add_comment)
diff --git a/spec/models/integrations/assembla_spec.rb b/spec/models/integrations/assembla_spec.rb
index e5972bce95d..960dfea3dc4 100644
--- a/spec/models/integrations/assembla_spec.rb
+++ b/spec/models/integrations/assembla_spec.rb
@@ -5,11 +5,6 @@ require 'spec_helper'
RSpec.describe Integrations::Assembla do
include StubRequests
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe "Execute" do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -19,7 +14,6 @@ RSpec.describe Integrations::Assembla do
allow(@assembla_integration).to receive_messages(
project_id: project.id,
project: project,
- service_hook: true,
token: 'verySecret',
subdomain: 'project_name'
)
diff --git a/spec/models/integrations/bamboo_spec.rb b/spec/models/integrations/bamboo_spec.rb
index 39966f7978d..73ebf404828 100644
--- a/spec/models/integrations/bamboo_spec.rb
+++ b/spec/models/integrations/bamboo_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
let_it_be(:project) { create(:project) }
- subject(:service) do
+ subject(:integration) do
described_class.create!(
project: project,
properties: {
@@ -22,53 +22,48 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
)
end
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when active' do
before do
- subject.active = true
+ integration.active = true
end
it { is_expected.to validate_presence_of(:build_key) }
it { is_expected.to validate_presence_of(:bamboo_url) }
- it_behaves_like 'issue tracker service URL attribute', :bamboo_url
+ it_behaves_like 'issue tracker integration URL attribute', :bamboo_url
describe '#username' do
it 'does not validate the presence of username if password is nil' do
- subject.password = nil
+ integration.password = nil
- expect(subject).not_to validate_presence_of(:username)
+ expect(integration).not_to validate_presence_of(:username)
end
it 'validates the presence of username if password is present' do
- subject.password = 'secret'
+ integration.password = 'secret'
- expect(subject).to validate_presence_of(:username)
+ expect(integration).to validate_presence_of(:username)
end
end
describe '#password' do
it 'does not validate the presence of password if username is nil' do
- subject.username = nil
+ integration.username = nil
- expect(subject).not_to validate_presence_of(:password)
+ expect(integration).not_to validate_presence_of(:password)
end
it 'validates the presence of password if username is present' do
- subject.username = 'john'
+ integration.username = 'john'
- expect(subject).to validate_presence_of(:password)
+ expect(integration).to validate_presence_of(:password)
end
end
end
- context 'when service is inactive' do
+ context 'when inactive' do
before do
- subject.active = false
+ integration.active = false
end
it { is_expected.not_to validate_presence_of(:build_key) }
@@ -82,45 +77,38 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
describe 'before_update :reset_password' do
context 'when a password was previously set' do
it 'resets password if url changed' do
- bamboo_integration = service
-
- bamboo_integration.bamboo_url = 'http://gitlab1.com'
- bamboo_integration.save!
+ integration.bamboo_url = 'http://gitlab1.com'
+ integration.save!
- expect(bamboo_integration.password).to be_nil
+ expect(integration.password).to be_nil
end
it 'does not reset password if username changed' do
- bamboo_integration = service
+ integration.username = 'some_name'
+ integration.save!
- bamboo_integration.username = 'some_name'
- bamboo_integration.save!
-
- expect(bamboo_integration.password).to eq('password')
+ expect(integration.password).to eq('password')
end
it "does not reset password if new url is set together with password, even if it's the same password" do
- bamboo_integration = service
-
- bamboo_integration.bamboo_url = 'http://gitlab_edited.com'
- bamboo_integration.password = 'password'
- bamboo_integration.save!
+ integration.bamboo_url = 'http://gitlab_edited.com'
+ integration.password = 'password'
+ integration.save!
- expect(bamboo_integration.password).to eq('password')
- expect(bamboo_integration.bamboo_url).to eq('http://gitlab_edited.com')
+ expect(integration.password).to eq('password')
+ expect(integration.bamboo_url).to eq('http://gitlab_edited.com')
end
end
it 'saves password if new url is set together with password when no password was previously set' do
- bamboo_integration = service
- bamboo_integration.password = nil
+ integration.password = nil
- bamboo_integration.bamboo_url = 'http://gitlab_edited.com'
- bamboo_integration.password = 'password'
- bamboo_integration.save!
+ integration.bamboo_url = 'http://gitlab_edited.com'
+ integration.password = 'password'
+ integration.save!
- expect(bamboo_integration.password).to eq('password')
- expect(bamboo_integration.bamboo_url).to eq('http://gitlab_edited.com')
+ expect(integration.password).to eq('password')
+ expect(integration.bamboo_url).to eq('http://gitlab_edited.com')
end
end
end
@@ -129,29 +117,29 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
it 'runs update and build action' do
stub_update_and_build_request
- subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
+ integration.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
end
end
describe '#build_page' do
it 'returns the contents of the reactive cache' do
- stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
+ stub_reactive_cache(integration, { build_page: 'foo' }, 'sha', 'ref')
- expect(service.build_page('sha', 'ref')).to eq('foo')
+ expect(integration.build_page('sha', 'ref')).to eq('foo')
end
end
describe '#commit_status' do
it 'returns the contents of the reactive cache' do
- stub_reactive_cache(service, { commit_status: 'foo' }, 'sha', 'ref')
+ stub_reactive_cache(integration, { commit_status: 'foo' }, 'sha', 'ref')
- expect(service.commit_status('sha', 'ref')).to eq('foo')
+ expect(integration.commit_status('sha', 'ref')).to eq('foo')
end
end
shared_examples 'reactive cache calculation' do
describe '#build_page' do
- subject { service.calculate_reactive_cache('123', 'unused')[:build_page] }
+ subject { integration.calculate_reactive_cache('123', 'unused')[:build_page] }
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
@@ -183,7 +171,7 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
end
describe '#commit_status' do
- subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
+ subject { integration.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
stub_request(status: 500)
diff --git a/spec/models/integrations/base_chat_notification_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb
index 656eaa3bbdd..ac4031a9b7d 100644
--- a/spec/models/integrations/base_chat_notification_spec.rb
+++ b/spec/models/integrations/base_chat_notification_spec.rb
@@ -15,26 +15,8 @@ RSpec.describe Integrations::BaseChatNotification do
it { is_expected.to validate_inclusion_of(:labels_to_be_notified_behavior).in_array(%w[match_any match_all]).allow_blank }
end
- describe '#can_test?' do
- context 'with empty repository' do
- it 'returns true' do
- subject.project = create(:project, :empty_repo)
-
- expect(subject.can_test?).to be true
- end
- end
-
- context 'with repository' do
- it 'returns true' do
- subject.project = create(:project, :repository)
-
- expect(subject.can_test?).to be true
- end
- end
- end
-
describe '#execute' do
- subject(:chat_service) { described_class.new }
+ subject(:chat_integration) { described_class.new }
let_it_be(:project) { create(:project, :repository) }
@@ -43,10 +25,9 @@ RSpec.describe Integrations::BaseChatNotification do
let(:data) { Gitlab::DataBuilder::Push.build_sample(subject.project, user) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
webhook: webhook_url
)
@@ -57,8 +38,8 @@ RSpec.describe Integrations::BaseChatNotification do
context 'with a repository' do
it 'returns true' do
- expect(chat_service).to receive(:notify).and_return(true)
- expect(chat_service.execute(data)).to be true
+ expect(chat_integration).to receive(:notify).and_return(true)
+ expect(chat_integration.execute(data)).to be true
end
end
@@ -66,8 +47,8 @@ RSpec.describe Integrations::BaseChatNotification do
it 'returns true' do
subject.project = create(:project, :empty_repo)
- expect(chat_service).to receive(:notify).and_return(true)
- expect(chat_service.execute(data)).to be true
+ expect(chat_integration).to receive(:notify).and_return(true)
+ expect(chat_integration.execute(data)).to be true
end
end
@@ -75,8 +56,8 @@ RSpec.describe Integrations::BaseChatNotification do
it 'does not remove spaces' do
allow(project).to receive(:full_name).and_return('Project Name')
- expect(chat_service).to receive(:get_message).with(any_args, hash_including(project_name: 'Project Name'))
- chat_service.execute(data)
+ expect(chat_integration).to receive(:get_message).with(any_args, hash_including(project_name: 'Project Name'))
+ chat_integration.execute(data)
end
end
@@ -89,76 +70,76 @@ RSpec.describe Integrations::BaseChatNotification do
let(:data) { Gitlab::DataBuilder::Note.build(note, user) }
- shared_examples 'notifies the chat service' do
+ shared_examples 'notifies the chat integration' do
specify do
- expect(chat_service).to receive(:notify).with(any_args)
+ expect(chat_integration).to receive(:notify).with(any_args)
- chat_service.execute(data)
+ chat_integration.execute(data)
end
end
- shared_examples 'does not notify the chat service' do
+ shared_examples 'does not notify the chat integration' do
specify do
- expect(chat_service).not_to receive(:notify).with(any_args)
+ expect(chat_integration).not_to receive(:notify).with(any_args)
- chat_service.execute(data)
+ chat_integration.execute(data)
end
end
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
context 'with label filter' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: '~Bug') }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: '~Bug') }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
context 'MergeRequest events' do
let(:data) { create(:merge_request, labels: [label]).to_hook_data(user) }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'Issue events' do
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is not defined' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: label_filter) }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: label_filter) }
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is blank' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: label_filter, labels_to_be_notified_behavior: '') }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: label_filter, labels_to_be_notified_behavior: '') }
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is match_any' do
- subject(:chat_service) do
+ subject(:chat_integration) do
described_class.new(
labels_to_be_notified: label_filter,
labels_to_be_notified_behavior: 'match_any'
@@ -168,24 +149,24 @@ RSpec.describe Integrations::BaseChatNotification do
context 'no label filter' do
let(:label_filter) { nil }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is match_all' do
- subject(:chat_service) do
+ subject(:chat_integration) do
described_class.new(
labels_to_be_notified: label_filter,
labels_to_be_notified_behavior: 'match_all'
@@ -195,31 +176,31 @@ RSpec.describe Integrations::BaseChatNotification do
context 'no label filter' do
let(:label_filter) { nil }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'labels matches exactly' do
let(:label_filter) { '~Bug, ~Backend, ~Community contribution' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'labels matches but object has more' do
let(:label_filter) { '~Bug, ~Backend' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'labels are distributed on multiple objects' do
@@ -241,22 +222,22 @@ RSpec.describe Integrations::BaseChatNotification do
})
end
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
end
end
context 'with "channel" property' do
before do
- allow(chat_service).to receive(:channel).and_return(channel)
+ allow(chat_integration).to receive(:channel).and_return(channel)
end
context 'empty string' do
let(:channel) { '' }
it 'does not include the channel' do
- expect(chat_service).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end
@@ -264,20 +245,20 @@ RSpec.describe Integrations::BaseChatNotification do
let(:channel) { ' ' }
it 'does not include the channel' do
- expect(chat_service).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end
end
shared_examples 'with channel specified' do |channel, expected_channels|
before do
- allow(chat_service).to receive(:push_channel).and_return(channel)
+ allow(chat_integration).to receive(:push_channel).and_return(channel)
end
it 'notifies all channels' do
- expect(chat_service).to receive(:notify).with(any_args, hash_including(channel: expected_channels)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_including(channel: expected_channels)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end
diff --git a/spec/models/integrations/base_issue_tracker_spec.rb b/spec/models/integrations/base_issue_tracker_spec.rb
index 0f1bc39929a..25e27e96a84 100644
--- a/spec/models/integrations/base_issue_tracker_spec.rb
+++ b/spec/models/integrations/base_issue_tracker_spec.rb
@@ -7,26 +7,26 @@ RSpec.describe Integrations::BaseIssueTracker do
let(:project) { create :project }
describe 'only one issue tracker per project' do
- let(:service) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
+ let(:integration) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
before do
create(:custom_issue_tracker_integration, project: project)
end
- context 'when service is changed manually by user' do
+ context 'when integration is changed manually by user' do
it 'executes the validation' do
- valid = service.valid?(:manual_change)
+ valid = integration.valid?(:manual_change)
expect(valid).to be_falsey
- expect(service.errors[:base]).to include(
+ expect(integration.errors[:base]).to include(
'Another issue tracker is already in use. Only one issue tracker service can be active at a time'
)
end
end
- context 'when service is changed internally' do
+ context 'when integration is changed internally' do
it 'does not execute the validation' do
- expect(service.valid?).to be_truthy
+ expect(integration.valid?).to be_truthy
end
end
end
diff --git a/spec/models/integrations/bugzilla_spec.rb b/spec/models/integrations/bugzilla_spec.rb
index e75fa8dd4d4..432306c8fa8 100644
--- a/spec/models/integrations/bugzilla_spec.rb
+++ b/spec/models/integrations/bugzilla_spec.rb
@@ -3,13 +3,8 @@
require 'spec_helper'
RSpec.describe Integrations::Bugzilla do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -17,12 +12,12 @@ RSpec.describe Integrations::Bugzilla do
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
- it_behaves_like 'issue tracker service URL attribute', :issues_url
- it_behaves_like 'issue tracker service URL attribute', :new_issue_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :issues_url
+ it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/buildkite_spec.rb b/spec/models/integrations/buildkite_spec.rb
index 7dc81da7003..4207ae0d555 100644
--- a/spec/models/integrations/buildkite_spec.rb
+++ b/spec/models/integrations/buildkite_spec.rb
@@ -8,34 +8,32 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
let(:project) { create(:project) }
- subject(:service) do
+ subject(:integration) do
described_class.create!(
project: project,
properties: {
- service_hook: true,
project_url: 'https://buildkite.com/organization-name/example-pipeline',
token: 'secret-sauce-webhook-token:secret-sauce-status-token'
}
)
end
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
+ it_behaves_like Integrations::HasWebHook do
+ let(:hook_url) { 'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token' }
end
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:token) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -47,7 +45,7 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
describe '.supported_events' do
it 'supports push, merge_request, and tag_push events' do
- expect(service.supported_events).to eq %w(push merge_request tag_push)
+ expect(integration.supported_events).to eq %w(push merge_request tag_push)
end
end
@@ -57,18 +55,18 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
end
it 'always activates SSL verification after saved' do
- service.create_service_hook(enable_ssl_verification: false)
+ integration.create_service_hook(enable_ssl_verification: false)
- service.enable_ssl_verification = false
- service.active = true
+ integration.enable_ssl_verification = false
+ integration.active = true
- expect { service.save! }
- .to change { service.service_hook.enable_ssl_verification }.from(false).to(true)
+ expect { integration.save! }
+ .to change { integration.service_hook.enable_ssl_verification }.from(false).to(true)
end
- describe '#webhook_url' do
+ describe '#hook_url' do
it 'returns the webhook url' do
- expect(service.webhook_url).to eq(
+ expect(integration.hook_url).to eq(
'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
)
end
@@ -76,7 +74,7 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
describe '#commit_status_path' do
it 'returns the correct status page' do
- expect(service.commit_status_path('2ab7834c')).to eq(
+ expect(integration.commit_status_path('2ab7834c')).to eq(
'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
)
end
@@ -84,7 +82,7 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
describe '#build_page' do
it 'returns the correct build page' do
- expect(service.build_page('2ab7834c', nil)).to eq(
+ expect(integration.build_page('2ab7834c', nil)).to eq(
'https://buildkite.com/organization-name/example-pipeline/builds?commit=2ab7834c'
)
end
@@ -92,9 +90,9 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
describe '#commit_status' do
it 'returns the contents of the reactive cache' do
- stub_reactive_cache(service, { commit_status: 'foo' }, 'sha', 'ref')
+ stub_reactive_cache(integration, { commit_status: 'foo' }, 'sha', 'ref')
- expect(service.commit_status('sha', 'ref')).to eq('foo')
+ expect(integration.commit_status('sha', 'ref')).to eq('foo')
end
end
@@ -104,7 +102,7 @@ RSpec.describe Integrations::Buildkite, :use_clean_rails_memory_store_caching do
'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123'
end
- subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
+ subject { integration.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
stub_request(status: 500)
diff --git a/spec/models/integrations/campfire_spec.rb b/spec/models/integrations/campfire_spec.rb
index d68f8e0bd4e..0044e6fae21 100644
--- a/spec/models/integrations/campfire_spec.rb
+++ b/spec/models/integrations/campfire_spec.rb
@@ -5,13 +5,8 @@ require 'spec_helper'
RSpec.describe Integrations::Campfire do
include StubRequests
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -19,7 +14,7 @@ RSpec.describe Integrations::Campfire do
it { is_expected.to validate_presence_of(:token) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -37,7 +32,6 @@ RSpec.describe Integrations::Campfire do
allow(@campfire_integration).to receive_messages(
project_id: project.id,
project: project,
- service_hook: true,
token: 'verySecret',
subdomain: 'project-name',
room: 'test-room'
diff --git a/spec/models/integrations/confluence_spec.rb b/spec/models/integrations/confluence_spec.rb
index 08e18c99376..e2f9316bc95 100644
--- a/spec/models/integrations/confluence_spec.rb
+++ b/spec/models/integrations/confluence_spec.rb
@@ -3,17 +3,12 @@
require 'spec_helper'
RSpec.describe Integrations::Confluence do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
before do
subject.active = active
end
- context 'when service is active' do
+ context 'when integration is active' do
let(:active) { true }
it { is_expected.not_to allow_value('https://example.com').for(:confluence_url) }
@@ -35,7 +30,7 @@ RSpec.describe Integrations::Confluence do
it { is_expected.to validate_presence_of(:confluence_url) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
let(:active) { false }
it { is_expected.not_to validate_presence_of(:confluence_url) }
@@ -71,13 +66,13 @@ RSpec.describe Integrations::Confluence do
subject { project.project_setting.has_confluence? }
- it 'sets the property to true when service is active' do
+ it 'sets the property to true when integration is active' do
create(:confluence_integration, project: project, active: true)
is_expected.to be(true)
end
- it 'sets the property to false when service is not active' do
+ it 'sets the property to false when integration is not active' do
create(:confluence_integration, project: project, active: false)
is_expected.to be(false)
diff --git a/spec/models/integrations/custom_issue_tracker_spec.rb b/spec/models/integrations/custom_issue_tracker_spec.rb
index 25f2648e738..e1ffe7a74f0 100644
--- a/spec/models/integrations/custom_issue_tracker_spec.rb
+++ b/spec/models/integrations/custom_issue_tracker_spec.rb
@@ -3,13 +3,8 @@
require 'spec_helper'
RSpec.describe Integrations::CustomIssueTracker do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -17,12 +12,12 @@ RSpec.describe Integrations::CustomIssueTracker do
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
- it_behaves_like 'issue tracker service URL attribute', :issues_url
- it_behaves_like 'issue tracker service URL attribute', :new_issue_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :issues_url
+ it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/datadog_spec.rb b/spec/models/integrations/datadog_spec.rb
index 165b21840e0..e2749ab1bc1 100644
--- a/spec/models/integrations/datadog_spec.rb
+++ b/spec/models/integrations/datadog_spec.rb
@@ -10,13 +10,13 @@ RSpec.describe Integrations::Datadog do
let(:active) { true }
let(:dd_site) { 'datadoghq.com' }
- let(:default_url) { 'https://webhooks-http-intake.logs.datadoghq.com/v1/input/' }
+ let(:default_url) { 'https://webhooks-http-intake.logs.datadoghq.com/api/v2/webhook' }
let(:api_url) { '' }
let(:api_key) { SecureRandom.hex(32) }
let(:dd_env) { 'ci' }
let(:dd_service) { 'awesome-gitlab' }
- let(:expected_hook_url) { default_url + api_key + "?env=#{dd_env}&service=#{dd_service}" }
+ let(:expected_hook_url) { default_url + "?dd-api-key=#{api_key}&env=#{dd_env}&service=#{dd_service}" }
let(:instance) do
described_class.new(
@@ -38,9 +38,9 @@ RSpec.describe Integrations::Datadog do
let(:pipeline_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
let(:build_data) { Gitlab::DataBuilder::Build.build(build) }
- describe 'associations' do
- it { is_expected.to belong_to(:project) }
- it { is_expected.to have_one(:service_hook) }
+ it_behaves_like Integrations::HasWebHook do
+ let(:integration) { instance }
+ let(:hook_url) { "#{described_class::URL_TEMPLATE % { datadog_domain: dd_site }}?dd-api-key=#{api_key}&env=#{dd_env}&service=#{dd_service}" }
end
describe 'validations' do
@@ -65,7 +65,7 @@ RSpec.describe Integrations::Datadog do
context 'with custom api_url' do
let(:dd_site) { '' }
- let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' }
+ let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/api/v2/webhook' }
it { is_expected.not_to validate_presence_of(:datadog_site) }
it { is_expected.to validate_presence_of(:api_url) }
@@ -91,7 +91,7 @@ RSpec.describe Integrations::Datadog do
end
end
- context 'when service is not active' do
+ context 'when integration is not active' do
let(:active) { false }
it { is_expected.to be_valid }
@@ -107,9 +107,9 @@ RSpec.describe Integrations::Datadog do
end
context 'with custom URL' do
- let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' }
+ let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/api/v2/webhook' }
- it { is_expected.to eq(api_url + api_key + "?env=#{dd_env}&service=#{dd_service}") }
+ it { is_expected.to eq(api_url + "?dd-api-key=#{api_key}&env=#{dd_env}&service=#{dd_service}") }
context 'blank' do
let(:api_url) { '' }
@@ -122,7 +122,7 @@ RSpec.describe Integrations::Datadog do
let(:dd_service) { '' }
let(:dd_env) { '' }
- it { is_expected.to eq(default_url + api_key) }
+ it { is_expected.to eq(default_url + "?dd-api-key=#{api_key}") }
end
end
diff --git a/spec/models/integrations/discord_spec.rb b/spec/models/integrations/discord_spec.rb
index bff6a8ee5b2..b85620782c1 100644
--- a/spec/models/integrations/discord_spec.rb
+++ b/spec/models/integrations/discord_spec.rb
@@ -11,7 +11,9 @@ RSpec.describe Integrations::Discord do
embeds: [
include(
author: include(name: be_present),
- description: be_present
+ description: be_present,
+ color: be_present,
+ timestamp: be_present
)
]
}
@@ -33,7 +35,6 @@ RSpec.describe Integrations::Discord do
allow(subject).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
webhook: webhook_url
)
@@ -47,15 +48,19 @@ RSpec.describe Integrations::Discord do
allow(client).to receive(:execute).and_yield(builder)
end
- subject.execute(sample_data)
+ freeze_time do
+ subject.execute(sample_data)
- expect(builder.to_json_hash[:embeds].first).to include(
- description: start_with("#{user.name} pushed to branch [master](http://localhost/#{project.namespace.path}/#{project.path}/commits/master) of"),
- author: hash_including(
- icon_url: start_with('https://www.gravatar.com/avatar/'),
- name: user.name
+ expect(builder.to_json_hash[:embeds].first).to include(
+ description: start_with("#{user.name} pushed to branch [master](http://localhost/#{project.namespace.path}/#{project.path}/commits/master) of"),
+ author: hash_including(
+ icon_url: start_with('https://www.gravatar.com/avatar/'),
+ name: user.name
+ ),
+ color: 16543014,
+ timestamp: Time.now.utc.iso8601
)
- )
+ end
end
context 'DNS rebind to local address' do
diff --git a/spec/models/integrations/drone_ci_spec.rb b/spec/models/integrations/drone_ci_spec.rb
index 137f078edca..062e23d628e 100644
--- a/spec/models/integrations/drone_ci_spec.rb
+++ b/spec/models/integrations/drone_ci_spec.rb
@@ -5,11 +5,6 @@ require 'spec_helper'
RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
- describe 'associations' do
- it { is_expected.to belong_to(:project) }
- it { is_expected.to have_one(:service_hook) }
- end
-
describe 'validations' do
context 'active' do
before do
@@ -18,7 +13,7 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:drone_url) }
- it_behaves_like 'issue tracker service URL attribute', :drone_url
+ it_behaves_like 'issue tracker integration URL attribute', :drone_url
end
context 'inactive' do
@@ -32,7 +27,15 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
shared_context :drone_ci_integration do
- let(:drone) { described_class.new }
+ subject(:drone) do
+ described_class.new(
+ project: project,
+ active: true,
+ drone_url: drone_url,
+ token: token
+ )
+ end
+
let(:project) { create(:project, :repository, name: 'project') }
let(:path) { project.full_path }
let(:drone_url) { 'http://drone.example.com' }
@@ -45,16 +48,6 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
let(:build_page) { "#{drone_url}/gitlab/#{path}/redirect/commits/#{sha}?branch=#{branch}" }
let(:commit_status_path) { "#{drone_url}/gitlab/#{path}/commits/#{sha}?branch=#{branch}&access_token=#{token}" }
- before do
- allow(drone).to receive_messages(
- project_id: project.id,
- project: project,
- active: true,
- drone_url: drone_url,
- token: token
- )
- end
-
def stub_request(status: 200, body: nil)
body ||= %q({"status":"success"})
@@ -66,7 +59,21 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
end
- describe "service page/path methods" do
+ it_behaves_like Integrations::HasWebHook do
+ include_context :drone_ci_integration
+
+ let(:integration) { drone }
+ let(:hook_url) { "#{drone_url}/hook?owner=#{project.namespace.full_path}&name=#{project.path}&access_token=#{token}" }
+
+ it 'does not create a hook if project is not present' do
+ integration.project = nil
+ integration.instance = true
+
+ expect { integration.save! }.not_to change(ServiceHook, :count)
+ end
+ end
+
+ describe "integration page/path methods" do
include_context :drone_ci_integration
it { expect(drone.build_page(sha, branch)).to eq(build_page) }
@@ -137,10 +144,17 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
- it do
- service_hook = double
- expect(service_hook).to receive(:execute)
- expect(drone).to receive(:service_hook).and_return(service_hook)
+ it 'executes the webhook' do
+ expect(drone).to receive(:execute_web_hook!).with(push_sample_data)
+
+ drone.execute(push_sample_data)
+ end
+
+ it 'does not try to execute the webhook if the integration is not in a project' do
+ drone.project = nil
+ drone.instance = true
+
+ expect(drone).not_to receive(:execute_web_hook!)
drone.execute(push_sample_data)
end
diff --git a/spec/models/integrations/emails_on_push_spec.rb b/spec/models/integrations/emails_on_push_spec.rb
index c82d4bdff9b..bdca267f6cb 100644
--- a/spec/models/integrations/emails_on_push_spec.rb
+++ b/spec/models/integrations/emails_on_push_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Integrations::EmailsOnPush do
let_it_be(:project) { create_default(:project).freeze }
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -14,7 +14,7 @@ RSpec.describe Integrations::EmailsOnPush do
it { is_expected.to validate_presence_of(:recipients) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -27,7 +27,7 @@ RSpec.describe Integrations::EmailsOnPush do
stub_const("#{described_class}::RECIPIENTS_LIMIT", 2)
end
- subject(:service) { described_class.new(project: project, recipients: recipients, active: true) }
+ subject(:integration) { described_class.new(project: project, recipients: recipients, active: true) }
context 'valid number of recipients' do
let(:recipients) { 'foo@bar.com duplicate@example.com Duplicate@example.com invalid-email' }
@@ -43,14 +43,14 @@ RSpec.describe Integrations::EmailsOnPush do
it { is_expected.not_to be_valid }
it 'adds an error message' do
- service.valid?
+ integration.valid?
- expect(service.errors).to contain_exactly('Recipients can\'t exceed 2')
+ expect(integration.errors).to contain_exactly('Recipients can\'t exceed 2')
end
- context 'when service is not active' do
+ context 'when integration is not active' do
before do
- service.active = false
+ integration.active = false
end
it { is_expected.to be_valid }
diff --git a/spec/models/integrations/ewm_spec.rb b/spec/models/integrations/ewm_spec.rb
index 38897adb447..49681fefe55 100644
--- a/spec/models/integrations/ewm_spec.rb
+++ b/spec/models/integrations/ewm_spec.rb
@@ -3,13 +3,8 @@
require 'spec_helper'
RSpec.describe Integrations::Ewm do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -17,12 +12,12 @@ RSpec.describe Integrations::Ewm do
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
- it_behaves_like 'issue tracker service URL attribute', :issues_url
- it_behaves_like 'issue tracker service URL attribute', :new_issue_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :issues_url
+ it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/external_wiki_spec.rb b/spec/models/integrations/external_wiki_spec.rb
index 8c20b810301..e4d6a1c7c84 100644
--- a/spec/models/integrations/external_wiki_spec.rb
+++ b/spec/models/integrations/external_wiki_spec.rb
@@ -3,22 +3,17 @@
require 'spec_helper'
RSpec.describe Integrations::ExternalWiki do
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:external_wiki_url) }
- it_behaves_like 'issue tracker service URL attribute', :external_wiki_url
+ it_behaves_like 'issue tracker integration URL attribute', :external_wiki_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/flowdock_spec.rb b/spec/models/integrations/flowdock_spec.rb
index 189831fa32d..daafb1b3958 100644
--- a/spec/models/integrations/flowdock_spec.rb
+++ b/spec/models/integrations/flowdock_spec.rb
@@ -3,13 +3,8 @@
require 'spec_helper'
RSpec.describe Integrations::Flowdock do
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -17,7 +12,7 @@ RSpec.describe Integrations::Flowdock do
it { is_expected.to validate_presence_of(:token) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -38,7 +33,6 @@ RSpec.describe Integrations::Flowdock do
allow(flowdock_integration).to receive_messages(
project_id: project.id,
project: project,
- service_hook: true,
token: 'verySecret'
)
WebMock.stub_request(:post, api_url)
diff --git a/spec/models/integrations/irker_spec.rb b/spec/models/integrations/irker_spec.rb
index a69be1292e0..8b207e8b43e 100644
--- a/spec/models/integrations/irker_spec.rb
+++ b/spec/models/integrations/irker_spec.rb
@@ -5,13 +5,8 @@ require 'socket'
require 'json'
RSpec.describe Integrations::Irker do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -19,7 +14,7 @@ RSpec.describe Integrations::Irker do
it { is_expected.to validate_presence_of(:recipients) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -46,7 +41,6 @@ RSpec.describe Integrations::Irker do
active: true,
project: project,
project_id: project.id,
- service_hook: true,
server_host: @irker_server.addr[2],
server_port: @irker_server.addr[1],
default_irc_uri: 'irc://chat.freenode.net/',
diff --git a/spec/models/integrations/jenkins_spec.rb b/spec/models/integrations/jenkins_spec.rb
index 2374dfe4480..9eb2a7fc098 100644
--- a/spec/models/integrations/jenkins_spec.rb
+++ b/spec/models/integrations/jenkins_spec.rb
@@ -24,14 +24,14 @@ RSpec.describe Integrations::Jenkins do
let(:jenkins_authorization) { "Basic " + ::Base64.strict_encode64(jenkins_username + ':' + jenkins_password) }
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
+ it_behaves_like Integrations::HasWebHook do
+ let(:integration) { described_class.new(jenkins_params) }
+ let(:hook_url) { "http://#{ERB::Util.url_encode jenkins_username}:#{ERB::Util.url_encode jenkins_password}@jenkins.example.com/project/my_project" }
end
describe 'username validation' do
- before do
- @jenkins_service = described_class.create!(
+ let(:jenkins_integration) do
+ described_class.create!(
active: active,
project: project,
properties: {
@@ -43,9 +43,9 @@ RSpec.describe Integrations::Jenkins do
)
end
- subject { @jenkins_service }
+ subject { jenkins_integration }
- context 'when the service is active' do
+ context 'when the integration is active' do
let(:active) { true }
context 'when password was not touched' do
@@ -74,7 +74,7 @@ RSpec.describe Integrations::Jenkins do
end
end
- context 'when the service is inactive' do
+ context 'when the integration is inactive' do
let(:active) { false }
it { is_expected.not_to validate_presence_of :username }
@@ -84,7 +84,7 @@ RSpec.describe Integrations::Jenkins do
describe '#hook_url' do
let(:username) { nil }
let(:password) { nil }
- let(:jenkins_service) do
+ let(:jenkins_integration) do
described_class.new(
project: project,
properties: {
@@ -96,7 +96,7 @@ RSpec.describe Integrations::Jenkins do
)
end
- subject { jenkins_service.hook_url }
+ subject { jenkins_integration.hook_url }
context 'when the jenkins_url has no relative path' do
let(:jenkins_url) { 'http://jenkins.example.com/' }
@@ -138,10 +138,10 @@ RSpec.describe Integrations::Jenkins do
user = create(:user, username: 'username')
project = create(:project, name: 'project')
push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
- jenkins_service = described_class.create!(jenkins_params)
+ jenkins_integration = described_class.create!(jenkins_params)
stub_request(:post, jenkins_hook_url).with(headers: { 'Authorization' => jenkins_authorization })
- result = jenkins_service.test(push_sample_data)
+ result = jenkins_integration.test(push_sample_data)
expect(result).to eq({ success: true, result: '' })
end
@@ -152,20 +152,20 @@ RSpec.describe Integrations::Jenkins do
let(:namespace) { create(:group, :private) }
let(:project) { create(:project, :private, name: 'project', namespace: namespace) }
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- let(:jenkins_service) { described_class.create!(jenkins_params) }
+ let(:jenkins_integration) { described_class.create!(jenkins_params) }
before do
stub_request(:post, jenkins_hook_url)
end
it 'invokes the Jenkins API' do
- jenkins_service.execute(push_sample_data)
+ jenkins_integration.execute(push_sample_data)
expect(a_request(:post, jenkins_hook_url)).to have_been_made.once
end
it 'adds default web hook headers to the request' do
- jenkins_service.execute(push_sample_data)
+ jenkins_integration.execute(push_sample_data)
expect(
a_request(:post, jenkins_hook_url)
@@ -174,7 +174,7 @@ RSpec.describe Integrations::Jenkins do
end
it 'request url contains properly serialized username and password' do
- jenkins_service.execute(push_sample_data)
+ jenkins_integration.execute(push_sample_data)
expect(
a_request(:post, 'http://jenkins.example.com/project/my_project')
@@ -187,8 +187,8 @@ RSpec.describe Integrations::Jenkins do
let(:project) { create(:project) }
context 'when a password was previously set' do
- before do
- @jenkins_service = described_class.create!(
+ let(:jenkins_integration) do
+ described_class.create!(
project: project,
properties: {
jenkins_url: 'http://jenkins.example.com/',
@@ -199,42 +199,47 @@ RSpec.describe Integrations::Jenkins do
end
it 'resets password if url changed' do
- @jenkins_service.jenkins_url = 'http://jenkins-edited.example.com/'
- @jenkins_service.save!
- expect(@jenkins_service.password).to be_nil
+ jenkins_integration.jenkins_url = 'http://jenkins-edited.example.com/'
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to be_nil
end
it 'resets password if username is blank' do
- @jenkins_service.username = ''
- @jenkins_service.save!
- expect(@jenkins_service.password).to be_nil
+ jenkins_integration.username = ''
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to be_nil
end
it 'does not reset password if username changed' do
- @jenkins_service.username = 'some_name'
- @jenkins_service.save!
- expect(@jenkins_service.password).to eq('password')
+ jenkins_integration.username = 'some_name'
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to eq('password')
end
it 'does not reset password if new url is set together with password, even if it\'s the same password' do
- @jenkins_service.jenkins_url = 'http://jenkins_edited.example.com/'
- @jenkins_service.password = 'password'
- @jenkins_service.save!
- expect(@jenkins_service.password).to eq('password')
- expect(@jenkins_service.jenkins_url).to eq('http://jenkins_edited.example.com/')
+ jenkins_integration.jenkins_url = 'http://jenkins_edited.example.com/'
+ jenkins_integration.password = 'password'
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to eq('password')
+ expect(jenkins_integration.jenkins_url).to eq('http://jenkins_edited.example.com/')
end
it 'resets password if url changed, even if setter called multiple times' do
- @jenkins_service.jenkins_url = 'http://jenkins1.example.com/'
- @jenkins_service.jenkins_url = 'http://jenkins1.example.com/'
- @jenkins_service.save!
- expect(@jenkins_service.password).to be_nil
+ jenkins_integration.jenkins_url = 'http://jenkins1.example.com/'
+ jenkins_integration.jenkins_url = 'http://jenkins1.example.com/'
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to be_nil
end
end
context 'when no password was previously set' do
- before do
- @jenkins_service = described_class.create!(
+ let(:jenkins_integration) do
+ described_class.create!(
project: create(:project),
properties: {
jenkins_url: 'http://jenkins.example.com/',
@@ -244,11 +249,12 @@ RSpec.describe Integrations::Jenkins do
end
it 'saves password if new url is set together with password' do
- @jenkins_service.jenkins_url = 'http://jenkins_edited.example.com/'
- @jenkins_service.password = 'password'
- @jenkins_service.save!
- expect(@jenkins_service.password).to eq('password')
- expect(@jenkins_service.jenkins_url).to eq('http://jenkins_edited.example.com/')
+ jenkins_integration.jenkins_url = 'http://jenkins_edited.example.com/'
+ jenkins_integration.password = 'password'
+ jenkins_integration.save!
+
+ expect(jenkins_integration.password).to eq('password')
+ expect(jenkins_integration.jenkins_url).to eq('http://jenkins_edited.example.com/')
end
end
end
diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb
index 23ade570383..6ca72d68bbb 100644
--- a/spec/models/integrations/jira_spec.rb
+++ b/spec/models/integrations/jira_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Integrations::Jira do
let(:password) { 'jira-password' }
let(:transition_id) { 'test27' }
let(:server_info_results) { { 'deploymentType' => 'Cloud' } }
- let(:jira_service) do
+ let(:jira_integration) do
described_class.new(
project: project,
url: url,
@@ -100,20 +100,15 @@ RSpec.describe Integrations::Jira do
end
describe '#fields' do
- let(:service) { create(:jira_service) }
+ let(:integration) { create(:jira_integration) }
- subject(:fields) { service.fields }
+ subject(:fields) { integration.fields }
it 'returns custom fields' do
expect(fields.pluck(:name)).to eq(%w[url api_url username password])
end
end
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe '.reference_pattern' do
using RSpec::Parameterized::TableSyntax
@@ -146,39 +141,35 @@ RSpec.describe Integrations::Jira do
}
end
- subject { described_class.create!(params) }
+ subject(:integration) { described_class.create!(params) }
it 'does not store data into properties' do
- expect(subject.properties).to be_nil
+ expect(integration.properties).to be_nil
end
it 'stores data in data_fields correctly' do
- service = subject
-
- expect(service.jira_tracker_data.url).to eq(url)
- expect(service.jira_tracker_data.api_url).to eq(api_url)
- expect(service.jira_tracker_data.username).to eq(username)
- expect(service.jira_tracker_data.password).to eq(password)
- expect(service.jira_tracker_data.jira_issue_transition_id).to eq(transition_id)
- expect(service.jira_tracker_data.deployment_cloud?).to be_truthy
+ expect(integration.jira_tracker_data.url).to eq(url)
+ expect(integration.jira_tracker_data.api_url).to eq(api_url)
+ expect(integration.jira_tracker_data.username).to eq(username)
+ expect(integration.jira_tracker_data.password).to eq(password)
+ expect(integration.jira_tracker_data.jira_issue_transition_id).to eq(transition_id)
+ expect(integration.jira_tracker_data.deployment_cloud?).to be_truthy
end
context 'when loading serverInfo' do
- let(:jira_service) { subject }
-
- context 'from a Cloud instance' do
+ context 'with a Cloud instance' do
let(:server_info_results) { { 'deploymentType' => 'Cloud' } }
it 'is detected' do
- expect(jira_service.jira_tracker_data.deployment_cloud?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_cloud
end
end
- context 'from a Server instance' do
+ context 'with a Server instance' do
let(:server_info_results) { { 'deploymentType' => 'Server' } }
it 'is detected' do
- expect(jira_service.jira_tracker_data.deployment_server?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_server
end
end
@@ -189,7 +180,7 @@ RSpec.describe Integrations::Jira do
let(:api_url) { 'http://example-api.atlassian.net' }
it 'deployment_type is set to cloud' do
- expect(jira_service.jira_tracker_data.deployment_cloud?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_cloud
end
end
@@ -197,7 +188,7 @@ RSpec.describe Integrations::Jira do
let(:api_url) { 'http://my-jira-api.someserver.com' }
it 'deployment_type is set to server' do
- expect(jira_service.jira_tracker_data.deployment_server?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_server
end
end
end
@@ -210,7 +201,7 @@ RSpec.describe Integrations::Jira do
it 'deployment_type is set to cloud' do
expect(Gitlab::AppLogger).to receive(:warn).with(message: "Jira API returned no ServerInfo, setting deployment_type from URL", server_info: server_info_results, url: api_url)
- expect(jira_service.jira_tracker_data.deployment_cloud?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_cloud
end
end
@@ -219,7 +210,7 @@ RSpec.describe Integrations::Jira do
it 'deployment_type is set to server' do
expect(Gitlab::AppLogger).to receive(:warn).with(message: "Jira API returned no ServerInfo, setting deployment_type from URL", server_info: server_info_results, url: api_url)
- expect(jira_service.jira_tracker_data.deployment_server?).to be_truthy
+ expect(integration.jira_tracker_data).to be_deployment_server
end
end
end
@@ -253,11 +244,11 @@ RSpec.describe Integrations::Jira do
context 'reading data' do
it 'reads data correctly' do
- expect(service.url).to eq(url)
- expect(service.api_url).to eq(api_url)
- expect(service.username).to eq(username)
- expect(service.password).to eq(password)
- expect(service.jira_issue_transition_id).to eq(transition_id)
+ expect(integration.url).to eq(url)
+ expect(integration.api_url).to eq(api_url)
+ expect(integration.username).to eq(username)
+ expect(integration.password).to eq(password)
+ expect(integration.jira_issue_transition_id).to eq(transition_id)
end
end
@@ -267,15 +258,11 @@ RSpec.describe Integrations::Jira do
let_it_be(:new_url) { 'http://jira-new.example.com' }
before do
- service.update!(username: new_username, url: new_url)
- end
-
- it 'leaves properties field emtpy' do
- # expect(service.reload.properties).to be_empty
+ integration.update!(username: new_username, url: new_url)
end
it 'stores updated data in jira_tracker_data table' do
- data = service.jira_tracker_data.reload
+ data = integration.jira_tracker_data.reload
expect(data.url).to eq(new_url)
expect(data.api_url).to eq(api_url)
@@ -288,15 +275,15 @@ RSpec.describe Integrations::Jira do
context 'when updating the url, api_url, username, or password' do
context 'when updating the integration' do
it 'updates deployment type' do
- service.update!(url: 'http://first.url')
- service.jira_tracker_data.update!(deployment_type: 'server')
+ integration.update!(url: 'http://first.url')
+ integration.jira_tracker_data.update!(deployment_type: 'server')
- expect(service.jira_tracker_data.deployment_server?).to be_truthy
+ expect(integration.jira_tracker_data.deployment_server?).to be_truthy
- service.update!(api_url: 'http://another.url')
- service.jira_tracker_data.reload
+ integration.update!(api_url: 'http://another.url')
+ integration.jira_tracker_data.reload
- expect(service.jira_tracker_data.deployment_cloud?).to be_truthy
+ expect(integration.jira_tracker_data.deployment_cloud?).to be_truthy
expect(WebMock).to have_requested(:get, /serverInfo/).twice
end
end
@@ -305,34 +292,34 @@ RSpec.describe Integrations::Jira do
let(:server_info_results) { {} }
it 'updates deployment type' do
- service.update!(url: nil, api_url: nil, active: false)
+ integration.update!(url: nil, api_url: nil, active: false)
- service.jira_tracker_data.reload
+ integration.jira_tracker_data.reload
- expect(service.jira_tracker_data.deployment_unknown?).to be_truthy
+ expect(integration.jira_tracker_data.deployment_unknown?).to be_truthy
end
end
it 'calls serverInfo for url' do
- service.update!(url: 'http://first.url')
+ integration.update!(url: 'http://first.url')
expect(WebMock).to have_requested(:get, /serverInfo/)
end
it 'calls serverInfo for api_url' do
- service.update!(api_url: 'http://another.url')
+ integration.update!(api_url: 'http://another.url')
expect(WebMock).to have_requested(:get, /serverInfo/)
end
it 'calls serverInfo for username' do
- service.update!(username: 'test-user')
+ integration.update!(username: 'test-user')
expect(WebMock).to have_requested(:get, /serverInfo/)
end
it 'calls serverInfo for password' do
- service.update!(password: 'test-password')
+ integration.update!(password: 'test-password')
expect(WebMock).to have_requested(:get, /serverInfo/)
end
@@ -340,7 +327,8 @@ RSpec.describe Integrations::Jira do
context 'when not updating the url, api_url, username, or password' do
it 'does not update deployment type' do
- expect {service.update!(jira_issue_transition_id: 'jira_issue_transition_id')}.to raise_error(ActiveRecord::RecordInvalid)
+ expect { integration.update!(jira_issue_transition_id: 'jira_issue_transition_id') }
+ .to raise_error(ActiveRecord::RecordInvalid)
expect(WebMock).not_to have_requested(:get, /serverInfo/)
end
@@ -348,9 +336,9 @@ RSpec.describe Integrations::Jira do
context 'when not allowed to test an instance or group' do
it 'does not update deployment type' do
- allow(service).to receive(:can_test?).and_return(false)
+ allow(integration).to receive(:testable?).and_return(false)
- service.update!(url: 'http://first.url')
+ integration.update!(url: 'http://first.url')
expect(WebMock).not_to have_requested(:get, /serverInfo/)
end
@@ -368,68 +356,68 @@ RSpec.describe Integrations::Jira do
end
it 'resets password if url changed' do
- service
- service.url = 'http://jira_edited.example.com'
- service.save!
+ integration
+ integration.url = 'http://jira_edited.example.com'
+ integration.save!
- expect(service.reload.url).to eq('http://jira_edited.example.com')
- expect(service.password).to be_nil
+ expect(integration.reload.url).to eq('http://jira_edited.example.com')
+ expect(integration.password).to be_nil
end
it 'does not reset password if url "changed" to the same url as before' do
- service.url = 'http://jira.example.com'
- service.save!
+ integration.url = 'http://jira.example.com'
+ integration.save!
- expect(service.reload.url).to eq('http://jira.example.com')
- expect(service.password).not_to be_nil
+ expect(integration.reload.url).to eq('http://jira.example.com')
+ expect(integration.password).not_to be_nil
end
it 'resets password if url not changed but api url added' do
- service.api_url = 'http://jira_edited.example.com/rest/api/2'
- service.save!
+ integration.api_url = 'http://jira_edited.example.com/rest/api/2'
+ integration.save!
- expect(service.reload.api_url).to eq('http://jira_edited.example.com/rest/api/2')
- expect(service.password).to be_nil
+ expect(integration.reload.api_url).to eq('http://jira_edited.example.com/rest/api/2')
+ expect(integration.password).to be_nil
end
it 'does not reset password if new url is set together with password, even if it\'s the same password' do
- service.url = 'http://jira_edited.example.com'
- service.password = password
- service.save!
+ integration.url = 'http://jira_edited.example.com'
+ integration.password = password
+ integration.save!
- expect(service.password).to eq(password)
- expect(service.url).to eq('http://jira_edited.example.com')
+ expect(integration.password).to eq(password)
+ expect(integration.url).to eq('http://jira_edited.example.com')
end
it 'resets password if url changed, even if setter called multiple times' do
- service.url = 'http://jira1.example.com/rest/api/2'
- service.url = 'http://jira1.example.com/rest/api/2'
- service.save!
+ integration.url = 'http://jira1.example.com/rest/api/2'
+ integration.url = 'http://jira1.example.com/rest/api/2'
+ integration.save!
- expect(service.password).to be_nil
+ expect(integration.password).to be_nil
end
it 'does not reset password if username changed' do
- service.username = 'some_name'
- service.save!
+ integration.username = 'some_name'
+ integration.save!
- expect(service.reload.password).to eq(password)
+ expect(integration.reload.password).to eq(password)
end
it 'does not reset password if password changed' do
- service.url = 'http://jira_edited.example.com'
- service.password = 'new_password'
- service.save!
+ integration.url = 'http://jira_edited.example.com'
+ integration.password = 'new_password'
+ integration.save!
- expect(service.reload.password).to eq('new_password')
+ expect(integration.reload.password).to eq('new_password')
end
it 'does not reset password if the password is touched and same as before' do
- service.url = 'http://jira_edited.example.com'
- service.password = password
- service.save!
+ integration.url = 'http://jira_edited.example.com'
+ integration.password = password
+ integration.save!
- expect(service.reload.password).to eq(password)
+ expect(integration.reload.password).to eq(password)
end
end
@@ -443,23 +431,23 @@ RSpec.describe Integrations::Jira do
end
it 'resets password if api url changed' do
- service.api_url = 'http://jira_edited.example.com/rest/api/2'
- service.save!
+ integration.api_url = 'http://jira_edited.example.com/rest/api/2'
+ integration.save!
- expect(service.password).to be_nil
+ expect(integration.password).to be_nil
end
it 'does not reset password if url changed' do
- service.url = 'http://jira_edited.example.com'
- service.save!
+ integration.url = 'http://jira_edited.example.com'
+ integration.save!
- expect(service.password).to eq(password)
+ expect(integration.password).to eq(password)
end
it 'resets password if api url set to empty' do
- service.update!(api_url: '')
+ integration.update!(api_url: '')
- expect(service.reload.password).to be_nil
+ expect(integration.reload.password).to be_nil
end
end
end
@@ -472,11 +460,11 @@ RSpec.describe Integrations::Jira do
end
it 'saves password if new url is set together with password' do
- service.url = 'http://jira_edited.example.com/rest/api/2'
- service.password = 'password'
- service.save!
- expect(service.reload.password).to eq('password')
- expect(service.reload.url).to eq('http://jira_edited.example.com/rest/api/2')
+ integration.url = 'http://jira_edited.example.com/rest/api/2'
+ integration.password = 'password'
+ integration.save!
+ expect(integration.reload.password).to eq('password')
+ expect(integration.reload.url).to eq('http://jira_edited.example.com/rest/api/2')
end
end
end
@@ -486,16 +474,16 @@ RSpec.describe Integrations::Jira do
# this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
context 'when data are stored in properties' do
let(:properties) { data_params }
- let!(:service) do
- create(:jira_service, :without_properties_callback, properties: properties.merge(additional: 'something'))
+ let!(:integration) do
+ create(:jira_integration, :without_properties_callback, properties: properties.merge(additional: 'something'))
end
it_behaves_like 'handles jira fields'
end
context 'when data are stored in separated fields' do
- let(:service) do
- create(:jira_service, data_params.merge(properties: {}))
+ let(:integration) do
+ create(:jira_integration, data_params.merge(properties: {}))
end
it_behaves_like 'handles jira fields'
@@ -503,8 +491,8 @@ RSpec.describe Integrations::Jira do
context 'when data are stored in both properties and separated fields' do
let(:properties) { data_params }
- let(:service) do
- create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |integration|
+ let(:integration) do
+ create(:jira_integration, :without_properties_callback, active: false, properties: properties).tap do |integration|
create(:jira_tracker_data, data_params.merge(integration: integration))
end
end
@@ -522,7 +510,7 @@ RSpec.describe Integrations::Jira do
end
it 'call the Jira API to get the issue' do
- jira_service.find_issue(issue_key)
+ jira_integration.find_issue(issue_key)
expect(WebMock).to have_requested(:get, issue_url)
end
@@ -531,7 +519,7 @@ RSpec.describe Integrations::Jira do
let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields,transitions" }
it 'calls the Jira API with the options to get the issue' do
- jira_service.find_issue(issue_key, rendered_fields: true, transitions: true)
+ jira_integration.find_issue(issue_key, rendered_fields: true, transitions: true)
expect(WebMock).to have_requested(:get, issue_url)
end
@@ -558,16 +546,16 @@ RSpec.describe Integrations::Jira do
end
subject(:close_issue) do
- jira_service.close_issue(resource, ExternalIssue.new(issue_key, project))
+ jira_integration.close_issue(resource, ExternalIssue.new(issue_key, project))
end
before do
- jira_service.jira_issue_transition_id = '999'
+ jira_integration.jira_issue_transition_id = '999'
# These stubs are needed to test Integrations::Jira#close_issue.
# We close the issue then do another request to API to check if it got closed.
# Here is stubbed the API return with a closed and an opened issues.
- open_issue = JIRA::Resource::Issue.new(jira_service.client, attrs: issue_fields.deep_stringify_keys)
+ open_issue = JIRA::Resource::Issue.new(jira_integration.client, attrs: issue_fields.deep_stringify_keys)
closed_issue = open_issue.dup
allow(open_issue).to receive(:resolution).and_return(false)
allow(closed_issue).to receive(:resolution).and_return(true)
@@ -585,7 +573,7 @@ RSpec.describe Integrations::Jira do
let(:external_issue) { ExternalIssue.new('JIRA-123', project) }
def close_issue
- jira_service.close_issue(resource, external_issue, current_user)
+ jira_integration.close_issue(resource, external_issue, current_user)
end
it 'calls Jira API' do
@@ -636,7 +624,7 @@ RSpec.describe Integrations::Jira do
context 'when "comment_on_event_enabled" is set to false' do
it 'creates Remote Link reference but does not create comment' do
- allow(jira_service).to receive_messages(comment_on_event_enabled: false)
+ allow(jira_integration).to receive_messages(comment_on_event_enabled: false)
close_issue
expect(WebMock).not_to have_requested(:post, comment_url)
@@ -709,12 +697,12 @@ RSpec.describe Integrations::Jira do
end
it 'logs exception when transition id is not valid' do
- allow(jira_service).to receive(:log_error)
+ allow(jira_integration).to receive(:log_error)
WebMock.stub_request(:post, transitions_url).with(basic_auth: %w(jira-username jira-password)).and_raise("Bad Request")
close_issue
- expect(jira_service).to have_received(:log_error).with(
+ expect(jira_integration).to have_received(:log_error).with(
"Issue transition failed",
error: hash_including(
exception_class: 'StandardError',
@@ -734,7 +722,7 @@ RSpec.describe Integrations::Jira do
context 'when custom transition IDs are blank' do
before do
- jira_service.jira_issue_transition_id = ''
+ jira_integration.jira_issue_transition_id = ''
end
it 'does not transition the issue' do
@@ -755,7 +743,7 @@ RSpec.describe Integrations::Jira do
end
before do
- jira_service.jira_issue_transition_automatic = true
+ jira_integration.jira_issue_transition_automatic = true
close_issue
end
@@ -789,7 +777,7 @@ RSpec.describe Integrations::Jira do
context 'when using multiple transition ids' do
before do
- allow(jira_service).to receive_messages(jira_issue_transition_id: '1,2,3')
+ allow(jira_integration).to receive_messages(jira_issue_transition_id: '1,2,3')
end
it 'calls the api with transition ids separated by comma' do
@@ -805,7 +793,7 @@ RSpec.describe Integrations::Jira do
end
it 'calls the api with transition ids separated by semicolon' do
- allow(jira_service).to receive_messages(jira_issue_transition_id: '1;2;3')
+ allow(jira_integration).to receive_messages(jira_issue_transition_id: '1;2;3')
close_issue
@@ -864,7 +852,7 @@ RSpec.describe Integrations::Jira do
let(:jira_issue) { ExternalIssue.new('JIRA-123', project) }
- subject { jira_service.create_cross_reference_note(jira_issue, resource, user) }
+ subject { jira_integration.create_cross_reference_note(jira_issue, resource, user) }
shared_examples 'creates a comment on Jira' do
let(:issue_url) { "#{url}/rest/api/2/issue/JIRA-123" }
@@ -936,7 +924,7 @@ RSpec.describe Integrations::Jira do
let(:server_info_results) { { 'url' => 'http://url', 'deploymentType' => 'Cloud' } }
def server_info
- jira_service.test(nil)
+ jira_integration.test(nil)
end
context 'when the test succeeds' do
@@ -946,7 +934,7 @@ RSpec.describe Integrations::Jira do
end
it 'gets Jira project with API URL if set' do
- jira_service.update!(api_url: 'http://jira.api.com')
+ jira_integration.update!(api_url: 'http://jira.api.com')
expect(server_info).to eq(success: true, result: server_info_results)
expect(WebMock).to have_requested(:get, /jira.api.com/)
@@ -961,13 +949,13 @@ RSpec.describe Integrations::Jira do
WebMock.stub_request(:get, test_url).with(basic_auth: [username, password])
.to_raise(JIRA::HTTPError.new(double(message: error_message)))
- expect(jira_service).to receive(:log_error).with(
+ expect(jira_integration).to receive(:log_error).with(
'Error sending message',
client_url: 'http://jira.example.com',
error: error_message
)
- expect(jira_service.test(nil)).to eq(success: false, result: error_message)
+ expect(jira_integration.test(nil)).to eq(success: false, result: error_message)
end
end
end
@@ -983,17 +971,17 @@ RSpec.describe Integrations::Jira do
}
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
- service = project.create_jira_service(active: true)
+ integration = project.create_jira_integration(active: true)
- expect(service.url).to eq('http://jira.sample/projects/project_a')
- expect(service.api_url).to eq('http://jira.sample/api')
+ expect(integration.url).to eq('http://jira.sample/projects/project_a')
+ expect(integration.api_url).to eq('http://jira.sample/api')
end
end
it 'removes trailing slashes from url' do
- service = described_class.new(url: 'http://jira.test.com/path/')
+ integration = described_class.new(url: 'http://jira.test.com/path/')
- expect(service.url).to eq('http://jira.test.com/path')
+ expect(integration.url).to eq('http://jira.test.com/path')
end
end
@@ -1093,19 +1081,65 @@ RSpec.describe Integrations::Jira do
describe '#issue_transition_enabled?' do
it 'returns true if automatic transitions are enabled' do
- jira_service.jira_issue_transition_automatic = true
+ jira_integration.jira_issue_transition_automatic = true
- expect(jira_service.issue_transition_enabled?).to be(true)
+ expect(jira_integration.issue_transition_enabled?).to be(true)
end
it 'returns true if custom transitions are set' do
- jira_service.jira_issue_transition_id = '1, 2, 3'
+ jira_integration.jira_issue_transition_id = '1, 2, 3'
- expect(jira_service.issue_transition_enabled?).to be(true)
+ expect(jira_integration.issue_transition_enabled?).to be(true)
end
it 'returns false if automatic and custom transitions are disabled' do
- expect(jira_service.issue_transition_enabled?).to be(false)
+ expect(jira_integration.issue_transition_enabled?).to be(false)
+ end
+ end
+
+ describe 'valid_connection? and configured?' do
+ before do
+ allow(jira_integration).to receive(:test).with(nil).and_return(test_result)
+ end
+
+ context 'when the test fails' do
+ let(:test_result) { { success: false } }
+
+ it 'is falsey' do
+ expect(jira_integration).not_to be_valid_connection
+ end
+
+ it 'implies that configured? is also falsey' do
+ expect(jira_integration).not_to be_configured
+ end
+ end
+
+ context 'when the test succeeds' do
+ let(:test_result) { { success: true } }
+
+ it 'is truthy' do
+ expect(jira_integration).to be_valid_connection
+ end
+
+ context 'when the integration is active' do
+ before do
+ jira_integration.active = true
+ end
+
+ it 'implies that configured? is also truthy' do
+ expect(jira_integration).to be_configured
+ end
+ end
+
+ context 'when the integration is inactive' do
+ before do
+ jira_integration.active = false
+ end
+
+ it 'implies that configured? is falsey' do
+ expect(jira_integration).not_to be_configured
+ end
+ end
end
end
end
diff --git a/spec/models/integrations/mattermost_slash_commands_spec.rb b/spec/models/integrations/mattermost_slash_commands_spec.rb
index c8a6584591c..b6abe00469b 100644
--- a/spec/models/integrations/mattermost_slash_commands_spec.rb
+++ b/spec/models/integrations/mattermost_slash_commands_spec.rb
@@ -5,27 +5,29 @@ require 'spec_helper'
RSpec.describe Integrations::MattermostSlashCommands do
it_behaves_like Integrations::BaseSlashCommands
- context 'Mattermost API' do
+ describe 'Mattermost API' do
let(:project) { create(:project) }
- let(:service) { project.build_mattermost_slash_commands_service }
+ let(:integration) { project.build_mattermost_slash_commands_integration }
let(:user) { create(:user) }
before do
session = ::Mattermost::Session.new(nil)
session.base_uri = 'http://mattermost.example.com'
- allow_any_instance_of(::Mattermost::Client).to receive(:with_session)
- .and_yield(session)
+ allow(session).to receive(:with_session).and_yield(session)
+ allow(::Mattermost::Session).to receive(:new).and_return(session)
end
describe '#configure' do
subject do
- service.configure(user, team_id: 'abc',
- trigger: 'gitlab', url: 'http://trigger.url',
- icon_url: 'http://icon.url/icon.png')
+ integration.configure(user,
+ team_id: 'abc',
+ trigger: 'gitlab',
+ url: 'http://trigger.url',
+ icon_url: 'http://icon.url/icon.png')
end
- context 'the requests succeeds' do
+ context 'when the request succeeds' do
before do
stub_request(:post, 'http://mattermost.example.com/api/v4/commands')
.with(body: {
@@ -48,18 +50,18 @@ RSpec.describe Integrations::MattermostSlashCommands do
)
end
- it 'saves the service' do
+ it 'saves the integration' do
expect { subject }.to change { project.integrations.count }.by(1)
end
it 'saves the token' do
subject
- expect(service.reload.token).to eq('token')
+ expect(integration.reload.token).to eq('token')
end
end
- context 'an error is received' do
+ context 'when an error is received' do
before do
stub_request(:post, 'http://mattermost.example.com/api/v4/commands')
.to_return(
@@ -86,10 +88,10 @@ RSpec.describe Integrations::MattermostSlashCommands do
describe '#list_teams' do
subject do
- service.list_teams(user)
+ integration.list_teams(user)
end
- context 'the requests succeeds' do
+ context 'when the request succeeds' do
before do
stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams')
.to_return(
@@ -104,7 +106,7 @@ RSpec.describe Integrations::MattermostSlashCommands do
end
end
- context 'an error is received' do
+ context 'when an error is received' do
before do
stub_request(:get, 'http://mattermost.example.com/api/v4/users/me/teams')
.to_return(
diff --git a/spec/models/integrations/microsoft_teams_spec.rb b/spec/models/integrations/microsoft_teams_spec.rb
index 2f1be233eb2..21b9a005746 100644
--- a/spec/models/integrations/microsoft_teams_spec.rb
+++ b/spec/models/integrations/microsoft_teams_spec.rb
@@ -3,25 +3,20 @@
require 'spec_helper'
RSpec.describe Integrations::MicrosoftTeams do
- let(:chat_service) { described_class.new }
+ let(:chat_integration) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:webhook) }
- it_behaves_like 'issue tracker service URL attribute', :webhook
+ it_behaves_like 'issue tracker integration URL attribute', :webhook
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -42,10 +37,9 @@ RSpec.describe Integrations::MicrosoftTeams do
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
webhook: webhook_url
)
@@ -58,28 +52,29 @@ RSpec.describe Integrations::MicrosoftTeams do
end
it "calls Microsoft Teams API for push events" do
- chat_service.execute(push_sample_data)
+ chat_integration.execute(push_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
it 'specifies the webhook when it is configured' do
- expect(::MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(double(:microsoft_teams_service).as_null_object)
+ integration = double(:microsoft_teams_integration).as_null_object
+ expect(::MicrosoftTeams::Notifier).to receive(:new).with(webhook_url).and_return(integration)
- chat_service.execute(push_sample_data)
+ chat_integration.execute(push_sample_data)
end
end
context 'with issue events' do
let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
let(:issues_sample_data) do
- service = Issues::CreateService.new(project: project, current_user: user, params: opts)
+ service = Issues::CreateService.new(project: project, current_user: user, params: opts, spam_params: nil)
issue = service.execute
service.hook_data(issue, 'open')
end
it "calls Microsoft Teams API" do
- chat_service.execute(issues_sample_data)
+ chat_integration.execute(issues_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -106,7 +101,7 @@ RSpec.describe Integrations::MicrosoftTeams do
end
it "calls Microsoft Teams API" do
- chat_service.execute(merge_sample_data)
+ chat_integration.execute(merge_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -126,7 +121,7 @@ RSpec.describe Integrations::MicrosoftTeams do
let(:wiki_page_sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
it "calls Microsoft Teams API" do
- chat_service.execute(wiki_page_sample_data)
+ chat_integration.execute(wiki_page_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -138,10 +133,9 @@ RSpec.describe Integrations::MicrosoftTeams do
let(:project) { create(:project, :repository, creator: user) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
webhook: webhook_url
)
@@ -159,7 +153,7 @@ RSpec.describe Integrations::MicrosoftTeams do
it "calls Microsoft Teams API for commit comment events" do
data = Gitlab::DataBuilder::Note.build(commit_note, user)
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -174,7 +168,7 @@ RSpec.describe Integrations::MicrosoftTeams do
it "calls Microsoft Teams API for merge request comment events" do
data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -188,7 +182,7 @@ RSpec.describe Integrations::MicrosoftTeams do
it "calls Microsoft Teams API for issue comment events" do
data = Gitlab::DataBuilder::Note.build(issue_note, user)
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -203,7 +197,7 @@ RSpec.describe Integrations::MicrosoftTeams do
it "calls Microsoft Teams API for snippet comment events" do
data = Gitlab::DataBuilder::Note.build(snippet_note, user)
- chat_service.execute(data)
+ chat_integration.execute(data)
expect(WebMock).to have_requested(:post, webhook_url).once
end
@@ -221,9 +215,8 @@ RSpec.describe Integrations::MicrosoftTeams do
end
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
- service_hook: true,
webhook: webhook_url
)
end
@@ -231,14 +224,14 @@ RSpec.describe Integrations::MicrosoftTeams do
shared_examples 'call Microsoft Teams API' do |branches_to_be_notified: nil|
before do
WebMock.stub_request(:post, webhook_url)
- chat_service.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
+ chat_integration.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
it 'calls Microsoft Teams API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
data[:markdown] = true
- chat_service.execute(data)
+ chat_integration.execute(data)
message = Integrations::ChatMessage::PipelineMessage.new(data)
@@ -250,11 +243,11 @@ RSpec.describe Integrations::MicrosoftTeams do
shared_examples 'does not call Microsoft Teams API' do |branches_to_be_notified: nil|
before do
- chat_service.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
+ chat_integration.branches_to_be_notified = branches_to_be_notified if branches_to_be_notified
end
it 'does not call Microsoft Teams API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
- result = chat_service.execute(data)
+ result = chat_integration.execute(data)
expect(result).to be_falsy
end
@@ -272,7 +265,7 @@ RSpec.describe Integrations::MicrosoftTeams do
context 'with default to notify_only_broken_pipelines' do
it 'does not call Microsoft Teams API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
- result = chat_service.execute(data)
+ result = chat_integration.execute(data)
expect(result).to be_falsy
end
@@ -280,7 +273,7 @@ RSpec.describe Integrations::MicrosoftTeams do
context 'with setting notify_only_broken_pipelines to false' do
before do
- chat_service.notify_only_broken_pipelines = false
+ chat_integration.notify_only_broken_pipelines = false
end
it_behaves_like 'call Microsoft Teams API'
diff --git a/spec/models/integrations/open_project_spec.rb b/spec/models/integrations/open_project_spec.rb
index e5b976dc91d..789911acae8 100644
--- a/spec/models/integrations/open_project_spec.rb
+++ b/spec/models/integrations/open_project_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Integrations::OpenProject do
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -13,11 +13,11 @@ RSpec.describe Integrations::OpenProject do
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:project_identifier_code) }
- it_behaves_like 'issue tracker service URL attribute', :url
- it_behaves_like 'issue tracker service URL attribute', :api_url
+ it_behaves_like 'issue tracker integration URL attribute', :url
+ it_behaves_like 'issue tracker integration URL attribute', :api_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -27,9 +27,4 @@ RSpec.describe Integrations::OpenProject do
it { is_expected.not_to validate_presence_of(:project_identifier_code) }
end
end
-
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
end
diff --git a/spec/models/integrations/packagist_spec.rb b/spec/models/integrations/packagist_spec.rb
index 48f7e81adca..dce96890522 100644
--- a/spec/models/integrations/packagist_spec.rb
+++ b/spec/models/integrations/packagist_spec.rb
@@ -24,23 +24,23 @@ RSpec.describe Integrations::Packagist do
let(:packagist_server) { 'https://packagist.example.com' }
let(:project) { create(:project) }
- describe "Associations" do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
+ it_behaves_like Integrations::HasWebHook do
+ let(:integration) { described_class.new(packagist_params) }
+ let(:hook_url) { "#{packagist_server}/api/update-package?username=#{packagist_username}&apiToken=#{packagist_token}" }
end
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- let(:packagist_service) { described_class.create!(packagist_params) }
+ let(:packagist_integration) { described_class.create!(packagist_params) }
before do
stub_request(:post, packagist_hook_url)
end
it 'calls Packagist API' do
- packagist_service.execute(push_sample_data)
+ packagist_integration.execute(push_sample_data)
expect(a_request(:post, packagist_hook_url)).to have_been_made.once
end
diff --git a/spec/models/integrations/pipelines_email_spec.rb b/spec/models/integrations/pipelines_email_spec.rb
index 90055b04bb8..761049f25fe 100644
--- a/spec/models/integrations/pipelines_email_spec.rb
+++ b/spec/models/integrations/pipelines_email_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
end
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -28,7 +28,7 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
it { is_expected.to validate_presence_of(:recipients) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/pivotaltracker_spec.rb b/spec/models/integrations/pivotaltracker_spec.rb
index 2ce90b6f739..bf8458a376c 100644
--- a/spec/models/integrations/pivotaltracker_spec.rb
+++ b/spec/models/integrations/pivotaltracker_spec.rb
@@ -5,13 +5,8 @@ require 'spec_helper'
RSpec.describe Integrations::Pivotaltracker do
include StubRequests
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -19,7 +14,7 @@ RSpec.describe Integrations::Pivotaltracker do
it { is_expected.to validate_presence_of(:token) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -29,9 +24,9 @@ RSpec.describe Integrations::Pivotaltracker do
end
describe 'Execute' do
- let(:service) do
- described_class.new.tap do |service|
- service.token = 'secret_api_token'
+ let(:integration) do
+ described_class.new.tap do |integration|
+ integration.token = 'secret_api_token'
end
end
@@ -59,7 +54,7 @@ RSpec.describe Integrations::Pivotaltracker do
end
it 'posts correct message' do
- service.execute(push_data)
+ integration.execute(push_data)
expect(WebMock).to have_requested(:post, stubbed_hostname(url)).with(
body: {
'source_commit' => {
@@ -77,22 +72,22 @@ RSpec.describe Integrations::Pivotaltracker do
end
context 'when allowed branches is specified' do
- let(:service) do
- super().tap do |service|
- service.restrict_to_branch = 'master,v10'
+ let(:integration) do
+ super().tap do |integration|
+ integration.restrict_to_branch = 'master,v10'
end
end
it 'posts message if branch is in the list' do
- service.execute(push_data(branch: 'master'))
- service.execute(push_data(branch: 'v10'))
+ integration.execute(push_data(branch: 'master'))
+ integration.execute(push_data(branch: 'v10'))
expect(WebMock).to have_requested(:post, stubbed_hostname(url)).twice
end
it 'does not post message if branch is not in the list' do
- service.execute(push_data(branch: 'mas'))
- service.execute(push_data(branch: 'v11'))
+ integration.execute(push_data(branch: 'mas'))
+ integration.execute(push_data(branch: 'v11'))
expect(WebMock).not_to have_requested(:post, stubbed_hostname(url))
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/integrations/prometheus_spec.rb
index a2025388fab..f6f242bf58e 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/integrations/prometheus_spec.rb
@@ -4,17 +4,13 @@ require 'spec_helper'
require 'googleauth'
-RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowplow do
+RSpec.describe Integrations::Prometheus, :use_clean_rails_memory_store_caching, :snowplow do
include PrometheusHelpers
include ReactiveCachingHelpers
let_it_be_with_reload(:project) { create(:prometheus_project) }
- let(:service) { project.prometheus_service }
-
- describe "Associations" do
- it { is_expected.to belong_to :project }
- end
+ let(:integration) { project.prometheus_integration }
context 'redirects' do
it 'does not follow redirects' do
@@ -22,7 +18,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
redirect_req_stub = stub_prometheus_request(prometheus_query_url('1'), status: 302, headers: { location: redirect_to })
redirected_req_stub = stub_prometheus_request(redirect_to, body: { 'status': 'success' })
- result = service.test
+ result = integration.test
# result = { success: false, result: error }
expect(result[:success]).to be_falsy
@@ -36,22 +32,22 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
describe 'Validations' do
context 'when manual_configuration is enabled' do
before do
- service.manual_configuration = true
+ integration.manual_configuration = true
end
it 'validates presence of api_url' do
- expect(service).to validate_presence_of(:api_url)
+ expect(integration).to validate_presence_of(:api_url)
end
end
context 'when manual configuration is disabled' do
before do
- service.manual_configuration = false
+ integration.manual_configuration = false
end
it 'does not validate presence of api_url' do
- expect(service).not_to validate_presence_of(:api_url)
- expect(service.valid?).to eq(true)
+ expect(integration).not_to validate_presence_of(:api_url)
+ expect(integration.valid?).to eq(true)
end
context 'local connections allowed' do
@@ -60,23 +56,23 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
it 'does not validate presence of api_url' do
- expect(service).not_to validate_presence_of(:api_url)
- expect(service.valid?).to eq(true)
+ expect(integration).not_to validate_presence_of(:api_url)
+ expect(integration.valid?).to eq(true)
end
end
end
context 'when the api_url domain points to localhost or local network' do
- let(:domain) { Addressable::URI.parse(service.api_url).hostname }
+ let(:domain) { Addressable::URI.parse(integration.api_url).hostname }
it 'cannot query' do
- expect(service.can_query?).to be true
+ expect(integration.can_query?).to be true
aggregate_failures do
['127.0.0.1', '192.168.2.3'].each do |url|
allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)])
- expect(service.can_query?).to be false
+ expect(integration.can_query?).to be false
end
end
end
@@ -88,14 +84,14 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
['127.0.0.1', '192.168.2.3'].each do |url|
allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)])
- expect(service.can_query?).to be true
+ expect(integration.can_query?).to be true
end
end
end
context 'with self-monitoring project and internal Prometheus' do
before do
- service.api_url = 'http://localhost:9090'
+ integration.api_url = 'http://localhost:9090'
stub_application_setting(self_monitoring_project_id: project.id)
stub_config(prometheus: { enable: true, server_address: 'localhost:9090' })
@@ -106,19 +102,19 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
['127.0.0.1', '192.168.2.3'].each do |url|
allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)])
- expect(service.can_query?).to be true
+ expect(integration.can_query?).to be true
end
end
end
it 'does not allow self-monitoring project to connect to other local URLs' do
- service.api_url = 'http://localhost:8000'
+ integration.api_url = 'http://localhost:8000'
aggregate_failures do
['127.0.0.1', '192.168.2.3'].each do |url|
allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)])
- expect(service.can_query?).to be false
+ expect(integration.can_query?).to be false
end
end
end
@@ -129,26 +125,26 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
describe 'callbacks' do
context 'after_create' do
let(:project) { create(:project) }
- let(:service) { build(:prometheus_service, project: project) }
+ let(:integration) { build(:prometheus_integration, project: project) }
- subject(:create_service) { service.save! }
+ subject(:create_integration) { integration.save! }
it 'creates default alerts' do
expect(Prometheus::CreateDefaultAlertsWorker)
.to receive(:perform_async)
.with(project.id)
- create_service
+ create_integration
end
context 'no project exists' do
- let(:service) { build(:prometheus_service, :instance) }
+ let(:integration) { build(:prometheus_integration, :instance) }
it 'does not create default alerts' do
expect(Prometheus::CreateDefaultAlertsWorker)
.not_to receive(:perform_async)
- create_service
+ create_integration
end
end
end
@@ -156,15 +152,15 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
describe '#test' do
before do
- service.manual_configuration = true
+ integration.manual_configuration = true
end
let!(:req_stub) { stub_prometheus_request(prometheus_query_url('1'), body: prometheus_value_body('vector')) }
context 'success' do
it 'reads the discovery endpoint' do
- expect(service.test[:result]).to eq('Checked API endpoint')
- expect(service.test[:success]).to be_truthy
+ expect(integration.test[:result]).to eq('Checked API endpoint')
+ expect(integration.test[:success]).to be_truthy
expect(req_stub).to have_been_requested.twice
end
end
@@ -173,7 +169,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let!(:req_stub) { stub_prometheus_request(prometheus_query_url('1'), status: 404) }
it 'fails to read the discovery endpoint' do
- expect(service.test[:success]).to be_falsy
+ expect(integration.test[:success]).to be_falsy
expect(req_stub).to have_been_requested
end
end
@@ -183,20 +179,20 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:api_url) { 'http://some_url' }
before do
- service.active = true
- service.api_url = api_url
- service.manual_configuration = manual_configuration
+ integration.active = true
+ integration.api_url = api_url
+ integration.manual_configuration = manual_configuration
end
context 'manual configuration is enabled' do
let(:manual_configuration) { true }
it 'calls valid?' do
- allow(service).to receive(:valid?).and_call_original
+ allow(integration).to receive(:valid?).and_call_original
- expect(service.prometheus_client).not_to be_nil
+ expect(integration.prometheus_client).not_to be_nil
- expect(service).to have_received(:valid?)
+ expect(integration).to have_received(:valid?)
end
end
@@ -204,7 +200,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:manual_configuration) { false }
it 'no client provided' do
- expect(service.prometheus_client).to be_nil
+ expect(integration.prometheus_client).to be_nil
end
end
@@ -219,8 +215,8 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
it 'allows local requests' do
- expect(service.prometheus_client).not_to be_nil
- expect { service.prometheus_client.ping }.not_to raise_error
+ expect(integration.prometheus_client).not_to be_nil
+ expect { integration.prometheus_client.ping }.not_to raise_error
end
end
@@ -235,7 +231,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
it 'blocks local requests' do
- expect(service.prometheus_client).to be_nil
+ expect(integration.prometheus_client).to be_nil
end
context 'with self monitoring project and internal Prometheus URL' do
@@ -250,8 +246,8 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
it 'allows local requests' do
- expect(service.prometheus_client).not_to be_nil
- expect { service.prometheus_client.ping }.not_to raise_error
+ expect(integration.prometheus_client).not_to be_nil
+ expect { integration.prometheus_client.ping }.not_to raise_error
end
end
end
@@ -278,8 +274,8 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
def stub_iap_request
- service.google_iap_service_account_json = Gitlab::Json.generate(google_iap_service_account)
- service.google_iap_audience_client_id = 'IAP_CLIENT_ID.apps.googleusercontent.com'
+ integration.google_iap_service_account_json = Gitlab::Json.generate(google_iap_service_account)
+ integration.google_iap_audience_client_id = 'IAP_CLIENT_ID.apps.googleusercontent.com'
stub_request(:post, 'https://oauth2.googleapis.com/token')
.to_return(
@@ -292,9 +288,9 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
it 'includes the authorization header' do
stub_iap_request
- expect(service.prometheus_client).not_to be_nil
- expect(service.prometheus_client.send(:options)).to have_key(:headers)
- expect(service.prometheus_client.send(:options)[:headers]).to eq(authorization: "Bearer FOO")
+ expect(integration.prometheus_client).not_to be_nil
+ expect(integration.prometheus_client.send(:options)).to have_key(:headers)
+ expect(integration.prometheus_client.send(:options)[:headers]).to eq(authorization: "Bearer FOO")
end
context 'when passed with token_credential_uri', issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/284819' do
@@ -315,7 +311,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
stub_iap_request
stub_request(:any, malicious_host).to_raise('Making additional HTTP requests is forbidden!')
- expect(service.prometheus_client).not_to be_nil
+ expect(integration.prometheus_client).not_to be_nil
end
end
end
@@ -332,7 +328,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:cluster) { create(:cluster, projects: [project]) }
it 'returns true' do
- expect(service.prometheus_available?).to be(true)
+ expect(integration.prometheus_available?).to be(true)
end
end
@@ -343,16 +339,16 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:cluster) { create(:cluster_for_group, groups: [group]) }
it 'returns true' do
- expect(service.prometheus_available?).to be(true)
+ expect(integration.prometheus_available?).to be(true)
end
it 'avoids N+1 queries' do
- service
+ integration
5.times do |i|
other_cluster = create(:cluster_for_group, groups: [group], environment_scope: i)
create(:clusters_integrations_prometheus, cluster: other_cluster)
end
- expect { service.prometheus_available? }.not_to exceed_query_limit(1)
+ expect { integration.prometheus_available? }.not_to exceed_query_limit(1)
end
end
@@ -360,7 +356,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:cluster) { create(:cluster, :instance) }
it 'returns true' do
- expect(service.prometheus_available?).to be(true)
+ expect(integration.prometheus_available?).to be(true)
end
end
end
@@ -370,7 +366,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let!(:prometheus) { create(:clusters_integrations_prometheus, :disabled, cluster: cluster) }
it 'returns false' do
- expect(service.prometheus_available?).to be(false)
+ expect(integration.prometheus_available?).to be(false)
end
end
@@ -378,78 +374,78 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
let(:cluster) { create(:cluster, projects: [project]) }
it 'returns false' do
- expect(service.prometheus_available?).to be(false)
+ expect(integration.prometheus_available?).to be(false)
end
end
context 'no clusters' do
it 'returns false' do
- expect(service.prometheus_available?).to be(false)
+ expect(integration.prometheus_available?).to be(false)
end
end
end
describe '#synchronize_service_state before_save callback' do
context 'no clusters with prometheus are installed' do
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
- service.active = false
+ integration.active = false
end
- it 'activates service when manual_configuration is enabled' do
- expect { service.update!(manual_configuration: true) }.to change { service.active }.from(false).to(true)
+ it 'activates integration when manual_configuration is enabled' do
+ expect { integration.update!(manual_configuration: true) }.to change { integration.active }.from(false).to(true)
end
- it 'keeps service inactive when manual_configuration is disabled' do
- expect { service.update!(manual_configuration: false) }.not_to change { service.active }.from(false)
+ it 'keeps integration inactive when manual_configuration is disabled' do
+ expect { integration.update!(manual_configuration: false) }.not_to change { integration.active }.from(false)
end
end
- context 'when service is active' do
+ context 'when integration is active' do
before do
- service.active = true
+ integration.active = true
end
- it 'keeps the service active when manual_configuration is enabled' do
- expect { service.update!(manual_configuration: true) }.not_to change { service.active }.from(true)
+ it 'keeps the integration active when manual_configuration is enabled' do
+ expect { integration.update!(manual_configuration: true) }.not_to change { integration.active }.from(true)
end
- it 'inactivates the service when manual_configuration is disabled' do
- expect { service.update!(manual_configuration: false) }.to change { service.active }.from(true).to(false)
+ it 'inactivates the integration when manual_configuration is disabled' do
+ expect { integration.update!(manual_configuration: false) }.to change { integration.active }.from(true).to(false)
end
end
end
context 'with prometheus installed in the cluster' do
before do
- allow(service).to receive(:prometheus_available?).and_return(true)
+ allow(integration).to receive(:prometheus_available?).and_return(true)
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
- service.active = false
+ integration.active = false
end
- it 'activates service when manual_configuration is enabled' do
- expect { service.update!(manual_configuration: true) }.to change { service.active }.from(false).to(true)
+ it 'activates integration when manual_configuration is enabled' do
+ expect { integration.update!(manual_configuration: true) }.to change { integration.active }.from(false).to(true)
end
- it 'activates service when manual_configuration is disabled' do
- expect { service.update!(manual_configuration: false) }.to change { service.active }.from(false).to(true)
+ it 'activates integration when manual_configuration is disabled' do
+ expect { integration.update!(manual_configuration: false) }.to change { integration.active }.from(false).to(true)
end
end
- context 'when service is active' do
+ context 'when integration is active' do
before do
- service.active = true
+ integration.active = true
end
- it 'keeps service active when manual_configuration is enabled' do
- expect { service.update!(manual_configuration: true) }.not_to change { service.active }.from(true)
+ it 'keeps integration active when manual_configuration is enabled' do
+ expect { integration.update!(manual_configuration: true) }.not_to change { integration.active }.from(true)
end
- it 'keeps service active when manual_configuration is disabled' do
- expect { service.update!(manual_configuration: false) }.not_to change { service.active }.from(true)
+ it 'keeps integration active when manual_configuration is disabled' do
+ expect { integration.update!(manual_configuration: false) }.not_to change { integration.active }.from(true)
end
end
end
@@ -457,20 +453,20 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
describe '#track_events after_commit callback' do
before do
- allow(service).to receive(:prometheus_available?).and_return(true)
+ allow(integration).to receive(:prometheus_available?).and_return(true)
end
context "enabling manual_configuration" do
it "tracks enable event" do
- service.update!(manual_configuration: false)
- service.update!(manual_configuration: true)
+ integration.update!(manual_configuration: false)
+ integration.update!(manual_configuration: true)
expect_snowplow_event(category: 'cluster:services:prometheus', action: 'enabled_manual_prometheus')
end
it "tracks disable event" do
- service.update!(manual_configuration: true)
- service.update!(manual_configuration: false)
+ integration.update!(manual_configuration: true)
+ integration.update!(manual_configuration: false)
expect_snowplow_event(category: 'cluster:services:prometheus', action: 'disabled_manual_prometheus')
end
@@ -479,20 +475,20 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
describe '#editable?' do
it 'is editable' do
- expect(service.editable?).to be(true)
+ expect(integration.editable?).to be(true)
end
context 'when cluster exists with prometheus enabled' do
let(:cluster) { create(:cluster, projects: [project]) }
before do
- service.update!(manual_configuration: false)
+ integration.update!(manual_configuration: false)
create(:clusters_integrations_prometheus, cluster: cluster)
end
it 'remains editable' do
- expect(service.editable?).to be(true)
+ expect(integration.editable?).to be(true)
end
end
end
@@ -536,7 +532,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowpl
end
it 'returns fields' do
- expect(service.fields).to eq(expected_fields)
+ expect(integration.fields).to eq(expected_fields)
end
end
end
diff --git a/spec/models/integrations/pushover_spec.rb b/spec/models/integrations/pushover_spec.rb
index be8dc5634a0..716a00c5bcf 100644
--- a/spec/models/integrations/pushover_spec.rb
+++ b/spec/models/integrations/pushover_spec.rb
@@ -5,13 +5,8 @@ require 'spec_helper'
RSpec.describe Integrations::Pushover do
include StubRequests
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -21,7 +16,7 @@ RSpec.describe Integrations::Pushover do
it { is_expected.to validate_presence_of(:priority) }
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -51,7 +46,6 @@ RSpec.describe Integrations::Pushover do
allow(pushover).to receive_messages(
project: project,
project_id: project.id,
- service_hook: true,
api_key: api_key,
user_key: user_key,
device: device,
diff --git a/spec/models/integrations/redmine_spec.rb b/spec/models/integrations/redmine_spec.rb
index 083585d4fed..59997d2b6f6 100644
--- a/spec/models/integrations/redmine_spec.rb
+++ b/spec/models/integrations/redmine_spec.rb
@@ -3,11 +3,6 @@
require 'spec_helper'
RSpec.describe Integrations::Redmine do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
# if redmine is set in setting the urls are set to defaults
# therefore the validation passes as the values are not nil
@@ -18,7 +13,7 @@ RSpec.describe Integrations::Redmine do
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
end
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -27,12 +22,12 @@ RSpec.describe Integrations::Redmine do
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
- it_behaves_like 'issue tracker service URL attribute', :issues_url
- it_behaves_like 'issue tracker service URL attribute', :new_issue_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :issues_url
+ it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/integrations/slack_slash_commands_spec.rb b/spec/models/integrations/slack_slash_commands_spec.rb
index a9d3c820a3c..ff89d2c6a40 100644
--- a/spec/models/integrations/slack_slash_commands_spec.rb
+++ b/spec/models/integrations/slack_slash_commands_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe Integrations::SlackSlashCommands do
}
end
- let(:service) do
- project.create_slack_slash_commands_service(
+ let(:integration) do
+ project.create_slack_slash_commands_integration(
properties: { token: 'token' },
active: true
)
@@ -30,11 +30,11 @@ RSpec.describe Integrations::SlackSlashCommands do
end
before do
- allow(service).to receive(:authorize_chat_name_url).and_return(authorize_url)
+ allow(integration).to receive(:authorize_chat_name_url).and_return(authorize_url)
end
it 'uses slack compatible links' do
- response = service.trigger(params)
+ response = integration.trigger(params)
expect(response[:text]).to include("<#{authorize_url}|connect your GitLab account>")
end
diff --git a/spec/models/integrations/slack_spec.rb b/spec/models/integrations/slack_spec.rb
index e598c528967..4661d9c8291 100644
--- a/spec/models/integrations/slack_spec.rb
+++ b/spec/models/integrations/slack_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Integrations::Slack do
stub_request(:post, "https://slack.service.url/")
end
- let_it_be(:slack_service) { create(:slack_service, branches_to_be_notified: 'all') }
+ let_it_be(:slack_integration) { create(:integrations_slack, branches_to_be_notified: 'all') }
it 'uses only known events', :aggregate_failures do
described_class::SUPPORTED_EVENTS_FOR_USAGE_LOG.each do |action|
@@ -26,7 +26,7 @@ RSpec.describe Integrations::Slack do
it 'increases the usage data counter' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(event_name, values: user.id).and_call_original
- slack_service.execute(data)
+ slack_integration.execute(data)
end
end
@@ -38,7 +38,7 @@ RSpec.describe Integrations::Slack do
it 'does not increase the usage data counter' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event).with('i_ecosystem_slack_service_pipeline_notification', values: user.id)
- slack_service.execute(data)
+ slack_integration.execute(data)
end
end
@@ -126,7 +126,7 @@ RSpec.describe Integrations::Slack do
it 'does not increase the usage data counter' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
- slack_service.execute(data)
+ slack_integration.execute(data)
end
end
end
diff --git a/spec/models/integrations/teamcity_spec.rb b/spec/models/integrations/teamcity_spec.rb
index b88a4722ad4..d425357aef0 100644
--- a/spec/models/integrations/teamcity_spec.rb
+++ b/spec/models/integrations/teamcity_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
let(:teamcity_full_url) { 'http://gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,revision:123' }
let(:project) { create(:project) }
- subject(:service) do
+ subject(:integration) do
described_class.create!(
project: project,
properties: {
@@ -22,20 +22,15 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
)
end
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:build_type) }
it { is_expected.to validate_presence_of(:teamcity_url) }
- it_behaves_like 'issue tracker service URL attribute', :teamcity_url
+ it_behaves_like 'issue tracker integration URL attribute', :teamcity_url
describe '#username' do
it 'does not validate the presence of username if password is nil' do
@@ -66,7 +61,7 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
end
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
@@ -79,71 +74,66 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
end
describe 'Callbacks' do
+ let(:teamcity_integration) { integration }
+
describe 'before_update :reset_password' do
context 'when a password was previously set' do
it 'resets password if url changed' do
- teamcity_service = service
+ teamcity_integration.teamcity_url = 'http://gitlab1.com'
+ teamcity_integration.save!
- teamcity_service.teamcity_url = 'http://gitlab1.com'
- teamcity_service.save!
-
- expect(teamcity_service.password).to be_nil
+ expect(teamcity_integration.password).to be_nil
end
it 'does not reset password if username changed' do
- teamcity_service = service
-
- teamcity_service.username = 'some_name'
- teamcity_service.save!
+ teamcity_integration.username = 'some_name'
+ teamcity_integration.save!
- expect(teamcity_service.password).to eq('password')
+ expect(teamcity_integration.password).to eq('password')
end
it "does not reset password if new url is set together with password, even if it's the same password" do
- teamcity_service = service
-
- teamcity_service.teamcity_url = 'http://gitlab_edited.com'
- teamcity_service.password = 'password'
- teamcity_service.save!
+ teamcity_integration.teamcity_url = 'http://gitlab_edited.com'
+ teamcity_integration.password = 'password'
+ teamcity_integration.save!
- expect(teamcity_service.password).to eq('password')
- expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
+ expect(teamcity_integration.password).to eq('password')
+ expect(teamcity_integration.teamcity_url).to eq('http://gitlab_edited.com')
end
end
it 'saves password if new url is set together with password when no password was previously set' do
- teamcity_service = service
- teamcity_service.password = nil
+ teamcity_integration.password = nil
- teamcity_service.teamcity_url = 'http://gitlab_edited.com'
- teamcity_service.password = 'password'
- teamcity_service.save!
+ teamcity_integration.teamcity_url = 'http://gitlab_edited.com'
+ teamcity_integration.password = 'password'
+ teamcity_integration.save!
- expect(teamcity_service.password).to eq('password')
- expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
+ expect(teamcity_integration.password).to eq('password')
+ expect(teamcity_integration.teamcity_url).to eq('http://gitlab_edited.com')
end
end
end
describe '#build_page' do
it 'returns the contents of the reactive cache' do
- stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
+ stub_reactive_cache(integration, { build_page: 'foo' }, 'sha', 'ref')
- expect(service.build_page('sha', 'ref')).to eq('foo')
+ expect(integration.build_page('sha', 'ref')).to eq('foo')
end
end
describe '#commit_status' do
it 'returns the contents of the reactive cache' do
- stub_reactive_cache(service, { commit_status: 'foo' }, 'sha', 'ref')
+ stub_reactive_cache(integration, { commit_status: 'foo' }, 'sha', 'ref')
- expect(service.commit_status('sha', 'ref')).to eq('foo')
+ expect(integration.commit_status('sha', 'ref')).to eq('foo')
end
end
describe '#calculate_reactive_cache' do
context 'build_page' do
- subject { service.calculate_reactive_cache('123', 'unused')[:build_page] }
+ subject { integration.calculate_reactive_cache('123', 'unused')[:build_page] }
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
@@ -179,7 +169,7 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
end
context 'commit_status' do
- subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
+ subject { integration.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
stub_request(status: 500)
@@ -243,25 +233,25 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
it 'handles push request correctly' do
stub_post_to_build_queue(branch: 'dev-123_branch')
- expect(service.execute(data)).to include('Ok')
+ expect(integration.execute(data)).to include('Ok')
end
it 'returns nil when ref is blank' do
data[:after] = Gitlab::Git::BLANK_SHA
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
it 'returns nil when there is no content' do
data[:total_commits_count] = 0
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
it 'returns nil when a merge request is opened for the same ref' do
create(:merge_request, source_project: project, source_branch: 'dev-123_branch')
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
end
@@ -283,26 +273,26 @@ RSpec.describe Integrations::Teamcity, :use_clean_rails_memory_store_caching do
it 'handles merge request correctly' do
stub_post_to_build_queue(branch: 'dev-123_branch')
- expect(service.execute(data)).to include('Ok')
+ expect(integration.execute(data)).to include('Ok')
end
it 'returns nil when merge request is not opened' do
data[:object_attributes][:state] = 'closed'
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
it 'returns nil unless merge request is marked as unchecked' do
data[:object_attributes][:merge_status] = 'can_be_merged'
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
end
it 'returns nil when event is not supported' do
data = { object_kind: 'foo' }
- expect(service.execute(data)).to be_nil
+ expect(integration.execute(data)).to be_nil
end
end
diff --git a/spec/models/integrations/youtrack_spec.rb b/spec/models/integrations/youtrack_spec.rb
index 314204f6fb4..f6a9dd8ef37 100644
--- a/spec/models/integrations/youtrack_spec.rb
+++ b/spec/models/integrations/youtrack_spec.rb
@@ -3,13 +3,8 @@
require 'spec_helper'
RSpec.describe Integrations::Youtrack do
- describe 'Associations' do
- it { is_expected.to belong_to :project }
- it { is_expected.to have_one :service_hook }
- end
-
describe 'Validations' do
- context 'when service is active' do
+ context 'when integration is active' do
before do
subject.active = true
end
@@ -17,11 +12,11 @@ RSpec.describe Integrations::Youtrack do
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
- it_behaves_like 'issue tracker service URL attribute', :project_url
- it_behaves_like 'issue tracker service URL attribute', :issues_url
+ it_behaves_like 'issue tracker integration URL attribute', :project_url
+ it_behaves_like 'issue tracker integration URL attribute', :issues_url
end
- context 'when service is inactive' do
+ context 'when integration is inactive' do
before do
subject.active = false
end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 390d1552c16..696b5b48cbf 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -39,216 +39,217 @@ RSpec.describe InternalId do
end
end
- describe '.generate_next' do
- subject { described_class.generate_next(id_subject, scope, usage, init) }
+ shared_examples_for 'a monotonically increasing id generator' do
+ describe '.generate_next' do
+ subject { described_class.generate_next(id_subject, scope, usage, init) }
- context 'in the absence of a record' do
- it 'creates a record if not yet present' do
- expect { subject }.to change { described_class.count }.from(0).to(1)
- end
+ context 'in the absence of a record' do
+ it 'creates a record if not yet present' do
+ expect { subject }.to change { described_class.count }.from(0).to(1)
+ end
- it 'stores record attributes' do
- subject
+ it 'stores record attributes' do
+ subject
- described_class.first.tap do |record|
- expect(record.project).to eq(project)
- expect(record.usage).to eq(usage.to_s)
+ described_class.first.tap do |record|
+ expect(record.project).to eq(project)
+ expect(record.usage).to eq(usage.to_s)
+ end
end
- end
- context 'with existing issues' do
- before do
- create_list(:issue, 2, project: project)
- described_class.delete_all
- end
+ context 'with existing issues' do
+ before do
+ create_list(:issue, 2, project: project)
+ described_class.delete_all
+ end
- it 'calculates last_value values automatically' do
- expect(subject).to eq(project.issues.size + 1)
+ it 'calculates last_value values automatically' do
+ expect(subject).to eq(project.issues.size + 1)
+ end
end
end
- context 'with concurrent inserts on table' do
- it 'looks up the record if it was created concurrently' do
- args = { **scope, usage: described_class.usages[usage.to_s] }
- record = double
- expect(described_class).to receive(:find_by).with(args).and_return(nil) # first call, record not present
- expect(described_class).to receive(:find_by).with(args).and_return(record) # second call, record was created by another process
- expect(described_class).to receive(:create!).and_raise(ActiveRecord::RecordNotUnique, 'record not unique')
- expect(record).to receive(:increment_and_save!)
-
- subject
+ it 'generates a strictly monotone, gapless sequence' do
+ seq = Array.new(10).map do
+ described_class.generate_next(issue, scope, usage, init)
end
- end
- end
+ normalized = seq.map { |i| i - seq.min }
- it 'generates a strictly monotone, gapless sequence' do
- seq = Array.new(10).map do
- described_class.generate_next(issue, scope, usage, init)
+ expect(normalized).to eq((0..seq.size - 1).to_a)
end
- normalized = seq.map { |i| i - seq.min }
-
- expect(normalized).to eq((0..seq.size - 1).to_a)
- end
- context 'there are no instances to pass in' do
- let(:id_subject) { Issue }
+ context 'there are no instances to pass in' do
+ let(:id_subject) { Issue }
- it 'accepts classes instead' do
- expect(subject).to eq(1)
+ it 'accepts classes instead' do
+ expect(subject).to eq(1)
+ end
end
- end
- context 'when executed outside of transaction' do
- it 'increments counter with in_transaction: "false"' do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
+ context 'when executed outside of transaction' do
+ it 'increments counter with in_transaction: "false"' do
+ allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :generate, usage: 'issues', in_transaction: 'false').and_call_original
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :generate, usage: 'issues', in_transaction: 'false').and_call_original
- subject
+ subject
+ end
end
- end
- context 'when executed within transaction' do
- it 'increments counter with in_transaction: "true"' do
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :generate, usage: 'issues', in_transaction: 'true').and_call_original
+ context 'when executed within transaction' do
+ it 'increments counter with in_transaction: "true"' do
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :generate, usage: 'issues', in_transaction: 'true').and_call_original
- InternalId.transaction { subject }
+ InternalId.transaction { subject }
+ end
end
end
- end
- describe '.reset' do
- subject { described_class.reset(issue, scope, usage, value) }
+ describe '.reset' do
+ subject { described_class.reset(issue, scope, usage, value) }
- context 'in the absence of a record' do
- let(:value) { 2 }
+ context 'in the absence of a record' do
+ let(:value) { 2 }
- it 'does not revert back the value' do
- expect { subject }.not_to change { described_class.count }
- expect(subject).to be_falsey
+ it 'does not revert back the value' do
+ expect { subject }.not_to change { described_class.count }
+ expect(subject).to be_falsey
+ end
end
- end
- context 'when valid iid is used to reset' do
- let!(:value) { generate_next }
+ context 'when valid iid is used to reset' do
+ let!(:value) { generate_next }
- context 'and iid is a latest one' do
- it 'does rewind and next generated value is the same' do
- expect(subject).to be_truthy
- expect(generate_next).to eq(value)
+ context 'and iid is a latest one' do
+ it 'does rewind and next generated value is the same' do
+ expect(subject).to be_truthy
+ expect(generate_next).to eq(value)
+ end
end
- end
- context 'and iid is not a latest one' do
- it 'does not rewind' do
- generate_next
+ context 'and iid is not a latest one' do
+ it 'does not rewind' do
+ generate_next
- expect(subject).to be_falsey
- expect(generate_next).to be > value
+ expect(subject).to be_falsey
+ expect(generate_next).to be > value
+ end
end
- end
- def generate_next
- described_class.generate_next(issue, scope, usage, init)
+ def generate_next
+ described_class.generate_next(issue, scope, usage, init)
+ end
end
- end
- context 'when executed outside of transaction' do
- let(:value) { 2 }
+ context 'when executed outside of transaction' do
+ let(:value) { 2 }
- it 'increments counter with in_transaction: "false"' do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
+ it 'increments counter with in_transaction: "false"' do
+ allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :reset, usage: 'issues', in_transaction: 'false').and_call_original
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :reset, usage: 'issues', in_transaction: 'false').and_call_original
- subject
+ subject
+ end
end
- end
- context 'when executed within transaction' do
- let(:value) { 2 }
+ context 'when executed within transaction' do
+ let(:value) { 2 }
- it 'increments counter with in_transaction: "true"' do
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :reset, usage: 'issues', in_transaction: 'true').and_call_original
+ it 'increments counter with in_transaction: "true"' do
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :reset, usage: 'issues', in_transaction: 'true').and_call_original
- InternalId.transaction { subject }
+ InternalId.transaction { subject }
+ end
end
end
- end
- describe '.track_greatest' do
- let(:value) { 9001 }
+ describe '.track_greatest' do
+ let(:value) { 9001 }
- subject { described_class.track_greatest(id_subject, scope, usage, value, init) }
+ subject { described_class.track_greatest(id_subject, scope, usage, value, init) }
- context 'in the absence of a record' do
- it 'creates a record if not yet present' do
- expect { subject }.to change { described_class.count }.from(0).to(1)
+ context 'in the absence of a record' do
+ it 'creates a record if not yet present' do
+ expect { subject }.to change { described_class.count }.from(0).to(1)
+ end
end
- end
- it 'stores record attributes' do
- subject
+ it 'stores record attributes' do
+ subject
- described_class.first.tap do |record|
- expect(record.project).to eq(project)
- expect(record.usage).to eq(usage.to_s)
- expect(record.last_value).to eq(value)
+ described_class.first.tap do |record|
+ expect(record.project).to eq(project)
+ expect(record.usage).to eq(usage.to_s)
+ expect(record.last_value).to eq(value)
+ end
end
- end
- context 'with existing issues' do
- before do
- create(:issue, project: project)
- described_class.delete_all
- end
+ context 'with existing issues' do
+ before do
+ create(:issue, project: project)
+ described_class.delete_all
+ end
- it 'still returns the last value to that of the given value' do
- expect(subject).to eq(value)
+ it 'still returns the last value to that of the given value' do
+ expect(subject).to eq(value)
+ end
end
- end
- context 'when value is less than the current last_value' do
- it 'returns the current last_value' do
- described_class.create!(**scope, usage: usage, last_value: 10_001)
+ context 'when value is less than the current last_value' do
+ it 'returns the current last_value' do
+ described_class.create!(**scope, usage: usage, last_value: 10_001)
- expect(subject).to eq 10_001
+ expect(subject).to eq 10_001
+ end
end
- end
- context 'there are no instances to pass in' do
- let(:id_subject) { Issue }
+ context 'there are no instances to pass in' do
+ let(:id_subject) { Issue }
- it 'accepts classes instead' do
- expect(subject).to eq(value)
+ it 'accepts classes instead' do
+ expect(subject).to eq(value)
+ end
end
- end
- context 'when executed outside of transaction' do
- it 'increments counter with in_transaction: "false"' do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
+ context 'when executed outside of transaction' do
+ it 'increments counter with in_transaction: "false"' do
+ allow(ActiveRecord::Base.connection).to receive(:transaction_open?) { false }
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :track_greatest, usage: 'issues', in_transaction: 'false').and_call_original
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :track_greatest, usage: 'issues', in_transaction: 'false').and_call_original
- subject
+ subject
+ end
end
- end
- context 'when executed within transaction' do
- it 'increments counter with in_transaction: "true"' do
- expect(InternalId::InternalIdGenerator.internal_id_transactions_total).to receive(:increment)
- .with(operation: :track_greatest, usage: 'issues', in_transaction: 'true').and_call_original
+ context 'when executed within transaction' do
+ it 'increments counter with in_transaction: "true"' do
+ expect(InternalId.internal_id_transactions_total).to receive(:increment)
+ .with(operation: :track_greatest, usage: 'issues', in_transaction: 'true').and_call_original
- InternalId.transaction { subject }
+ InternalId.transaction { subject }
+ end
end
end
end
+ context 'when the feature flag is disabled' do
+ stub_feature_flags(generate_iids_without_explicit_locking: false)
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
+
+ context 'when the feature flag is enabled' do
+ stub_feature_flags(generate_iids_without_explicit_locking: true)
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
+
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index edb93ecf4b6..441446bae60 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -128,6 +128,24 @@ RSpec.describe Issue do
end
end
+ context 'order by upvotes' do
+ let!(:issue) { create(:issue) }
+ let!(:issue2) { create(:issue) }
+ let!(:award_emoji) { create(:award_emoji, :upvote, awardable: issue2) }
+
+ describe '.order_upvotes_desc' do
+ it 'orders on upvotes' do
+ expect(described_class.order_upvotes_desc.to_a).to eq [issue2, issue]
+ end
+ end
+
+ describe '.order_upvotes_asc' do
+ it 'orders on upvotes' do
+ expect(described_class.order_upvotes_asc.to_a).to eq [issue, issue2]
+ end
+ end
+ end
+
describe '.with_alert_management_alerts' do
subject { described_class.with_alert_management_alerts }
@@ -1051,23 +1069,53 @@ RSpec.describe Issue do
describe '#check_for_spam?' do
using RSpec::Parameterized::TableSyntax
-
- where(:visibility_level, :confidential, :new_attributes, :check_for_spam?) do
- Gitlab::VisibilityLevel::PUBLIC | false | { description: 'woo' } | true
- Gitlab::VisibilityLevel::PUBLIC | false | { title: 'woo' } | true
- Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | true
- Gitlab::VisibilityLevel::PUBLIC | true | { description: 'woo' } | false
- Gitlab::VisibilityLevel::PUBLIC | false | { title: 'woo', confidential: true } | false
- Gitlab::VisibilityLevel::PUBLIC | false | { description: 'original description' } | false
- Gitlab::VisibilityLevel::INTERNAL | false | { description: 'woo' } | false
- Gitlab::VisibilityLevel::PRIVATE | false | { description: 'woo' } | false
+ let_it_be(:support_bot) { ::User.support_bot }
+
+ where(:support_bot?, :visibility_level, :confidential, :new_attributes, :check_for_spam?) do
+ ### non-support-bot cases
+ # spammable attributes changing
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { description: 'new' } | true
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new' } | true
+ # confidential to non-confidential
+ false | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | true
+ # non-confidential to confidential
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { confidential: true } | false
+ # spammable attributes changing on confidential
+ false | Gitlab::VisibilityLevel::PUBLIC | true | { description: 'new' } | false
+ # spammable attributes changing while changing to confidential
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new', confidential: true } | false
+ # spammable attribute not changing
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { description: 'original description' } | false
+ # non-spammable attribute changing
+ false | Gitlab::VisibilityLevel::PUBLIC | false | { weight: 3 } | false
+ # spammable attributes changing on non-public
+ false | Gitlab::VisibilityLevel::INTERNAL | false | { description: 'new' } | false
+ false | Gitlab::VisibilityLevel::PRIVATE | false | { description: 'new' } | false
+
+ ### support-bot cases
+ # confidential to non-confidential
+ true | Gitlab::VisibilityLevel::PUBLIC | true | { confidential: false } | true
+ # non-confidential to confidential
+ true | Gitlab::VisibilityLevel::PUBLIC | false | { confidential: true } | false
+ # spammable attributes changing on confidential
+ true | Gitlab::VisibilityLevel::PUBLIC | true | { description: 'new' } | true
+ # spammable attributes changing while changing to confidential
+ true | Gitlab::VisibilityLevel::PUBLIC | false | { title: 'new', confidential: true } | true
+ # spammable attributes changing on non-public
+ true | Gitlab::VisibilityLevel::INTERNAL | false | { description: 'new' } | true
+ true | Gitlab::VisibilityLevel::PRIVATE | false | { title: 'new' } | true
+ # spammable attribute not changing
+ true | Gitlab::VisibilityLevel::PUBLIC | false | { description: 'original description' } | false
+ # non-spammable attribute changing
+ true | Gitlab::VisibilityLevel::PRIVATE | true | { weight: 3 } | false
end
with_them do
- it 'checks for spam on issues that can be seen anonymously' do
+ it 'checks for spam when necessary' do
+ author = support_bot? ? support_bot : user
project = reusable_project
project.update!(visibility_level: visibility_level)
- issue = create(:issue, project: project, confidential: confidential, description: 'original description')
+ issue = create(:issue, project: project, confidential: confidential, description: 'original description', author: author)
issue.assign_attributes(new_attributes)
diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb
index 0bf202ce2b1..ee4822c653d 100644
--- a/spec/models/label_note_spec.rb
+++ b/spec/models/label_note_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe LabelNote do
let_it_be(:user) { create(:user) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
+
let(:resource_parent) { project }
context 'when resource is issue' do
diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb
index d3f79c7c7cf..5afad6c184f 100644
--- a/spec/models/lfs_file_lock_spec.rb
+++ b/spec/models/lfs_file_lock_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe LfsFileLock do
let_it_be(:lfs_file_lock, reload: true) { create(:lfs_file_lock) }
+
subject { lfs_file_lock }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 372fc40afcc..5824c2085ce 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Member do
context "when an invite email is provided" do
let_it_be(:project) { create(:project) }
+
let(:member) { build(:project_member, source: project, invite_email: "user@example.com", user: nil) }
it "doesn't require a user" do
@@ -98,6 +99,7 @@ RSpec.describe Member do
context 'project bots' do
let_it_be(:project_bot) { create(:user, :project_bot) }
+
let(:new_member) { build(:project_member, user_id: project_bot.id) }
context 'not a member of any group or project' do
@@ -476,6 +478,20 @@ RSpec.describe Member do
it { is_expected.to include @blocked_maintainer }
it { is_expected.to include @blocked_developer }
it { is_expected.to include @member_with_minimal_access }
+
+ context 'with where conditions' do
+ let_it_be(:example_member) { create(:group_member, invite_email: 'user@example.com') }
+
+ subject do
+ described_class
+ .default_scoped
+ .where(invite_email: 'user@example.com')
+ .distinct_on_user_with_max_access_level
+ .to_a
+ end
+
+ it { is_expected.to eq [example_member] }
+ end
end
end
@@ -494,282 +510,6 @@ RSpec.describe Member do
end
end
- describe '.add_user' do
- %w[project group].each do |source_type|
- context "when source is a #{source_type}" do
- let_it_be(:source, reload: true) { create(source_type, :public) }
- let_it_be(:user) { create(:user) }
- let_it_be(:admin) { create(:admin) }
-
- it 'returns a <Source>Member object' do
- member = described_class.add_user(source, user, :maintainer)
-
- expect(member).to be_a "#{source_type.classify}Member".constantize
- expect(member).to be_persisted
- end
-
- context 'when admin mode is enabled', :enable_admin_mode do
- it 'sets members.created_by to the given admin current_user' do
- member = described_class.add_user(source, user, :maintainer, current_user: admin)
-
- expect(member.created_by).to eq(admin)
- end
- end
-
- context 'when admin mode is disabled' do
- it 'rejects setting members.created_by to the given admin current_user' do
- member = described_class.add_user(source, user, :maintainer, current_user: admin)
-
- expect(member.created_by).to be_nil
- end
- end
-
- it 'sets members.expires_at to the given expires_at' do
- member = described_class.add_user(source, user, :maintainer, expires_at: Date.new(2016, 9, 22))
-
- expect(member.expires_at).to eq(Date.new(2016, 9, 22))
- end
-
- described_class.access_levels.each do |sym_key, int_access_level|
- it "accepts the :#{sym_key} symbol as access level" do
- expect(source.users).not_to include(user)
-
- member = described_class.add_user(source, user.id, sym_key)
-
- expect(member.access_level).to eq(int_access_level)
- expect(source.users.reload).to include(user)
- end
-
- it "accepts the #{int_access_level} integer as access level" do
- expect(source.users).not_to include(user)
-
- member = described_class.add_user(source, user.id, int_access_level)
-
- expect(member.access_level).to eq(int_access_level)
- expect(source.users.reload).to include(user)
- end
- end
-
- context 'with no current_user' do
- context 'when called with a known user id' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, user.id, :maintainer)
-
- expect(source.users.reload).to include(user)
- end
- end
-
- context 'when called with an unknown user id' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, non_existing_record_id, :maintainer)
-
- expect(source.users.reload).not_to include(user)
- end
- end
-
- context 'when called with a user object' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, user, :maintainer)
-
- expect(source.users.reload).to include(user)
- end
- end
-
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
-
- it 'adds the requester as a member' do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
-
- expect { described_class.add_user(source, user, :maintainer) }
- .to raise_error(Gitlab::Access::AccessDeniedError)
-
- expect(source.users.reload).not_to include(user)
- expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
- end
- end
-
- context 'when called with a known user email' do
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, user.email, :maintainer)
-
- expect(source.users.reload).to include(user)
- end
- end
-
- context 'when called with a known user secondary email' do
- let(:secondary_email) { create(:email, email: 'secondary@example.com', user: user) }
-
- it 'adds the user as a member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, secondary_email.email, :maintainer)
-
- expect(source.users.reload).to include(user)
- end
- end
-
- context 'when called with an unknown user email' do
- it 'creates an invited member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, 'user@example.com', :maintainer)
-
- expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
- end
- end
-
- context 'when called with an unknown user email starting with a number' do
- it 'creates an invited member', :aggregate_failures do
- email_starting_with_number = "#{user.id}_email@example.com"
-
- described_class.add_user(source, email_starting_with_number, :maintainer)
-
- expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
- expect(source.users.reload).not_to include(user)
- end
- end
- end
-
- context 'when current_user can update member', :enable_admin_mode do
- it 'creates the member' do
- expect(source.users).not_to include(user)
-
- described_class.add_user(source, user, :maintainer, current_user: admin)
-
- expect(source.users.reload).to include(user)
- end
-
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
-
- it 'adds the requester as a member' do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
-
- described_class.add_user(source, user, :maintainer, current_user: admin)
-
- expect(source.users.reload).to include(user)
- expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
- end
- end
- end
-
- context 'when current_user cannot update member' do
- it 'does not create the member' do
- expect(source.users).not_to include(user)
-
- member = described_class.add_user(source, user, :maintainer, current_user: user)
-
- expect(source.users.reload).not_to include(user)
- expect(member).not_to be_persisted
- end
-
- context 'when called with a requester user object' do
- before do
- source.request_access(user)
- end
-
- it 'does not destroy the requester' do
- expect(source.users).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
-
- described_class.add_user(source, user, :maintainer, current_user: user)
-
- expect(source.users.reload).not_to include(user)
- expect(source.requesters.exists?(user_id: user)).to be_truthy
- end
- end
- end
-
- context 'when member already exists' do
- before do
- source.add_user(user, :developer)
- end
-
- context 'with no current_user' do
- it 'updates the member' do
- expect(source.users).to include(user)
-
- described_class.add_user(source, user, :maintainer)
-
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
- end
- end
-
- context 'when current_user can update member', :enable_admin_mode do
- it 'updates the member' do
- expect(source.users).to include(user)
-
- described_class.add_user(source, user, :maintainer, current_user: admin)
-
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
- end
- end
-
- context 'when current_user cannot update member' do
- it 'does not update the member' do
- expect(source.users).to include(user)
-
- described_class.add_user(source, user, :maintainer, current_user: user)
-
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
- end
- end
- end
- end
- end
- end
-
- describe '.add_users' do
- %w[project group].each do |source_type|
- context "when source is a #{source_type}" do
- let_it_be(:source) { create(source_type, :public) }
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user1) { create(:user) }
- let_it_be(:user2) { create(:user) }
-
- it 'returns a <Source>Member objects' do
- members = described_class.add_users(source, [user1, user2], :maintainer)
-
- expect(members).to be_a Array
- expect(members.size).to eq(2)
- expect(members.first).to be_a "#{source_type.classify}Member".constantize
- expect(members.first).to be_persisted
- end
-
- it 'returns an empty array' do
- members = described_class.add_users(source, [], :maintainer)
-
- expect(members).to be_a Array
- expect(members).to be_empty
- end
-
- it 'supports differents formats' do
- list = ['joe@local.test', admin, user1.id, user2.id.to_s]
-
- members = described_class.add_users(source, list, :maintainer)
-
- expect(members.size).to eq(4)
- expect(members.first).to be_invite
- end
- end
- end
- end
-
describe '#accept_request' do
let(:member) { create(:project_member, requested_at: Time.current.utc) }
@@ -966,7 +706,8 @@ RSpec.describe Member do
end
context 'when after_commit :update_highest_role' do
- let!(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+
let(:user_id) { user.id }
where(:member_type, :source_type) do
@@ -1001,7 +742,7 @@ RSpec.describe Member do
end
describe 'destroy member' do
- subject { member.destroy! }
+ subject { member.reload.destroy! }
include_examples 'update highest role with exclusive lease'
end
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 8c942228059..472f4280d26 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -47,27 +47,6 @@ RSpec.describe GroupMember do
end
end
- describe '.access_levels' do
- it 'returns Gitlab::Access.options_with_owner' do
- expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
- end
- end
-
- describe '.add_users' do
- it 'adds the given users to the given group' do
- group = create(:group)
- users = create_list(:user, 2)
-
- described_class.add_users(
- group,
- [users.first.id, users.second],
- described_class::MAINTAINER
- )
-
- expect(group.users).to include(users.first, users.second)
- end
- end
-
it_behaves_like 'members notifications', :group
describe '#namespace_id' do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index b84b408cb4b..4c59bda856f 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -23,19 +23,6 @@ RSpec.describe ProjectMember do
end
end
- describe '.add_user' do
- it 'adds the user as a member' do
- user = create(:user)
- project = create(:project)
-
- expect(project.users).not_to include(user)
-
- described_class.add_user(project, user, :maintainer, current_user: project.owner)
-
- expect(project.users.reload).to include(user)
- end
- end
-
describe '#real_source_type' do
subject { create(:project_member).real_source_type }
diff --git a/spec/models/merge_request/cleanup_schedule_spec.rb b/spec/models/merge_request/cleanup_schedule_spec.rb
index 925d287088b..85208f901fd 100644
--- a/spec/models/merge_request/cleanup_schedule_spec.rb
+++ b/spec/models/merge_request/cleanup_schedule_spec.rb
@@ -11,22 +11,125 @@ RSpec.describe MergeRequest::CleanupSchedule do
it { is_expected.to validate_presence_of(:scheduled_at) }
end
- describe '.scheduled_merge_request_ids' do
- let_it_be(:mr_cleanup_schedule_1) { create(:merge_request_cleanup_schedule, scheduled_at: 2.days.ago) }
- let_it_be(:mr_cleanup_schedule_2) { create(:merge_request_cleanup_schedule, scheduled_at: 1.day.ago) }
- let_it_be(:mr_cleanup_schedule_3) { create(:merge_request_cleanup_schedule, scheduled_at: 1.day.ago, completed_at: Time.current) }
- let_it_be(:mr_cleanup_schedule_4) { create(:merge_request_cleanup_schedule, scheduled_at: 4.days.ago) }
- let_it_be(:mr_cleanup_schedule_5) { create(:merge_request_cleanup_schedule, scheduled_at: 3.days.ago) }
- let_it_be(:mr_cleanup_schedule_6) { create(:merge_request_cleanup_schedule, scheduled_at: 1.day.from_now) }
- let_it_be(:mr_cleanup_schedule_7) { create(:merge_request_cleanup_schedule, scheduled_at: 5.days.ago) }
-
- it 'only includes incomplete schedule within the specified limit' do
- expect(described_class.scheduled_merge_request_ids(4)).to eq([
- mr_cleanup_schedule_2.merge_request_id,
- mr_cleanup_schedule_1.merge_request_id,
- mr_cleanup_schedule_5.merge_request_id,
- mr_cleanup_schedule_4.merge_request_id
+ describe 'state machine transitions' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule) }
+
+ it 'sets status to unstarted by default' do
+ expect(cleanup_schedule).to be_unstarted
+ end
+
+ describe '#run' do
+ it 'sets the status to running' do
+ cleanup_schedule.run
+
+ expect(cleanup_schedule.reload).to be_running
+ end
+
+ context 'when previous status is not unstarted' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :running) }
+
+ it 'does not change status' do
+ expect { cleanup_schedule.run }.not_to change(cleanup_schedule, :status)
+ end
+ end
+ end
+
+ describe '#retry' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :running) }
+
+ it 'sets the status to unstarted' do
+ cleanup_schedule.retry
+
+ expect(cleanup_schedule.reload).to be_unstarted
+ end
+
+ it 'increments failed_count' do
+ expect { cleanup_schedule.retry }.to change(cleanup_schedule, :failed_count).by(1)
+ end
+
+ context 'when previous status is not running' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule) }
+
+ it 'does not change status' do
+ expect { cleanup_schedule.retry }.not_to change(cleanup_schedule, :status)
+ end
+ end
+ end
+
+ describe '#complete' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :running) }
+
+ it 'sets the status to completed' do
+ cleanup_schedule.complete
+
+ expect(cleanup_schedule.reload).to be_completed
+ end
+
+ it 'sets the completed_at' do
+ expect { cleanup_schedule.complete }.to change(cleanup_schedule, :completed_at)
+ end
+
+ context 'when previous status is not running' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :completed) }
+
+ it 'does not change status' do
+ expect { cleanup_schedule.complete }.not_to change(cleanup_schedule, :status)
+ end
+ end
+ end
+
+ describe '#mark_as_failed' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :running) }
+
+ it 'sets the status to failed' do
+ cleanup_schedule.mark_as_failed
+
+ expect(cleanup_schedule.reload).to be_failed
+ end
+
+ it 'increments failed_count' do
+ expect { cleanup_schedule.mark_as_failed }.to change(cleanup_schedule, :failed_count).by(1)
+ end
+
+ context 'when previous status is not running' do
+ let(:cleanup_schedule) { create(:merge_request_cleanup_schedule, :failed) }
+
+ it 'does not change status' do
+ expect { cleanup_schedule.mark_as_failed }.not_to change(cleanup_schedule, :status)
+ end
+ end
+ end
+ end
+
+ describe '.scheduled_and_unstarted' do
+ let!(:cleanup_schedule_1) { create(:merge_request_cleanup_schedule, scheduled_at: 2.days.ago) }
+ let!(:cleanup_schedule_2) { create(:merge_request_cleanup_schedule, scheduled_at: 1.day.ago) }
+ let!(:cleanup_schedule_3) { create(:merge_request_cleanup_schedule, :completed, scheduled_at: 1.day.ago) }
+ let!(:cleanup_schedule_4) { create(:merge_request_cleanup_schedule, scheduled_at: 4.days.ago) }
+ let!(:cleanup_schedule_5) { create(:merge_request_cleanup_schedule, scheduled_at: 3.days.ago) }
+ let!(:cleanup_schedule_6) { create(:merge_request_cleanup_schedule, scheduled_at: 1.day.from_now) }
+ let!(:cleanup_schedule_7) { create(:merge_request_cleanup_schedule, :failed, scheduled_at: 5.days.ago) }
+
+ it 'returns records that are scheduled before or on current time and unstarted (ordered by scheduled first)' do
+ expect(described_class.scheduled_and_unstarted).to eq([
+ cleanup_schedule_2,
+ cleanup_schedule_1,
+ cleanup_schedule_5,
+ cleanup_schedule_4
])
end
end
+
+ describe '.start_next' do
+ let!(:cleanup_schedule_1) { create(:merge_request_cleanup_schedule, :completed, scheduled_at: 1.day.ago) }
+ let!(:cleanup_schedule_2) { create(:merge_request_cleanup_schedule, scheduled_at: 2.days.ago) }
+ let!(:cleanup_schedule_3) { create(:merge_request_cleanup_schedule, :running, scheduled_at: 1.day.ago) }
+ let!(:cleanup_schedule_4) { create(:merge_request_cleanup_schedule, scheduled_at: 3.days.ago) }
+ let!(:cleanup_schedule_5) { create(:merge_request_cleanup_schedule, :failed, scheduled_at: 3.days.ago) }
+
+ it 'finds the next scheduled and unstarted then marked it as running' do
+ expect(described_class.start_next).to eq(cleanup_schedule_2)
+ expect(cleanup_schedule_2.reload).to be_running
+ end
+ end
end
diff --git a/spec/models/merge_request/diff_commit_user_spec.rb b/spec/models/merge_request/diff_commit_user_spec.rb
new file mode 100644
index 00000000000..08e073568f9
--- /dev/null
+++ b/spec/models/merge_request/diff_commit_user_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequest::DiffCommitUser do
+ describe 'validations' do
+ it 'requires that names are less than 512 characters long' do
+ expect(described_class.new(name: 'a' * 1000)).not_to be_valid
+ end
+
+ it 'requires that Emails are less than 512 characters long' do
+ expect(described_class.new(email: 'a' * 1000)).not_to be_valid
+ end
+
+ it 'requires either a name or Email' do
+ expect(described_class.new).not_to be_valid
+ end
+
+ it 'allows setting of just a name' do
+ expect(described_class.new(name: 'Alice')).to be_valid
+ end
+
+ it 'allows setting of just an Email' do
+ expect(described_class.new(email: 'alice@example.com')).to be_valid
+ end
+
+ it 'allows setting of both a name and Email' do
+ expect(described_class.new(name: 'Alice', email: 'alice@example.com'))
+ .to be_valid
+ end
+ end
+
+ describe '.prepare' do
+ it 'trims a value to at most 512 characters' do
+ expect(described_class.prepare('€' * 1_000)).to eq('€' * 512)
+ end
+
+ it 'returns nil if the value is an empty string' do
+ expect(described_class.prepare('')).to be_nil
+ end
+ end
+
+ describe '.find_or_create' do
+ it 'creates a new row if none exist' do
+ alice = described_class.find_or_create('Alice', 'alice@example.com')
+
+ expect(alice.name).to eq('Alice')
+ expect(alice.email).to eq('alice@example.com')
+ end
+
+ it 'returns an existing row if one exists' do
+ user1 = create(:merge_request_diff_commit_user)
+ user2 = described_class.find_or_create(user1.name, user1.email)
+
+ expect(user1).to eq(user2)
+ end
+
+ it 'handles concurrent inserts' do
+ user = create(:merge_request_diff_commit_user)
+
+ expect(described_class)
+ .to receive(:find_or_create_by!)
+ .ordered
+ .with(name: user.name, email: user.email)
+ .and_raise(ActiveRecord::RecordNotUnique)
+
+ expect(described_class)
+ .to receive(:find_or_create_by!)
+ .ordered
+ .with(name: user.name, email: user.email)
+ .and_return(user)
+
+ expect(described_class.find_or_create(user.name, user.email)).to eq(user)
+ end
+ end
+
+ describe '.bulk_find_or_create' do
+ it 'bulk creates missing rows and reuses existing rows' do
+ bob = create(
+ :merge_request_diff_commit_user,
+ name: 'Bob',
+ email: 'bob@example.com'
+ )
+
+ users = described_class.bulk_find_or_create(
+ [%w[Alice alice@example.com], %w[Bob bob@example.com]]
+ )
+ alice = described_class.find_by(name: 'Alice')
+
+ expect(users[%w[Alice alice@example.com]]).to eq(alice)
+ expect(users[%w[Bob bob@example.com]]).to eq(bob)
+ end
+
+ it 'does not insert any data when all users exist' do
+ bob = create(
+ :merge_request_diff_commit_user,
+ name: 'Bob',
+ email: 'bob@example.com'
+ )
+
+ users = described_class.bulk_find_or_create([%w[Bob bob@example.com]])
+
+ expect(described_class).not_to receive(:insert_all)
+ expect(users[%w[Bob bob@example.com]]).to eq(bob)
+ end
+
+ it 'handles concurrently inserted rows' do
+ bob = create(
+ :merge_request_diff_commit_user,
+ name: 'Bob',
+ email: 'bob@example.com'
+ )
+
+ input = [%w[Bob bob@example.com]]
+
+ expect(described_class)
+ .to receive(:bulk_find)
+ .twice
+ .with(input)
+ .and_return([], [bob])
+
+ users = described_class.bulk_find_or_create(input)
+
+ expect(users[%w[Bob bob@example.com]]).to eq(bob)
+ end
+ end
+end
diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb
index a24628b0f9d..6290468d4a7 100644
--- a/spec/models/merge_request_diff_commit_spec.rb
+++ b/spec/models/merge_request_diff_commit_spec.rb
@@ -16,6 +16,11 @@ RSpec.describe MergeRequestDiffCommit do
let(:invalid_items_for_bulk_insertion) { [] } # class does not have any validations defined
end
+ describe 'associations' do
+ it { is_expected.to belong_to(:commit_author) }
+ it { is_expected.to belong_to(:committer) }
+ end
+
describe '#to_hash' do
subject { merge_request.commits.first }
@@ -46,6 +51,8 @@ RSpec.describe MergeRequestDiffCommit do
"committed_date": "2014-02-27T10:01:38.000+01:00".to_time,
"committer_name": "Dmitriy Zaporozhets",
"committer_email": "dmitriy.zaporozhets@gmail.com",
+ "commit_author_id": an_instance_of(Integer),
+ "committer_id": an_instance_of(Integer),
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 0,
"sha": Gitlab::Database::ShaAttribute.serialize("5937ac0a7beb003549fc5fd26fc247adbce4a52e"),
@@ -59,6 +66,8 @@ RSpec.describe MergeRequestDiffCommit do
"committed_date": "2014-02-27T09:57:31.000+01:00".to_time,
"committer_name": "Dmitriy Zaporozhets",
"committer_email": "dmitriy.zaporozhets@gmail.com",
+ "commit_author_id": an_instance_of(Integer),
+ "committer_id": an_instance_of(Integer),
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 1,
"sha": Gitlab::Database::ShaAttribute.serialize("570e7b2abdd848b95f2f578043fc23bd6f6fd24d"),
@@ -76,6 +85,21 @@ RSpec.describe MergeRequestDiffCommit do
subject
end
+ it 'creates diff commit users' do
+ diff = create(:merge_request_diff, merge_request: merge_request)
+
+ described_class.create_bulk(diff.id, [commits.first])
+
+ commit_row = MergeRequestDiffCommit
+ .find_by(merge_request_diff_id: diff.id, relative_order: 0)
+
+ commit_user_row =
+ MergeRequest::DiffCommitUser.find_by(name: 'Dmitriy Zaporozhets')
+
+ expect(commit_row.commit_author).to eq(commit_user_row)
+ expect(commit_row.committer).to eq(commit_user_row)
+ end
+
context 'with dates larger than the DB limit' do
let(:commits) do
# This commit's date is "Sun Aug 17 07:12:55 292278994 +0000"
@@ -92,6 +116,8 @@ RSpec.describe MergeRequestDiffCommit do
"committed_date": timestamp,
"committer_name": "Alejandro Rodríguez",
"committer_email": "alejorro70@gmail.com",
+ "commit_author_id": an_instance_of(Integer),
+ "committer_id": an_instance_of(Integer),
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 0,
"sha": Gitlab::Database::ShaAttribute.serialize("ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69"),
@@ -107,4 +133,28 @@ RSpec.describe MergeRequestDiffCommit do
end
end
end
+
+ describe '.prepare_commits_for_bulk_insert' do
+ it 'returns the commit hashes and unique user tuples' do
+ commit = double(:commit, to_hash: {
+ parent_ids: %w[foo bar],
+ author_name: 'a' * 1000,
+ author_email: 'a' * 1000,
+ committer_name: 'Alice',
+ committer_email: 'alice@example.com'
+ })
+
+ hashes, tuples = described_class.prepare_commits_for_bulk_insert([commit])
+
+ expect(hashes).to eq([{
+ author_name: 'a' * 512,
+ author_email: 'a' * 512,
+ committer_name: 'Alice',
+ committer_email: 'alice@example.com'
+ }])
+
+ expect(tuples)
+ .to include(['a' * 512, 'a' * 512], %w[Alice alice@example.com])
+ end
+ end
end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 3741e01e99a..e0e25031589 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -9,10 +9,6 @@ RSpec.describe MergeRequestDiff do
let(:diff_with_commits) { create(:merge_request).merge_request_diff }
- before do
- stub_feature_flags(diffs_gradual_load: false)
- end
-
describe 'validations' do
subject { diff_with_commits }
@@ -115,6 +111,7 @@ RSpec.describe MergeRequestDiff do
let(:closed_recently) { recently_closed_mr.merge_request_diff }
let_it_be(:recently_merged_mr) { create(:merge_request, :merged) }
+
let(:merged_recently) { recently_merged_mr.merge_request_diff }
before do
@@ -436,9 +433,7 @@ RSpec.describe MergeRequestDiff do
it 'returns empty pagination data' do
diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
- expect(diffs.pagination_data).to eq(current_page: nil,
- next_page: nil,
- total_pages: nil)
+ expect(diffs.pagination_data).to eq(total_pages: nil)
end
end
@@ -460,19 +455,17 @@ RSpec.describe MergeRequestDiff do
context 'when persisted files available' do
it 'returns paginated diffs' do
- diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
+ diffs = diff_with_commits.diffs_in_batch(0, 10, diff_options: diff_options)
expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
expect(diffs.diff_files.size).to eq(10)
- expect(diffs.pagination_data).to eq(current_page: 1,
- next_page: 2,
- total_pages: 2)
+ expect(diffs.pagination_data).to eq(total_pages: 20)
end
it 'sorts diff files directory first' do
diff_with_commits.update!(sorted: false) # Mark as unsorted so it'll re-order
- expect(diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options).diff_file_paths).to eq([
+ expect(diff_with_commits.diffs_in_batch(0, 10, diff_options: diff_options).diff_file_paths).to eq([
'bar/branch-test.txt',
'custom-highlighting/test.gitlab-custom',
'encoding/iso8859.txt',
@@ -491,43 +484,21 @@ RSpec.describe MergeRequestDiff do
{ ignore_whitespace_change: true }
end
- it 'returns a Gitlab::Diff::FileCollection::Compare with paginated diffs' do
+ it 'returns pagination data from MergeRequestDiffBatch' do
diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
+ file_count = diff_with_commits.merge_request_diff_files.count
expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
expect(diffs.diff_files.size).to eq 10
- expect(diffs.pagination_data).to eq(current_page: 1, next_page: 2, total_pages: 2)
+ expect(diffs.pagination_data).to eq(total_pages: file_count)
end
it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do
- diffs = diff_with_commits.diffs_in_batch(3, 10, diff_options: diff_options)
+ diffs = diff_with_commits.diffs_in_batch(30, 10, diff_options: diff_options)
expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
expect(diffs.diff_files.size).to eq 0
- expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil)
- end
-
- context 'with gradual load enabled' do
- before do
- stub_feature_flags(diffs_gradual_load: true)
- end
-
- it 'returns pagination data from MergeRequestDiffBatch' do
- diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
- file_count = diff_with_commits.merge_request_diff_files.count
-
- expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
- expect(diffs.diff_files.size).to eq 10
- expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: file_count)
- end
-
- it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do
- diffs = diff_with_commits.diffs_in_batch(30, 10, diff_options: diff_options)
-
- expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
- expect(diffs.diff_files.size).to eq 0
- expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil)
- end
+ expect(diffs.pagination_data).to eq(total_pages: nil)
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 73b1cb13f19..edd543854cb 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -441,6 +441,22 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
+ describe '.join_metrics' do
+ let_it_be(:join_condition) { '"merge_request_metrics"."target_project_id" = 1' }
+
+ context 'when a no target_project_id is available' do
+ it 'moves target_project_id condition to the merge request metrics' do
+ expect(described_class.join_metrics(1).to_sql).to include(join_condition)
+ end
+ end
+
+ context 'when a target_project_id is present in the where conditions' do
+ it 'moves target_project_id condition to the merge request metrics' do
+ expect(described_class.where(target_project_id: 1).join_metrics.to_sql).to include(join_condition)
+ end
+ end
+ end
+
describe '.by_related_commit_sha' do
subject { described_class.by_related_commit_sha(sha) }
@@ -779,7 +795,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
context 'when both internal and external issue trackers are enabled' do
before do
- create(:jira_service, project: subject.project)
+ create(:jira_integration, project: subject.project)
subject.project.reload
end
@@ -1310,7 +1326,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
subject.project.add_developer(subject.author)
commit = double(:commit, safe_message: 'Fixes TEST-3')
- create(:jira_service, project: subject.project)
+ create(:jira_integration, project: subject.project)
subject.project.reload
allow(subject).to receive(:commits).and_return([commit])
@@ -1898,7 +1914,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
context 'has ci' do
it 'returns true if MR has head_pipeline_id and commits' do
- allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
+ allow(merge_request).to receive_message_chain(:source_project, :ci_integration) { nil }
allow(merge_request).to receive(:head_pipeline_id) { double }
allow(merge_request).to receive(:has_no_commits?) { false }
@@ -1906,7 +1922,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
it 'returns true if MR has any pipeline and commits' do
- allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
+ allow(merge_request).to receive_message_chain(:source_project, :ci_integration) { nil }
allow(merge_request).to receive(:head_pipeline_id) { nil }
allow(merge_request).to receive(:has_no_commits?) { false }
allow(merge_request).to receive(:all_pipelines) { [double] }
@@ -1914,8 +1930,8 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect(merge_request.has_ci?).to be(true)
end
- it 'returns true if MR has CI service and commits' do
- allow(merge_request).to receive_message_chain(:source_project, :ci_service) { double }
+ it 'returns true if MR has CI integration and commits' do
+ allow(merge_request).to receive_message_chain(:source_project, :ci_integration) { double }
allow(merge_request).to receive(:head_pipeline_id) { nil }
allow(merge_request).to receive(:has_no_commits?) { false }
allow(merge_request).to receive(:all_pipelines) { [] }
@@ -1925,8 +1941,8 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
context 'has no ci' do
- it 'returns false if MR has no CI service nor pipeline, and no commits' do
- allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
+ it 'returns false if MR has no CI integration nor pipeline, and no commits' do
+ allow(merge_request).to receive_message_chain(:source_project, :ci_integration) { nil }
allow(merge_request).to receive(:head_pipeline_id) { nil }
allow(merge_request).to receive(:all_pipelines) { [] }
allow(merge_request).to receive(:has_no_commits?) { true }
@@ -2067,14 +2083,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
let(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports) }
it { is_expected.to be_truthy }
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(codequality_mr_diff: false)
- end
-
- it { is_expected.to be_falsey }
- end
end
context 'when head pipeline does not have codeqquality mr diff report' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 7cf7c360dff..bc592acc80f 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -3,10 +3,9 @@
require 'spec_helper'
RSpec.describe Milestone do
- let(:user) { create(:user) }
- let(:issue) { create(:issue, project: project) }
- let(:milestone) { create(:milestone, project: project) }
- let(:project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
it_behaves_like 'a timebox', :milestone do
describe "#uniqueness_of_title" do
@@ -92,6 +91,8 @@ RSpec.describe Milestone do
end
describe '.predefined_id?' do
+ let_it_be(:milestone) { create(:milestone, project: project) }
+
it 'returns true for a predefined Milestone ID' do
expect(Milestone.predefined_id?(described_class::Upcoming.id)).to be true
end
@@ -129,6 +130,8 @@ RSpec.describe Milestone do
end
describe "#percent_complete" do
+ let(:milestone) { create(:milestone, project: project) }
+
it "does not count open issues" do
milestone.issues << issue
expect(milestone.percent_complete).to eq(0)
@@ -145,24 +148,22 @@ RSpec.describe Milestone do
end
end
- describe '#expired?' do
+ describe '#expired? and #expired' do
context "expired" do
- before do
- allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
- end
+ let(:milestone) { build(:milestone, project: project, due_date: Date.today.prev_year) }
- it 'returns true when due_date is in the past' do
+ it 'returns true when due_date is in the past', :aggregate_failures do
expect(milestone.expired?).to be_truthy
+ expect(milestone.expired).to eq true
end
end
context "not expired" do
- before do
- allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
- end
+ let(:milestone) { build(:milestone, project: project, due_date: Date.today.next_year) }
- it 'returns false when due_date is in the future' do
+ it 'returns false when due_date is in the future', :aggregate_failures do
expect(milestone.expired?).to be_falsey
+ expect(milestone.expired).to eq false
end
end
end
@@ -180,10 +181,8 @@ RSpec.describe Milestone do
end
describe '#can_be_closed?' do
- it { expect(milestone.can_be_closed?).to be_truthy }
- end
+ let_it_be(:milestone) { build(:milestone, project: project) }
- describe '#can_be_closed?' do
before do
milestone = create :milestone, project: project
create :closed_issue, milestone: milestone, project: project
@@ -335,10 +334,10 @@ RSpec.describe Milestone do
it_behaves_like '#for_projects_and_groups'
describe '.upcoming_ids' do
- let(:group_1) { create(:group) }
- let(:group_2) { create(:group) }
- let(:group_3) { create(:group) }
- let(:groups) { [group_1, group_2, group_3] }
+ let_it_be(:group_1) { create(:group) }
+ let_it_be(:group_2) { create(:group) }
+ let_it_be(:group_3) { create(:group) }
+ let_it_be(:groups) { [group_1, group_2, group_3] }
let!(:past_milestone_group_1) { create(:milestone, group: group_1, due_date: Time.current - 1.day) }
let!(:current_milestone_group_1) { create(:milestone, group: group_1, due_date: Time.current + 1.day) }
@@ -350,10 +349,10 @@ RSpec.describe Milestone do
let!(:past_milestone_group_3) { create(:milestone, group: group_3, due_date: Time.current - 1.day) }
- let(:project_1) { create(:project) }
- let(:project_2) { create(:project) }
- let(:project_3) { create(:project) }
- let(:projects) { [project_1, project_2, project_3] }
+ let_it_be(:project_1) { create(:project) }
+ let_it_be(:project_2) { create(:project) }
+ let_it_be(:project_3) { create(:project) }
+ let_it_be(:projects) { [project_1, project_2, project_3] }
let!(:past_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.current - 1.day) }
let!(:current_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.current + 1.day) }
@@ -451,6 +450,32 @@ RSpec.describe Milestone do
end
end
+ describe '.sort_with_expired_last' do
+ let_it_be(:milestone) { create(:milestone, title: 'Due today', due_date: Date.current) }
+ let_it_be(:milestone_1) { create(:milestone, title: 'Current 1', due_date: Date.current + 1.day) }
+ let_it_be(:milestone_2) { create(:milestone, title: 'Current 2', due_date: Date.current + 2.days) }
+ let_it_be(:milestone_3) { create(:milestone, title: 'Without due date') }
+ let_it_be(:milestone_4) { create(:milestone, title: 'Expired 1', due_date: Date.current - 2.days) }
+ let_it_be(:milestone_5) { create(:milestone, title: 'Expired 2', due_date: Date.current - 1.day) }
+ let_it_be(:milestone_6) { create(:milestone, title: 'Without due date2') }
+
+ context 'ordering by due_date ascending' do
+ it 'sorts by due date in ascending order (ties broken by id in desc order)', :aggregate_failures do
+ expect(milestone_3.id).to be < (milestone_6.id)
+ expect(described_class.sort_with_expired_last(:expired_last_due_date_asc))
+ .to eq([milestone, milestone_1, milestone_2, milestone_6, milestone_3, milestone_4, milestone_5])
+ end
+ end
+
+ context 'ordering by due_date descending' do
+ it 'sorts by due date in descending order (ties broken by id in desc order)', :aggregate_failures do
+ expect(milestone_3.id).to be < (milestone_6.id)
+ expect(described_class.sort_with_expired_last(:expired_last_due_date_desc))
+ .to eq([milestone_2, milestone_1, milestone, milestone_6, milestone_3, milestone_5, milestone_4])
+ end
+ end
+ end
+
describe '.sort_by_attribute' do
let_it_be(:milestone_1) { create(:milestone, title: 'Foo') }
let_it_be(:milestone_2) { create(:milestone, title: 'Bar') }
diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb
index b725d2366a1..51c191069ec 100644
--- a/spec/models/namespace/root_storage_statistics_spec.rb
+++ b/spec/models/namespace/root_storage_statistics_spec.rb
@@ -99,6 +99,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
context 'with a personal namespace' do
let_it_be(:user) { create(:user) }
+
let(:namespace) { user.namespace }
it_behaves_like 'data refresh'
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 373f3a89e14..ea1ce067e4d 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -156,7 +156,7 @@ RSpec.describe Namespace do
end
end
- describe 'scopes' do
+ describe 'scopes', :aggregate_failures do
let_it_be(:namespace1) { create(:group, name: 'Namespace 1', path: 'namespace-1') }
let_it_be(:namespace2) { create(:group, name: 'Namespace 2', path: 'namespace-2') }
let_it_be(:namespace1sub) { create(:group, name: 'Sub Namespace', path: 'sub-namespace', parent: namespace1) }
@@ -181,6 +181,15 @@ RSpec.describe Namespace do
expect(described_class.filter_by_path(namespace1.path.upcase)).to eq([namespace1])
end
end
+
+ describe '.sorted_by_similarity_and_parent_id_desc' do
+ it 'returns exact matches and top level groups first' do
+ expect(described_class.sorted_by_similarity_and_parent_id_desc(namespace1.path)).to eq([namespace1, namespace2, namespace2sub, namespace1sub, namespace])
+ expect(described_class.sorted_by_similarity_and_parent_id_desc(namespace2.path)).to eq([namespace2, namespace1, namespace2sub, namespace1sub, namespace])
+ expect(described_class.sorted_by_similarity_and_parent_id_desc(namespace2sub.name)).to eq([namespace2sub, namespace1sub, namespace2, namespace1, namespace])
+ expect(described_class.sorted_by_similarity_and_parent_id_desc('Namespace')).to eq([namespace2, namespace1, namespace2sub, namespace1sub, namespace])
+ end
+ end
end
describe 'delegate' do
@@ -965,6 +974,14 @@ RSpec.describe Namespace do
end
end
+ shared_examples 'disabled feature flag when traversal_ids is blank' do
+ before do
+ namespace.traversal_ids = []
+ end
+
+ it { is_expected.to eq false }
+ end
+
describe '#use_traversal_ids?' do
let_it_be(:namespace, reload: true) { create(:namespace) }
@@ -976,6 +993,8 @@ RSpec.describe Namespace do
end
it { is_expected.to eq true }
+
+ it_behaves_like 'disabled feature flag when traversal_ids is blank'
end
context 'when use_traversal_ids feature flag is false' do
@@ -987,6 +1006,62 @@ RSpec.describe Namespace do
end
end
+ describe '#use_traversal_ids_for_root_ancestor?' do
+ let_it_be(:namespace, reload: true) { create(:namespace) }
+
+ subject { namespace.use_traversal_ids_for_root_ancestor? }
+
+ context 'when use_traversal_ids_for_root_ancestor feature flag is true' do
+ before do
+ stub_feature_flags(use_traversal_ids_for_root_ancestor: true)
+ end
+
+ it { is_expected.to eq true }
+
+ it_behaves_like 'disabled feature flag when traversal_ids is blank'
+ end
+
+ context 'when use_traversal_ids_for_root_ancestor feature flag is false' do
+ before do
+ stub_feature_flags(use_traversal_ids_for_root_ancestor: false)
+ end
+
+ it { is_expected.to eq false }
+ end
+ end
+
+ describe '#use_traversal_ids_for_ancestors?' do
+ let_it_be(:namespace, reload: true) { create(:namespace) }
+
+ subject { namespace.use_traversal_ids_for_ancestors? }
+
+ context 'when use_traversal_ids_for_ancestors? feature flag is true' do
+ before do
+ stub_feature_flags(use_traversal_ids_for_ancestors: true)
+ end
+
+ it { is_expected.to eq true }
+
+ it_behaves_like 'disabled feature flag when traversal_ids is blank'
+ end
+
+ context 'when use_traversal_ids_for_ancestors? feature flag is false' do
+ before do
+ stub_feature_flags(use_traversal_ids_for_ancestors: false)
+ end
+
+ it { is_expected.to eq false }
+ end
+
+ context 'when use_traversal_ids? feature flag is false' do
+ before do
+ stub_feature_flags(use_traversal_ids: false)
+ end
+
+ it { is_expected.to eq false }
+ end
+ end
+
describe '#users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
@@ -1058,6 +1133,14 @@ RSpec.describe Namespace do
end
include_examples '#all_projects'
+
+ # Using #self_and_descendant instead of #self_and_descendant_ids can produce
+ # very slow queries.
+ it 'calls self_and_descendant_ids' do
+ namespace = create(:group)
+ expect(namespace).to receive(:self_and_descendant_ids)
+ namespace.all_projects
+ end
end
context 'with use_traversal_ids feature flag disabled' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index d9f566f9383..2afe9a0f29b 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -455,6 +455,7 @@ RSpec.describe Note do
describe "#system_note_viewable_by?(user)" do
let_it_be(:note) { create(:note) }
let_it_be(:user) { create(:user) }
+
let!(:metadata) { create(:system_note_metadata, note: note, action: "branch") }
context "when system_note_metadata is not present" do
@@ -536,6 +537,7 @@ RSpec.describe Note do
context "when there is a reference to a label" do
let_it_be(:private_label) { create(:label, project: private_project) }
+
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
@@ -550,6 +552,7 @@ RSpec.describe Note do
context "when there are two references in note" do
let_it_be(:ext_issue2) { create(:issue, project: ext_proj) }
+
let(:note) do
create :note,
noteable: ext_issue2, project: ext_proj,
@@ -1239,6 +1242,7 @@ RSpec.describe Note do
describe 'expiring ETag cache' do
let_it_be(:issue) { create(:issue) }
+
let(:note) { build(:note, project: issue.project, noteable: issue) }
def expect_expiration(noteable)
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 010b7455f85..3f1684327e7 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -51,6 +51,7 @@ RSpec.describe NotificationSetting do
context 'notification_email' do
let_it_be(:user) { create(:user) }
+
subject { described_class.new(source_id: 1, source_type: 'Project', user_id: user.id) }
it 'allows to change email to verified one' do
diff --git a/spec/models/operations/feature_flag_spec.rb b/spec/models/operations/feature_flag_spec.rb
index 55682e12642..cb9da2aea34 100644
--- a/spec/models/operations/feature_flag_spec.rb
+++ b/spec/models/operations/feature_flag_spec.rb
@@ -251,6 +251,7 @@ RSpec.describe Operations::FeatureFlag do
describe '.for_unleash_client' do
let_it_be(:project) { create(:project) }
+
let!(:feature_flag) do
create(:operations_feature_flag, project: project,
name: 'feature1', active: true, version: 2)
diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb
index 7f2f22c815c..ee0aeb26d50 100644
--- a/spec/models/packages/package_file_spec.rb
+++ b/spec/models/packages/package_file_spec.rb
@@ -5,6 +5,7 @@ RSpec.describe Packages::PackageFile, type: :model do
let_it_be(:project) { create(:project) }
let_it_be(:package_file1) { create(:package_file, :xml, file_name: 'FooBar') }
let_it_be(:package_file2) { create(:package_file, :xml, file_name: 'ThisIsATest') }
+ let_it_be(:package_file3) { create(:package_file, :xml, file_name: 'formatted.zip') }
let_it_be(:debian_package) { create(:debian_package, project: project) }
describe 'relationships' do
@@ -36,6 +37,12 @@ RSpec.describe Packages::PackageFile, type: :model do
it { is_expected.to match_array([package_file1]) }
end
+
+ describe '.with_format' do
+ subject { described_class.with_format('zip') }
+
+ it { is_expected.to contain_exactly(package_file3) }
+ end
end
context 'updating project statistics' do
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index b2c1d51e4af..449e30f9fb7 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -1006,37 +1006,4 @@ RSpec.describe Packages::Package, type: :model do
it_behaves_like 'not enqueuing a sync worker job'
end
end
-
- context 'destroying a composer package' do
- let_it_be(:package_name) { 'composer-package-name' }
- let_it_be(:json) { { 'name' => package_name } }
- let_it_be(:project) { create(:project, :custom_repo, files: { 'composer.json' => json.to_json } ) }
-
- let!(:package) { create(:composer_package, :with_metadatum, project: project, name: package_name, version: '1.0.0', json: json) }
-
- before do
- Gitlab::Composer::Cache.new(project: project, name: package_name).execute
- package.composer_metadatum.reload
- end
-
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(disable_composer_callback: false)
- end
-
- it 'schedule the update job' do
- expect(::Packages::Composer::CacheUpdateWorker).to receive(:perform_async).with(project.id, package_name, package.composer_metadatum.version_cache_sha)
-
- package.destroy!
- end
- end
-
- context 'with feature flag enabled' do
- it 'does nothing' do
- expect(::Packages::Composer::CacheUpdateWorker).not_to receive(:perform_async)
-
- package.destroy!
- end
- end
- end
end
diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb
index cf8e30023eb..72fda2280e5 100644
--- a/spec/models/plan_limits_spec.rb
+++ b/spec/models/plan_limits_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe PlanLimits do
let_it_be(:project) { create(:project) }
let_it_be(:plan_limits) { create(:plan_limits) }
+
let(:project_hooks_count) { 2 }
before do
@@ -184,6 +185,7 @@ RSpec.describe PlanLimits do
ci_max_artifact_size_junit
ci_max_artifact_size_sast
ci_max_artifact_size_dast
+ ci_max_artifact_size_cluster_image_scanning
ci_max_artifact_size_codequality
ci_max_artifact_size_license_management
ci_max_artifact_size_performance
diff --git a/spec/models/plan_spec.rb b/spec/models/plan_spec.rb
index 490c6b1bbf7..73e88a17e24 100644
--- a/spec/models/plan_spec.rb
+++ b/spec/models/plan_spec.rb
@@ -15,6 +15,29 @@ RSpec.describe Plan do
end
end
+ describe '#default' do
+ context 'when default plan exists' do
+ let!(:default_plan) { create(:default_plan) }
+
+ it 'returns default plan' do
+ expect(described_class.default).to eq(default_plan)
+ end
+ end
+
+ context 'when default plan does not exist' do
+ it 'creates default plan' do
+ expect { described_class.default }.to change { Plan.count }.by(1)
+ end
+
+ it 'creates plan with correct attributes' do
+ plan = described_class.default
+
+ expect(plan.name).to eq(Plan::DEFAULT)
+ expect(plan.title).to eq(Plan::DEFAULT.titleize)
+ end
+ end
+ end
+
context 'when updating plan limits' do
let(:plan) { described_class.default }
diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb
index c206ba27ec1..caab182cda8 100644
--- a/spec/models/project_ci_cd_setting_spec.rb
+++ b/spec/models/project_ci_cd_setting_spec.rb
@@ -22,8 +22,8 @@ RSpec.describe ProjectCiCdSetting do
end
describe '#job_token_scope_enabled' do
- it 'is true by default' do
- expect(described_class.new.job_token_scope_enabled).to be_truthy
+ it 'is false by default' do
+ expect(described_class.new.job_token_scope_enabled).to be_falsey
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 144b00e1d2e..efa269cdb5c 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -35,14 +35,14 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:hooks) }
it { is_expected.to have_many(:protected_branches) }
it { is_expected.to have_many(:exported_protected_branches) }
- it { is_expected.to have_one(:slack_service) }
- it { is_expected.to have_one(:microsoft_teams_service) }
- it { is_expected.to have_one(:mattermost_service) }
+ it { is_expected.to have_one(:slack_integration) }
+ it { is_expected.to have_one(:microsoft_teams_integration) }
+ it { is_expected.to have_one(:mattermost_integration) }
it { is_expected.to have_one(:hangouts_chat_integration) }
- it { is_expected.to have_one(:unify_circuit_service) }
- it { is_expected.to have_one(:webex_teams_service) }
- it { is_expected.to have_one(:packagist_service) }
- it { is_expected.to have_one(:pushover_service) }
+ it { is_expected.to have_one(:unify_circuit_integration) }
+ it { is_expected.to have_one(:webex_teams_integration) }
+ it { is_expected.to have_one(:packagist_integration) }
+ it { is_expected.to have_one(:pushover_integration) }
it { is_expected.to have_one(:asana_integration) }
it { is_expected.to have_many(:boards) }
it { is_expected.to have_one(:campfire_integration) }
@@ -50,19 +50,19 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:discord_integration) }
it { is_expected.to have_one(:drone_ci_integration) }
it { is_expected.to have_one(:emails_on_push_integration) }
- it { is_expected.to have_one(:pipelines_email_service) }
+ it { is_expected.to have_one(:pipelines_email_integration) }
it { is_expected.to have_one(:irker_integration) }
- it { is_expected.to have_one(:pivotaltracker_service) }
+ it { is_expected.to have_one(:pivotaltracker_integration) }
it { is_expected.to have_one(:flowdock_integration) }
it { is_expected.to have_one(:assembla_integration) }
- it { is_expected.to have_one(:slack_slash_commands_service) }
- it { is_expected.to have_one(:mattermost_slash_commands_service) }
+ it { is_expected.to have_one(:slack_slash_commands_integration) }
+ it { is_expected.to have_one(:mattermost_slash_commands_integration) }
it { is_expected.to have_one(:buildkite_integration) }
it { is_expected.to have_one(:bamboo_integration) }
- it { is_expected.to have_one(:teamcity_service) }
- it { is_expected.to have_one(:jira_service) }
- it { is_expected.to have_one(:redmine_service) }
- it { is_expected.to have_one(:youtrack_service) }
+ it { is_expected.to have_one(:teamcity_integration) }
+ it { is_expected.to have_one(:jira_integration) }
+ it { is_expected.to have_one(:redmine_integration) }
+ it { is_expected.to have_one(:youtrack_integration) }
it { is_expected.to have_one(:custom_issue_tracker_integration) }
it { is_expected.to have_one(:bugzilla_integration) }
it { is_expected.to have_one(:ewm_integration) }
@@ -80,6 +80,8 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') }
it { is_expected.to have_one(:project_setting) }
it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') }
+ it { is_expected.to have_one(:mock_ci_integration) }
+ it { is_expected.to have_one(:mock_monitoring_integration) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:ci_pipelines) }
it { is_expected.to have_many(:ci_refs) }
@@ -656,12 +658,51 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to delegate_method(:container_registry_enabled?).to(:project_feature) }
it { is_expected.to delegate_method(:container_registry_access_level).to(:project_feature) }
- context 'when read_container_registry_access_level is disabled' do
- before do
- stub_feature_flags(read_container_registry_access_level: false)
+ include_examples 'ci_cd_settings delegation' do
+ # Skip attributes defined in EE code
+ let(:exclude_attributes) do
+ %w(
+ merge_pipelines_enabled
+ merge_trains_enabled
+ auto_rollback_enabled
+ )
+ end
+ end
+
+ describe '#ci_forward_deployment_enabled?' do
+ it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do
+ let(:delegated_method) { :forward_deployment_enabled? }
+ end
+ end
+
+ describe '#ci_job_token_scope_enabled?' do
+ it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do
+ let(:delegated_method) { :job_token_scope_enabled? }
+ end
+ end
+
+ describe '#restrict_user_defined_variables?' do
+ it_behaves_like 'a ci_cd_settings predicate method' do
+ let(:delegated_method) { :restrict_user_defined_variables? }
+ end
+ end
+
+ describe '#keep_latest_artifacts_available?' do
+ it_behaves_like 'a ci_cd_settings predicate method' do
+ let(:delegated_method) { :keep_latest_artifacts_available? }
+ end
+ end
+
+ describe '#keep_latest_artifact?' do
+ it_behaves_like 'a ci_cd_settings predicate method' do
+ let(:delegated_method) { :keep_latest_artifact? }
end
+ end
- it { is_expected.not_to delegate_method(:container_registry_enabled?).to(:project_feature) }
+ describe '#group_runners_enabled?' do
+ it_behaves_like 'a ci_cd_settings predicate method' do
+ let(:delegated_method) { :group_runners_enabled? }
+ end
end
end
@@ -1444,13 +1485,13 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '.with_active_jira_services' do
- it 'returns the correct project' do
- active_jira_service = create(:jira_service)
+ describe '.with_active_jira_integrations' do
+ it 'returns the correct integrations' do
+ active_jira_integration = create(:jira_integration)
active_service = create(:service, active: true)
- expect(described_class.with_active_jira_services).to include(active_jira_service.project)
- expect(described_class.with_active_jira_services).not_to include(active_service.project)
+ expect(described_class.with_active_jira_integrations).to include(active_jira_integration.project)
+ expect(described_class.with_active_jira_integrations).not_to include(active_service.project)
end
end
@@ -1555,13 +1596,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '.with_service' do
+ describe '.with_integration' do
before do
create_list(:prometheus_project, 2)
end
- it 'avoid n + 1' do
- expect { described_class.with_service(:prometheus_service).map(&:prometheus_service) }.not_to exceed_query_limit(1)
+ let(:integration) { :prometheus_integration }
+
+ it 'avoids n + 1' do
+ expect { described_class.with_integration(integration).map(&integration) }
+ .not_to exceed_query_limit(1)
end
end
@@ -2403,20 +2447,6 @@ RSpec.describe Project, factory_default: :keep do
expect(project.container_registry_enabled).to eq(false)
expect(project.container_registry_enabled?).to eq(false)
end
-
- context 'with read_container_registry_access_level disabled' do
- before do
- stub_feature_flags(read_container_registry_access_level: false)
- end
-
- it 'reads project.container_registry_enabled' do
- project.update_column(:container_registry_enabled, true)
- project.project_feature.update_column(:container_registry_access_level, ProjectFeature::DISABLED)
-
- expect(project.container_registry_enabled).to eq(true)
- expect(project.container_registry_enabled?).to eq(true)
- end
- end
end
describe '#has_container_registry_tags?' do
@@ -3083,8 +3113,8 @@ RSpec.describe Project, factory_default: :keep do
context 'LFS disabled in group' do
before do
+ stub_lfs_setting(enabled: true)
project.namespace.update_attribute(:lfs_enabled, false)
- enable_lfs
end
it_behaves_like 'project overrides group'
@@ -3092,14 +3122,18 @@ RSpec.describe Project, factory_default: :keep do
context 'LFS enabled in group' do
before do
+ stub_lfs_setting(enabled: true)
project.namespace.update_attribute(:lfs_enabled, true)
- enable_lfs
end
it_behaves_like 'project overrides group'
end
describe 'LFS disabled globally' do
+ before do
+ stub_lfs_setting(enabled: false)
+ end
+
shared_examples 'it always returns false' do
it do
expect(project.lfs_enabled?).to be_falsey
@@ -3896,10 +3930,6 @@ RSpec.describe Project, factory_default: :keep do
end
end
- def enable_lfs
- allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- end
-
describe '#pages_url' do
let(:group) { create(:group, name: 'Group') }
let(:nested_group) { create(:group, parent: group) }
@@ -5350,27 +5380,27 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#execute_services' do
- let(:service) { create(:slack_service, push_events: true, merge_requests_events: false, active: true) }
+ describe '#execute_integrations' do
+ let(:integration) { create(:integrations_slack, push_events: true, merge_requests_events: false, active: true) }
- it 'executes services with the specified scope' do
+ it 'executes integrations with the specified scope' do
data = 'any data'
expect_next_found_instance_of(Integrations::Slack) do |instance|
expect(instance).to receive(:async_execute).with(data).once
end
- service.project.execute_services(data, :push_hooks)
+ integration.project.execute_integrations(data, :push_hooks)
end
- it 'does not execute services that don\'t match the specified scope' do
+ it 'does not execute integration that don\'t match the specified scope' do
expect(Integrations::Slack).not_to receive(:allocate).and_wrap_original do |method|
method.call.tap do |instance|
expect(instance).not_to receive(:async_execute)
end
end
- service.project.execute_services(anything, :merge_request_hooks)
+ integration.project.execute_integrations(anything, :merge_request_hooks)
end
end
@@ -5401,16 +5431,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#has_active_services?' do
+ describe '#has_active_integrations?' do
let_it_be(:project) { create(:project) }
- it { expect(project.has_active_services?).to be_falsey }
+ it { expect(project.has_active_integrations?).to be_falsey }
it 'returns true when a matching service exists' do
create(:custom_issue_tracker_integration, push_events: true, merge_requests_events: false, project: project)
- expect(project.has_active_services?(:merge_request_hooks)).to be_falsey
- expect(project.has_active_services?).to be_truthy
+ expect(project.has_active_integrations?(:merge_request_hooks)).to be_falsey
+ expect(project.has_active_integrations?).to be_truthy
end
end
@@ -5820,112 +5850,92 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#find_or_initialize_services' do
+ describe '#find_or_initialize_integrations' do
let_it_be(:subject) { create(:project) }
it 'avoids N+1 database queries' do
- control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_services }.count
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_integrations }.count
expect(control_count).to be <= 4
end
- it 'avoids N+1 database queries with more available services' do
- allow(Integration).to receive(:available_services_names).and_return(%w[pushover])
- control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_services }
-
- allow(Integration).to receive(:available_services_names).and_call_original
- expect { subject.find_or_initialize_services }.not_to exceed_query_limit(control_count)
- end
-
- context 'with disabled services' do
- before do
- allow(Integration).to receive(:available_services_names).and_return(%w[prometheus pushover teamcity])
- allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
- end
-
- it 'returns only enabled services sorted' do
- services = subject.find_or_initialize_services
+ it 'avoids N+1 database queries with more available integrations' do
+ allow(Integration).to receive(:available_integration_names).and_return(%w[pushover])
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_integrations }
- expect(services.size).to eq(2)
- expect(services.map(&:title)).to eq(['JetBrains TeamCity', 'Pushover'])
- end
+ allow(Integration).to receive(:available_integration_names).and_call_original
+ expect { subject.find_or_initialize_integrations }.not_to exceed_query_limit(control_count)
end
- end
-
- describe '#disabled_services' do
- subject { build(:project).disabled_services }
- context 'without datadog_ci_integration' do
+ context 'with disabled integrations' do
before do
- stub_feature_flags(datadog_ci_integration: false)
+ allow(Integration).to receive(:available_integration_names).and_return(%w[prometheus pushover teamcity])
+ allow(subject).to receive(:disabled_integrations).and_return(%w[prometheus])
end
- it { is_expected.to include('datadog') }
- end
-
- context 'with datadog_ci_integration' do
- before do
- stub_feature_flags(datadog_ci_integration: true)
+ it 'returns only enabled integrations sorted' do
+ expect(subject.find_or_initialize_integrations).to match [
+ have_attributes(title: 'JetBrains TeamCity'),
+ have_attributes(title: 'Pushover')
+ ]
end
-
- it { is_expected.not_to include('datadog') }
end
end
- describe '#find_or_initialize_service' do
+ describe '#find_or_initialize_integration' do
it 'avoids N+1 database queries' do
- allow(Integration).to receive(:available_services_names).and_return(%w[prometheus pushover])
+ allow(Integration).to receive(:available_integration_names).and_return(%w[prometheus pushover])
- control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_integration('prometheus') }.count
- allow(Integration).to receive(:available_services_names).and_call_original
+ allow(Integration).to receive(:available_integration_names).and_call_original
- expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
+ expect { subject.find_or_initialize_integration('prometheus') }.not_to exceed_query_limit(control_count)
end
it 'returns nil if integration is disabled' do
- allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
+ allow(subject).to receive(:disabled_integrations).and_return(%w[prometheus])
- expect(subject.find_or_initialize_service('prometheus')).to be_nil
+ expect(subject.find_or_initialize_integration('prometheus')).to be_nil
end
context 'with an existing integration' do
subject { create(:project) }
before do
- create(:prometheus_service, project: subject, api_url: 'https://prometheus.project.com/')
+ create(:prometheus_integration, project: subject, api_url: 'https://prometheus.project.com/')
end
it 'retrieves the integration' do
- expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.project.com/')
+ expect(subject.find_or_initialize_integration('prometheus').api_url).to eq('https://prometheus.project.com/')
end
end
context 'with an instance-level and template integrations' do
before do
- create(:prometheus_service, :instance, api_url: 'https://prometheus.instance.com/')
- create(:prometheus_service, :template, api_url: 'https://prometheus.template.com/')
+ create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/')
+ create(:prometheus_integration, :template, api_url: 'https://prometheus.template.com/')
end
- it 'builds the service from the instance if exists' do
- expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.instance.com/')
+ it 'builds the integration from the instance integration' do
+ expect(subject.find_or_initialize_integration('prometheus').api_url).to eq('https://prometheus.instance.com/')
end
end
- context 'with an instance-level and template integrations' do
+ context 'with a template integration and no instance-level' do
before do
- create(:prometheus_service, :template, api_url: 'https://prometheus.template.com/')
+ create(:prometheus_integration, :template, api_url: 'https://prometheus.template.com/')
end
- it 'builds the service from the template if instance does not exists' do
- expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.template.com/')
+ it 'builds the integration from the template' do
+ expect(subject.find_or_initialize_integration('prometheus').api_url).to eq('https://prometheus.template.com/')
end
end
- context 'without an exisiting integration, nor instance-level or template' do
- it 'builds the service if instance or template does not exists' do
- expect(subject.find_or_initialize_service('prometheus')).to be_a(PrometheusService)
- expect(subject.find_or_initialize_service('prometheus').api_url).to be_nil
+ context 'without an exisiting integration, or instance-level or template' do
+ it 'builds the integration' do
+ expect(subject.find_or_initialize_integration('prometheus')).to be_a(::Integrations::Prometheus)
+ expect(subject.find_or_initialize_integration('prometheus').api_url).to be_nil
end
end
end
@@ -6605,25 +6615,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#prometheus_service_active?' do
+ describe '#prometheus_integration_active?' do
let(:project) { create(:project) }
- subject { project.prometheus_service_active? }
+ subject { project.prometheus_integration_active? }
before do
- create(:prometheus_service, project: project, manual_configuration: manual_configuration)
+ create(:prometheus_integration, project: project, manual_configuration: manual_configuration)
end
- context 'when project has an activated prometheus service' do
+ context 'when project has an activated prometheus integration' do
let(:manual_configuration) { true }
it { is_expected.to be_truthy }
end
- context 'when project has an inactive prometheus service' do
+ context 'when project has an inactive prometheus integration' do
let(:manual_configuration) { false }
- it 'the service is marked as inactive' do
+ it 'the integration is marked as inactive' do
expect(subject).to be_falsey
end
end
diff --git a/spec/models/prometheus_alert_spec.rb b/spec/models/prometheus_alert_spec.rb
index 8e517e1764e..bfe2c7cc2a4 100644
--- a/spec/models/prometheus_alert_spec.rb
+++ b/spec/models/prometheus_alert_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe PrometheusAlert do
let_it_be(:project) { build(:project) }
+
let(:metric) { build(:prometheus_metric) }
describe '.distinct_projects' do
diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb
index fa84cd660cb..13d33b95b16 100644
--- a/spec/models/protected_branch/push_access_level_spec.rb
+++ b/spec/models/protected_branch/push_access_level_spec.rb
@@ -40,6 +40,7 @@ RSpec.describe ProtectedBranch::PushAccessLevel do
let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_push, project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:deploy_key) { create(:deploy_key, user: user) }
+
let!(:deploy_keys_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key, can_push: can_push) }
let(:can_push) { true }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c896b6c0c6c..452eafe733f 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -41,6 +41,7 @@ RSpec.describe Repository do
describe '#branch_names_contains' do
let_it_be(:project) { create(:project, :repository) }
+
let(:repository) { project.repository }
subject { repository.branch_names_contains(sample_commit.id) }
@@ -398,6 +399,7 @@ RSpec.describe Repository do
describe '#new_commits' do
let_it_be(:project) { create(:project, :repository) }
+
let(:repository) { project.repository }
subject { repository.new_commits(rev) }
@@ -426,6 +428,7 @@ RSpec.describe Repository do
describe '#commits_by' do
let_it_be(:project) { create(:project, :repository) }
+
let(:oids) { TestEnv::BRANCH_SHA.values }
subject { project.repository.commits_by(oids: oids) }
@@ -2990,6 +2993,7 @@ RSpec.describe Repository do
describe '#merge_base' do
let_it_be(:project) { create(:project, :repository) }
+
subject(:repository) { project.repository }
it 'only makes one gitaly call' do
@@ -3088,6 +3092,7 @@ RSpec.describe Repository do
describe "#blobs_metadata" do
let_it_be(:project) { create(:project, :repository) }
+
let(:repository) { project.repository }
def expect_metadata_blob(thing)
diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb
index 8ccbd983ba1..f99ac84175c 100644
--- a/spec/models/service_desk_setting_spec.rb
+++ b/spec/models/service_desk_setting_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe ServiceDeskSetting do
it { is_expected.to validate_length_of(:outgoing_name).is_at_most(255) }
it { is_expected.to validate_length_of(:project_key).is_at_most(255) }
it { is_expected.to allow_value('abc123_').for(:project_key) }
- it { is_expected.not_to allow_value('abc 12').for(:project_key) }
+ it { is_expected.not_to allow_value('abc 12').for(:project_key).with_message("can contain only lowercase letters, digits, and '_'.") }
it { is_expected.not_to allow_value('Big val').for(:project_key) }
describe '.valid_issue_template' do
diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb
index 11196f06529..40a28b9e0cc 100644
--- a/spec/models/snippet_repository_spec.rb
+++ b/spec/models/snippet_repository_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe SnippetRepository do
let_it_be(:user) { create(:user) }
+
let(:snippet) { create(:personal_snippet, :repository, author: user) }
let(:snippet_repository) { snippet.snippet_repository }
let(:commit_opts) { { branch_name: 'master', message: 'whatever' } }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 06e9899c0bd..19d3895177f 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -722,6 +722,7 @@ RSpec.describe Snippet do
describe '#list_files' do
let_it_be(:snippet) { create(:snippet, :repository) }
+
let(:ref) { 'test-ref' }
subject { snippet.list_files(ref) }
@@ -827,14 +828,10 @@ RSpec.describe Snippet do
end
context 'when default branch in settings is different from "master"' do
- let(:default_branch) { 'main' }
+ let(:default_branch) { 'custom-branch' }
it 'changes the HEAD reference to the default branch' do
- expect(File.read(head_path).squish).to eq 'ref: refs/heads/master'
-
- subject
-
- expect(File.read(head_path).squish).to eq "ref: refs/heads/#{default_branch}"
+ expect { subject }.to change { File.read(head_path).squish }.to("ref: refs/heads/#{default_branch}")
end
end
end
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index 1319e2adb03..a113ae37203 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe Terraform::State do
describe 'scopes' do
describe '.ordered_by_name' do
let_it_be(:project) { create(:project) }
+
let(:names) { %w(state_d state_b state_a state_c) }
subject { described_class.ordered_by_name }
diff --git a/spec/models/timelog_spec.rb b/spec/models/timelog_spec.rb
index bc042f7a639..9d6fda1d2a9 100644
--- a/spec/models/timelog_spec.rb
+++ b/spec/models/timelog_spec.rb
@@ -17,6 +17,8 @@ RSpec.describe Timelog do
it { is_expected.to validate_presence_of(:time_spent) }
it { is_expected.to validate_presence_of(:user) }
+ it { is_expected.to validate_length_of(:summary).is_at_most(255) }
+
it { expect(subject.project_id).not_to be_nil }
describe 'Issuable validation' do
diff --git a/spec/models/u2f_registration_spec.rb b/spec/models/u2f_registration_spec.rb
index 1f2e4d1e447..aba2f27d104 100644
--- a/spec/models/u2f_registration_spec.rb
+++ b/spec/models/u2f_registration_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe U2fRegistration do
let_it_be(:user) { create(:user) }
+
let(:u2f_registration) do
device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
create(:u2f_registration, name: 'u2f_device',
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e86a9c262d8..0eb769c65cd 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1005,6 +1005,7 @@ RSpec.describe User do
let_it_be(:valid_token_and_notified) { create(:personal_access_token, user: user2, expires_at: 2.days.from_now, expire_notification_delivered: true) }
let_it_be(:valid_token1) { create(:personal_access_token, user: user2, expires_at: 2.days.from_now) }
let_it_be(:valid_token2) { create(:personal_access_token, user: user2, expires_at: 2.days.from_now) }
+
let(:users) { described_class.with_expiring_and_not_notified_personal_access_tokens(from) }
context 'in one day' do
@@ -1898,6 +1899,14 @@ RSpec.describe User do
expect(user.deactivated?).to be_truthy
end
+
+ it 'sends deactivated user an email' do
+ expect_next_instance_of(NotificationService) do |notification|
+ allow(notification).to receive(:user_deactivated).with(user.name, user.notification_email)
+ end
+
+ user.deactivate
+ end
end
context "a user who is blocked" do
@@ -2826,6 +2835,14 @@ RSpec.describe User do
end
end
+ describe '#matches_identity?' do
+ it 'finds the identity when the DN is formatted differently' do
+ user = create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com')
+
+ expect(user.matches_identity?('ldapmain', 'uid=John Smith, ou=People, dc=example, dc=com')).to eq(true)
+ end
+ end
+
describe '#ldap_block' do
let(:user) { create(:omniauth_user, provider: 'ldapmain', name: 'John Smith') }
@@ -4241,6 +4258,7 @@ RSpec.describe User do
describe '#source_groups_of_two_factor_authentication_requirement' do
let_it_be(:group_not_requiring_2FA) { create :group }
+
let(:user) { create :user }
before do
@@ -5258,11 +5276,43 @@ RSpec.describe User do
end
describe '#password_expired_if_applicable?' do
- let(:user) { build(:user, password_expires_at: password_expires_at) }
+ let(:user) { build(:user, password_expires_at: password_expires_at, password_automatically_set: set_automatically?) }
subject { user.password_expired_if_applicable? }
context 'when user is not ldap user' do
+ context 'when user has password set automatically' do
+ let(:set_automatically?) { true }
+
+ context 'when password_expires_at is not set' do
+ let(:password_expires_at) {}
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'when password_expires_at is in the past' do
+ let(:password_expires_at) { 1.minute.ago }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when password_expires_at is in the future' do
+ let(:password_expires_at) { 1.minute.from_now }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
+ end
+
+ context 'when user has password not set automatically' do
+ let(:set_automatically?) { false }
+
context 'when password_expires_at is not set' do
let(:password_expires_at) {}
@@ -5274,8 +5324,8 @@ RSpec.describe User do
context 'when password_expires_at is in the past' do
let(:password_expires_at) { 1.minute.ago }
- it 'returns true' do
- is_expected.to be_truthy
+ it 'returns false' do
+ is_expected.to be_falsey
end
end
@@ -5319,6 +5369,34 @@ RSpec.describe User do
end
end
end
+
+ context 'when user is a project bot' do
+ let(:user) { build(:user, :project_bot, password_expires_at: password_expires_at) }
+
+ context 'when password_expires_at is not set' do
+ let(:password_expires_at) {}
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'when password_expires_at is in the past' do
+ let(:password_expires_at) { 1.minute.ago }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'when password_expires_at is in the future' do
+ let(:password_expires_at) { 1.minute.from_now }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
describe '#read_only_attribute?' do
@@ -5787,6 +5865,20 @@ RSpec.describe User do
end
end
+ describe '#default_dashboard?' do
+ it 'is the default dashboard' do
+ user = build(:user)
+
+ expect(user.default_dashboard?).to be true
+ end
+
+ it 'is not the default dashboard' do
+ user = build(:user, dashboard: 'stars')
+
+ expect(user.default_dashboard?).to be false
+ end
+ end
+
describe '.dormant' do
it 'returns dormant users' do
freeze_time do
@@ -5829,4 +5921,17 @@ RSpec.describe User do
end
end
end
+
+ describe '.by_provider_and_extern_uid' do
+ it 'calls Identity model scope to ensure case-insensitive query', :aggregate_failures do
+ expected_user = create(:user)
+ create(:identity, extern_uid: 'some-other-name-id', provider: :github)
+ create(:identity, extern_uid: 'my_github_id', provider: :gitlab)
+ create(:identity)
+ create(:identity, user: expected_user, extern_uid: 'my_github_id', provider: :github)
+
+ expect(Identity).to receive(:with_extern_uid).and_call_original
+ expect(described_class.by_provider_and_extern_uid(:github, 'my_github_id')).to match_array([expected_user])
+ end
+ end
end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 579a9e664cf..699dd35196f 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -201,11 +201,10 @@ RSpec.describe WikiPage do
expect(subject.errors.messages).to eq(title: ["can't be blank"])
end
- it "validates presence of content" do
+ it "does not validate presence of content" do
subject.attributes.delete(:content)
- expect(subject).not_to be_valid
- expect(subject.errors.messages).to eq(content: ["can't be blank"])
+ expect(subject).to be_valid
end
describe '#validate_content_size_limit' do