diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/models | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/models')
459 files changed, 5259 insertions, 1373 deletions
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 9ef77da6f43..9206f14fd37 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ability do +RSpec.describe Ability do context 'using a nil subject' do it 'has no permissions' do expect(described_class.policy_for(nil, nil)).to be_banned diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 2c4fa398636..a97574fa524 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AbuseReport do +RSpec.describe AbuseReport do let_it_be(:report, reload: true) { create(:abuse_report) } let_it_be(:user, reload: true) { create(:admin) } subject { report } diff --git a/spec/models/alert_management/alert_assignee_spec.rb b/spec/models/alert_management/alert_assignee_spec.rb index c51a5d543ab..c50a3ec0d01 100644 --- a/spec/models/alert_management/alert_assignee_spec.rb +++ b/spec/models/alert_management/alert_assignee_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AlertManagement::AlertAssignee do +RSpec.describe AlertManagement::AlertAssignee do describe 'associations' do it { is_expected.to belong_to(:alert) } it { is_expected.to belong_to(:assignee) } diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb index 27b8bb48073..becc5475c15 100644 --- a/spec/models/alert_management/alert_spec.rb +++ b/spec/models/alert_management/alert_spec.rb @@ -2,10 +2,12 @@ require 'spec_helper' -describe AlertManagement::Alert do +RSpec.describe AlertManagement::Alert do describe 'associations' do it { is_expected.to belong_to(:project) } - it { is_expected.to belong_to(:issue) } + it { is_expected.to belong_to(:issue).optional } + it { is_expected.to belong_to(:prometheus_alert).optional } + it { is_expected.to belong_to(:environment).optional } it { is_expected.to have_many(:assignees).through(:alert_assignees) } it { is_expected.to have_many(:notes) } it { is_expected.to have_many(:ordered_notes) } @@ -81,21 +83,50 @@ describe AlertManagement::Alert do end describe 'fingerprint' do + let_it_be(:project) { create(:project) } let_it_be(:fingerprint) { 'fingerprint' } - let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) } let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project) } subject { new_alert } context 'adding an alert with the same fingerprint' do - context 'same project' do - let(:project) { existing_alert.project } - - it { is_expected.not_to be_valid } + context 'same project, various states' do + using RSpec::Parameterized::TableSyntax + + # We are only validating uniqueness for non-resolved alerts + where(:existing_status, :new_status, :valid) do + :resolved | :triggered | true + :resolved | :acknowledged | true + :resolved | :ignored | true + :resolved | :resolved | true + :triggered | :triggered | false + :triggered | :acknowledged | false + :triggered | :ignored | false + :triggered | :resolved | true + :acknowledged | :triggered | false + :acknowledged | :acknowledged | false + :acknowledged | :ignored | false + :acknowledged | :resolved | true + :ignored | :triggered | false + :ignored | :acknowledged | false + :ignored | :ignored | false + :ignored | :resolved | true + end + + with_them do + let!(:existing_alert) { create(:alert_management_alert, existing_status, fingerprint: fingerprint, project: project) } + let(:new_alert) { build(:alert_management_alert, new_status, fingerprint: fingerprint, project: project) } + + if params[:valid] + it { is_expected.to be_valid } + else + it { is_expected.to be_invalid } + end + end end context 'different project' do - let(:project) { create(:project) } + let!(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) } it { is_expected.to be_valid } end @@ -163,6 +194,15 @@ describe AlertManagement::Alert do it { is_expected.to contain_exactly(alert_with_fingerprint) } end + describe '.for_environment' do + let(:environment) { create(:environment, project: project) } + let!(:env_alert) { create(:alert_management_alert, project: project, environment: environment) } + + subject { described_class.for_environment(environment) } + + it { is_expected.to match_array(env_alert) } + end + describe '.counts_by_status' do subject { described_class.counts_by_status } @@ -174,6 +214,51 @@ describe AlertManagement::Alert do ) end end + + describe '.counts_by_project_id' do + subject { described_class.counts_by_project_id } + + let!(:alert_other_project) { create(:alert_management_alert) } + + it do + is_expected.to eq( + project.id => 3, + alert_other_project.project.id => 1 + ) + end + end + + describe '.open' do + subject { described_class.open } + + let!(:acknowledged_alert) { create(:alert_management_alert, :acknowledged, project: project)} + + it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert) } + end + + describe '.not_resolved' do + subject { described_class.not_resolved } + + let!(:acknowledged_alert) { create(:alert_management_alert, :acknowledged, project: project) } + + it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert, ignored_alert) } + end + end + + describe '.last_prometheus_alert_by_project_id' do + subject { described_class.last_prometheus_alert_by_project_id } + + let(:project_1) { create(:project) } + let!(:alert_1) { create(:alert_management_alert, project: project_1) } + let!(:alert_2) { create(:alert_management_alert, project: project_1) } + + let(:project_2) { create(:project) } + let!(:alert_3) { create(:alert_management_alert, project: project_2) } + let!(:alert_4) { create(:alert_management_alert, project: project_2) } + + it 'returns the latest alert for each project' do + expect(subject).to contain_exactly(alert_2, alert_4) + end end describe '.search' do @@ -337,4 +422,22 @@ describe AlertManagement::Alert do expect { subject }.to change { alert.events }.by(1) end end + + describe '#present' do + context 'when alert is generic' do + let(:alert) { build(:alert_management_alert) } + + it 'uses generic alert presenter' do + expect(alert.present).to be_kind_of(AlertManagement::AlertPresenter) + end + end + + context 'when alert is Prometheus specific' do + let(:alert) { build(:alert_management_alert, :prometheus) } + + it 'uses Prometheus Alert presenter' do + expect(alert.present).to be_kind_of(AlertManagement::PrometheusAlertPresenter) + end + end + end end diff --git a/spec/models/alert_management/alert_user_mention_spec.rb b/spec/models/alert_management/alert_user_mention_spec.rb index cce090a2231..27c3d290dde 100644 --- a/spec/models/alert_management/alert_user_mention_spec.rb +++ b/spec/models/alert_management/alert_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AlertManagement::AlertUserMention do +RSpec.describe AlertManagement::AlertUserMention do describe 'associations' do it { is_expected.to belong_to(:alert_management_alert) } it { is_expected.to belong_to(:note) } diff --git a/spec/models/alerting/project_alerting_setting_spec.rb b/spec/models/alerting/project_alerting_setting_spec.rb index 40fbe3a6e78..90c5f8313b0 100644 --- a/spec/models/alerting/project_alerting_setting_spec.rb +++ b/spec/models/alerting/project_alerting_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Alerting::ProjectAlertingSetting do +RSpec.describe Alerting::ProjectAlertingSetting do let_it_be(:project) { create(:project) } subject { create(:project_alerting_setting, project: project) } diff --git a/spec/models/analytics/cycle_analytics/project_stage_spec.rb b/spec/models/analytics/cycle_analytics/project_stage_spec.rb index 9850bfde30e..2e024011553 100644 --- a/spec/models/analytics/cycle_analytics/project_stage_spec.rb +++ b/spec/models/analytics/cycle_analytics/project_stage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Analytics::CycleAnalytics::ProjectStage do +RSpec.describe Analytics::CycleAnalytics::ProjectStage do describe 'associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb index 2c32028c3e5..37eddf9a22a 100644 --- a/spec/models/appearance_spec.rb +++ b/spec/models/appearance_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Appearance do +RSpec.describe Appearance do subject { build(:appearance) } it { include(CacheableAttributes) } diff --git a/spec/models/application_record_spec.rb b/spec/models/application_record_spec.rb index 74573d0941c..cc314d9077d 100644 --- a/spec/models/application_record_spec.rb +++ b/spec/models/application_record_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ApplicationRecord do +RSpec.describe ApplicationRecord do describe '#id_in' do let(:records) { create_list(:user, 3) } @@ -58,4 +58,11 @@ describe ApplicationRecord do expect(MergeRequest.underscore).to eq('merge_request') end end + + describe '.at_most' do + it 'limits the number of records returned' do + create_list(:user, 3) + expect(User.at_most(2).count).to eq(2) + end + end end diff --git a/spec/models/application_setting/term_spec.rb b/spec/models/application_setting/term_spec.rb index dd263335b81..82347453437 100644 --- a/spec/models/application_setting/term_spec.rb +++ b/spec/models/application_setting/term_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ApplicationSetting::Term do +RSpec.describe ApplicationSetting::Term do describe 'validations' do it { is_expected.to validate_presence_of(:terms) } end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 96bf19439a1..f618e13ca26 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ApplicationSetting do +RSpec.describe ApplicationSetting do using RSpec::Parameterized::TableSyntax subject(:setting) { described_class.create_from_defaults } diff --git a/spec/models/approval_spec.rb b/spec/models/approval_spec.rb new file mode 100644 index 00000000000..e2c0d5faa07 --- /dev/null +++ b/spec/models/approval_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Approval do + context 'presence validation' do + it { is_expected.to validate_presence_of(:merge_request_id) } + it { is_expected.to validate_presence_of(:user_id) } + end + + context 'uniqueness validation' do + let!(:existing_record) { create(:approval) } + + it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:merge_request_id]) } + end +end diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb index 8b370e80c9b..f268408c095 100644 --- a/spec/models/award_emoji_spec.rb +++ b/spec/models/award_emoji_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AwardEmoji do +RSpec.describe AwardEmoji do describe 'Associations' do it { is_expected.to belong_to(:awardable) } it { is_expected.to belong_to(:user) } diff --git a/spec/models/aws/role_spec.rb b/spec/models/aws/role_spec.rb index d4165567146..612868f6eb0 100644 --- a/spec/models/aws/role_spec.rb +++ b/spec/models/aws/role_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Aws::Role do +RSpec.describe Aws::Role do it { is_expected.to belong_to(:user) } it { is_expected.to validate_length_of(:role_external_id).is_at_least(1).is_at_most(64) } diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb index fba8f40e99b..f3c95332ca0 100644 --- a/spec/models/badge_spec.rb +++ b/spec/models/badge_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Badge do +RSpec.describe Badge do let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{default_branch}/%{commit_sha}' } describe 'validations' do diff --git a/spec/models/badges/group_badge_spec.rb b/spec/models/badges/group_badge_spec.rb index c297bc957ea..8913a2e6f17 100644 --- a/spec/models/badges/group_badge_spec.rb +++ b/spec/models/badges/group_badge_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupBadge do +RSpec.describe GroupBadge do describe 'associations' do it { is_expected.to belong_to(:group) } end diff --git a/spec/models/badges/project_badge_spec.rb b/spec/models/badges/project_badge_spec.rb index c0e85d3de87..9b9836129a6 100644 --- a/spec/models/badges/project_badge_spec.rb +++ b/spec/models/badges/project_badge_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectBadge do +RSpec.describe ProjectBadge do let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{default_branch}/%{commit_sha}' } describe 'associations' do diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb index c2d6406c3fb..bd4832bd978 100644 --- a/spec/models/blob_spec.rb +++ b/spec/models/blob_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Blob do +RSpec.describe Blob do include FakeBlobHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/models/blob_viewer/base_spec.rb b/spec/models/blob_viewer/base_spec.rb index 39c7a34f052..682b6dc3b1d 100644 --- a/spec/models/blob_viewer/base_spec.rb +++ b/spec/models/blob_viewer/base_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::Base do +RSpec.describe BlobViewer::Base do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/changelog_spec.rb b/spec/models/blob_viewer/changelog_spec.rb index b71531ff3c2..5346483cfc8 100644 --- a/spec/models/blob_viewer/changelog_spec.rb +++ b/spec/models/blob_viewer/changelog_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::Changelog do +RSpec.describe BlobViewer::Changelog do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/blob_viewer/composer_json_spec.rb b/spec/models/blob_viewer/composer_json_spec.rb index a6bb64ba121..8d66e9e951f 100644 --- a/spec/models/blob_viewer/composer_json_spec.rb +++ b/spec/models/blob_viewer/composer_json_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::ComposerJson do +RSpec.describe BlobViewer::ComposerJson do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/gemspec_spec.rb b/spec/models/blob_viewer/gemspec_spec.rb index 291d14e2d72..b6f3e059c7e 100644 --- a/spec/models/blob_viewer/gemspec_spec.rb +++ b/spec/models/blob_viewer/gemspec_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::Gemspec do +RSpec.describe BlobViewer::Gemspec do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb index e645733e02d..cd885d312dc 100644 --- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb +++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::GitlabCiYml do +RSpec.describe BlobViewer::GitlabCiYml do include FakeBlobHelpers include RepoHelpers diff --git a/spec/models/blob_viewer/go_mod_spec.rb b/spec/models/blob_viewer/go_mod_spec.rb index ba6038533ea..21e84d39a54 100644 --- a/spec/models/blob_viewer/go_mod_spec.rb +++ b/spec/models/blob_viewer/go_mod_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::GoMod do +RSpec.describe BlobViewer::GoMod do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/license_spec.rb b/spec/models/blob_viewer/license_spec.rb index b0426401932..bc970136503 100644 --- a/spec/models/blob_viewer/license_spec.rb +++ b/spec/models/blob_viewer/license_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::License do +RSpec.describe BlobViewer::License do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb index f5b8586975d..057f0f32158 100644 --- a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb +++ b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::MetricsDashboardYml do +RSpec.describe BlobViewer::MetricsDashboardYml do include FakeBlobHelpers include RepoHelpers diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb index 7f7b1dcfcb3..d2e8ab6575f 100644 --- a/spec/models/blob_viewer/package_json_spec.rb +++ b/spec/models/blob_viewer/package_json_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::PackageJson do +RSpec.describe BlobViewer::PackageJson do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/podspec_json_spec.rb b/spec/models/blob_viewer/podspec_json_spec.rb index dd5ed03b77d..61d2602c413 100644 --- a/spec/models/blob_viewer/podspec_json_spec.rb +++ b/spec/models/blob_viewer/podspec_json_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::PodspecJson do +RSpec.describe BlobViewer::PodspecJson do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/podspec_spec.rb b/spec/models/blob_viewer/podspec_spec.rb index 2d9b184c5cb..0a0fbcaebd4 100644 --- a/spec/models/blob_viewer/podspec_spec.rb +++ b/spec/models/blob_viewer/podspec_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::Podspec do +RSpec.describe BlobViewer::Podspec do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/readme_spec.rb b/spec/models/blob_viewer/readme_spec.rb index 89bc5be94fb..4c5f11d55ff 100644 --- a/spec/models/blob_viewer/readme_spec.rb +++ b/spec/models/blob_viewer/readme_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::Readme do +RSpec.describe BlobViewer::Readme do include FakeBlobHelpers let(:project) { create(:project, :repository, :wiki_repo) } diff --git a/spec/models/blob_viewer/route_map_spec.rb b/spec/models/blob_viewer/route_map_spec.rb index 6c703df5c4c..bb0284d7868 100644 --- a/spec/models/blob_viewer/route_map_spec.rb +++ b/spec/models/blob_viewer/route_map_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::RouteMap do +RSpec.describe BlobViewer::RouteMap do include FakeBlobHelpers let(:project) { build_stubbed(:project) } diff --git a/spec/models/blob_viewer/server_side_spec.rb b/spec/models/blob_viewer/server_side_spec.rb index f95305abe78..284ac4524f7 100644 --- a/spec/models/blob_viewer/server_side_spec.rb +++ b/spec/models/blob_viewer/server_side_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobViewer::ServerSide do +RSpec.describe BlobViewer::ServerSide do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb index 558be61824f..4d16e1ff839 100644 --- a/spec/models/board_group_recent_visit_spec.rb +++ b/spec/models/board_group_recent_visit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BoardGroupRecentVisit do +RSpec.describe BoardGroupRecentVisit do let(:user) { create(:user) } let(:group) { create(:group) } let(:board) { create(:board, group: group) } diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb index e404fb3bbdb..8e74405fd8c 100644 --- a/spec/models/board_project_recent_visit_spec.rb +++ b/spec/models/board_project_recent_visit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BoardProjectRecentVisit do +RSpec.describe BoardProjectRecentVisit do let(:user) { create(:user) } let(:project) { create(:project) } let(:board) { create(:board, project: project) } diff --git a/spec/models/board_spec.rb b/spec/models/board_spec.rb index 2d5309b4d23..d309b4dbdb9 100644 --- a/spec/models/board_spec.rb +++ b/spec/models/board_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Board do +RSpec.describe Board do let(:project) { create(:project) } let(:other_project) { create(:project) } diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 8032f913d86..fc463c6af52 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BroadcastMessage do +RSpec.describe BroadcastMessage do subject { build(:broadcast_message) } it { is_expected.to be_valid } diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb index 02594b98665..623e55aad21 100644 --- a/spec/models/chat_name_spec.rb +++ b/spec/models/chat_name_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatName do +RSpec.describe ChatName do let_it_be(:chat_name) { create(:chat_name) } subject { chat_name } diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb index 107fdaccc68..08fd05324aa 100644 --- a/spec/models/chat_team_spec.rb +++ b/spec/models/chat_team_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatTeam do +RSpec.describe ChatTeam do let_it_be(:chat_team) { create(:chat_team) } subject { chat_team } diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb index 99983686670..44f895cc1c5 100644 --- a/spec/models/ci/artifact_blob_spec.rb +++ b/spec/models/ci/artifact_blob_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::ArtifactBlob do +RSpec.describe Ci::ArtifactBlob do let_it_be(:project) { create(:project, :public) } let_it_be(:build) { create(:ci_build, :artifacts, project: project) } let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 385261e0ee9..3a459e5897a 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Bridge do +RSpec.describe Ci::Bridge do let_it_be(:project) { create(:project) } let_it_be(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } @@ -47,8 +47,8 @@ describe Ci::Bridge do CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH - CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID - CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE + CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE + CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED ] diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb index 8f2199ac360..4fa1b3eb5a5 100644 --- a/spec/models/ci/build_dependencies_spec.rb +++ b/spec/models/ci/build_dependencies_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildDependencies do +RSpec.describe Ci::BuildDependencies do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :repository) } diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 588e5872cc8..e4d71632957 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildMetadata do +RSpec.describe Ci::BuildMetadata do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :repository, group: group, build_timeout: 2000) } @@ -92,4 +92,33 @@ describe Ci::BuildMetadata do end end end + + describe 'validations' do + context 'when attributes are valid' do + it 'returns no errors' do + metadata.secrets = { + DATABASE_PASSWORD: { + vault: { + engine: { name: 'kv-v2', path: 'kv-v2' }, + path: 'production/db', + field: 'password' + } + } + } + + expect(metadata).to be_valid + end + end + + context 'when data is invalid' do + it 'returns errors' do + metadata.secrets = { DATABASE_PASSWORD: { vault: {} } } + + aggregate_failures do + expect(metadata).to be_invalid + expect(metadata.errors.full_messages).to eq(["Secrets must be a valid json schema"]) + end + end + end + end end diff --git a/spec/models/ci/build_need_spec.rb b/spec/models/ci/build_need_spec.rb index d1186fa981d..43cce073918 100644 --- a/spec/models/ci/build_need_spec.rb +++ b/spec/models/ci/build_need_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildNeed, model: true do +RSpec.describe Ci::BuildNeed, model: true do let(:build_need) { build(:ci_build_need) } it { is_expected.to belong_to(:build) } @@ -17,4 +17,22 @@ describe Ci::BuildNeed, model: true do it { expect(described_class.artifacts).to contain_exactly(with_artifacts) } end + + describe 'BulkInsertSafe' do + let(:ci_build) { build(:ci_build) } + + it "bulk inserts from Ci::Build model" do + ci_build.needs_attributes = [ + { name: "build", artifacts: true }, + { name: "build2", artifacts: true }, + { name: "build3", artifacts: true } + ] + + expect(described_class).to receive(:bulk_insert!).and_call_original + + BulkInsertableAssociations.with_bulk_insert do + ci_build.save! + end + end + end end diff --git a/spec/models/ci/build_report_result_spec.rb b/spec/models/ci/build_report_result_spec.rb index 078b0d100a1..e78f602feef 100644 --- a/spec/models/ci/build_report_result_spec.rb +++ b/spec/models/ci/build_report_result_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildReportResult do +RSpec.describe Ci::BuildReportResult do let(:build_report_result) { build(:ci_build_report_result, :with_junit_success) } describe 'associations' do diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb index 3e520407884..601c6ad26f9 100644 --- a/spec/models/ci/build_runner_session_spec.rb +++ b/spec/models/ci/build_runner_session_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildRunnerSession, model: true do +RSpec.describe Ci::BuildRunnerSession, model: true do let!(:build) { create(:ci_build, :with_runner_session) } let(:url) { 'https://new.example.com' } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 6fdd8463329..857b238981b 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Build do +RSpec.describe Ci::Build do let_it_be(:user) { create(:user) } let_it_be(:group, reload: true) { create(:group) } let_it_be(:project, reload: true) { create(:project, :repository, group: group) } @@ -1811,6 +1811,50 @@ describe Ci::Build do end end + describe '.keep_artifacts!' do + let!(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } + let!(:builds_for_update) do + Ci::Build.where(id: create_list(:ci_build, 3, artifacts_expire_at: Time.current + 7.days).map(&:id)) + end + + it 'resets expire_at' do + builds_for_update.keep_artifacts! + + builds_for_update.each do |build| + expect(build.reload.artifacts_expire_at).to be_nil + end + end + + it 'does not reset expire_at for other builds' do + builds_for_update.keep_artifacts! + + expect(build.reload.artifacts_expire_at).to be_present + end + + context 'when having artifacts files' do + let!(:artifact) { create(:ci_job_artifact, job: build, expire_in: '7 days') } + let!(:artifacts_for_update) do + builds_for_update.map do |build| + create(:ci_job_artifact, job: build, expire_in: '7 days') + end + end + + it 'resets dependent objects' do + builds_for_update.keep_artifacts! + + artifacts_for_update.each do |artifact| + expect(artifact.reload.expire_at).to be_nil + end + end + + it 'does not reset dependent object for other builds' do + builds_for_update.keep_artifacts! + + expect(artifact.reload.expire_at).to be_present + end + end + end + describe '#keep_artifacts!' do let(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } @@ -2336,6 +2380,7 @@ describe Ci::Build do { key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false }, { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false }, { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false }, + { key: 'CI_PROJECT_ROOT_NAMESPACE', value: project.namespace.root_ancestor.path, public: true, masked: false }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false }, { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false }, { key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false }, @@ -2929,19 +2974,6 @@ describe Ci::Build do it { is_expected.to include(deployment_variable) } end - context 'when build has a freeze period' do - let(:freeze_variable) { { key: 'CI_DEPLOY_FREEZE', value: 'true', masked: false, public: true } } - - before do - expect_next_instance_of(Ci::FreezePeriodStatus) do |freeze_period| - expect(freeze_period).to receive(:execute) - .and_return(true) - end - end - - it { is_expected.to include(freeze_variable) } - end - context 'when project has default CI config path' do let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: '.gitlab-ci.yml', public: true, masked: false } } @@ -3269,17 +3301,6 @@ describe Ci::Build do expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1') end end - - context 'when CI instance variables are disabled' do - before do - create(:ci_instance_variable, key: 'MY_VAR', value: 'my value 1') - stub_feature_flags(ci_instance_level_variables: false) - end - - it 'does not include instance level variables' do - expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1') - end - end end describe '#any_unmet_prerequisites?' do @@ -4050,6 +4071,10 @@ describe Ci::Build do it 'parses blobs and add the results to the terraform report' do expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error + terraform_reports.plans.each do |key, hash_value| + expect(hash_value.keys).to match_array(%w[create delete job_id job_name job_path update]) + end + expect(terraform_reports.plans).to match( a_hash_including( build.id.to_s => a_hash_including( @@ -4068,9 +4093,19 @@ describe Ci::Build do create(:ci_job_artifact, :terraform_with_corrupted_data, job: build, project: build.project) end - it 'raises an error' do - expect { build.collect_terraform_reports!(terraform_reports) }.to raise_error( - Gitlab::Ci::Parsers::Terraform::Tfplan::TfplanParserError + it 'adds invalid plan report' do + expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error + + terraform_reports.plans.each do |key, hash_value| + expect(hash_value.keys).to match_array(%w[job_id job_name job_path tf_report_error]) + end + + expect(terraform_reports.plans).to match( + a_hash_including( + build.id.to_s => a_hash_including( + 'tf_report_error' => :invalid_json_format + ) + ) ) end end @@ -4258,15 +4293,15 @@ describe Ci::Build do end end - context 'when `release_steps` feature is required by build' do + context 'when `multi_build_steps` feature is required by build' do before do expect(build).to receive(:runner_required_feature_names) do - [:release_steps] + [:multi_build_steps] end end context 'when runner provides given feature' do - let(:runner_features) { { release_steps: true } } + let(:runner_features) { { multi_build_steps: true } } it { is_expected.to be_truthy } end diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 85873847fca..dab523f67ab 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do +RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do include ExclusiveLeaseHelpers let_it_be(:build) { create(:ci_build, :running) } diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb index eb94d7dae38..245625b8046 100644 --- a/spec/models/ci/build_trace_chunks/database_spec.rb +++ b/spec/models/ci/build_trace_chunks/database_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Database do +RSpec.describe Ci::BuildTraceChunks::Database do let(:data_store) { described_class.new } describe '#available?' do diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb index b8d78bcd069..7ef3018d87b 100644 --- a/spec/models/ci/build_trace_chunks/fog_spec.rb +++ b/spec/models/ci/build_trace_chunks/fog_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Fog do +RSpec.describe Ci::BuildTraceChunks::Fog do let(:data_store) { described_class.new } before do diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb index 6cff33d24fa..c37b8697a4d 100644 --- a/spec/models/ci/build_trace_chunks/redis_spec.rb +++ b/spec/models/ci/build_trace_chunks/redis_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do +RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do let(:data_store) { described_class.new } describe '#available?' do diff --git a/spec/models/ci/build_trace_section_name_spec.rb b/spec/models/ci/build_trace_section_name_spec.rb index 11e2d27ff79..b220e67d48e 100644 --- a/spec/models/ci/build_trace_section_name_spec.rb +++ b/spec/models/ci/build_trace_section_name_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceSectionName, model: true do +RSpec.describe Ci::BuildTraceSectionName, model: true do subject { build(:ci_build_trace_section_name) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/ci/build_trace_section_spec.rb b/spec/models/ci/build_trace_section_spec.rb index 5bd3a953ec0..640bd202b3a 100644 --- a/spec/models/ci/build_trace_section_spec.rb +++ b/spec/models/ci/build_trace_section_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceSection, model: true do +RSpec.describe Ci::BuildTraceSection, model: true do it { is_expected.to belong_to(:build)} it { is_expected.to belong_to(:project)} it { is_expected.to belong_to(:section_name)} diff --git a/spec/models/ci/build_trace_spec.rb b/spec/models/ci/build_trace_spec.rb index 2471a6fa827..3beca0565c6 100644 --- a/spec/models/ci/build_trace_spec.rb +++ b/spec/models/ci/build_trace_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTrace do +RSpec.describe Ci::BuildTrace do let(:build) { build_stubbed(:ci_build) } let(:state) { nil } let(:data) { StringIO.new('the-stream') } @@ -11,7 +11,7 @@ describe Ci::BuildTrace do Gitlab::Ci::Trace::Stream.new { data } end - subject { described_class.new(build: build, stream: stream, state: state, content_format: content_format) } + subject { described_class.new(build: build, stream: stream, state: state) } shared_examples 'delegates methods' do it { is_expected.to delegate_method(:state).to(:trace) } @@ -25,29 +25,11 @@ describe Ci::BuildTrace do it { is_expected.to delegate_method(:complete?).to(:build).with_prefix } end - context 'with :json content format' do - let(:content_format) { :json } + it_behaves_like 'delegates methods' - it_behaves_like 'delegates methods' - - it { is_expected.to be_json } - - it 'returns formatted trace' do - expect(subject.trace.lines).to eq([ - { offset: 0, content: [{ text: 'the-stream' }] } - ]) - end - end - - context 'with :html content format' do - let(:content_format) { :html } - - it_behaves_like 'delegates methods' - - it { is_expected.to be_html } - - it 'returns formatted trace' do - expect(subject.trace.html).to eq('<span>the-stream</span>') - end + it 'returns formatted trace' do + expect(subject.lines).to eq([ + { offset: 0, content: [{ text: 'the-stream' }] } + ]) end end diff --git a/spec/models/ci/daily_build_group_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb index f2ce1b5775f..059a5b76b9a 100644 --- a/spec/models/ci/daily_build_group_report_result_spec.rb +++ b/spec/models/ci/daily_build_group_report_result_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::DailyBuildGroupReportResult do +RSpec.describe Ci::DailyBuildGroupReportResult do let(:daily_build_group_report_result) { build(:ci_daily_build_group_report_result)} describe 'associations' do diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb index b700ec8c45f..831895cb528 100644 --- a/spec/models/ci/freeze_period_status_spec.rb +++ b/spec/models/ci/freeze_period_status_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe Ci::FreezePeriodStatus do +RSpec.describe Ci::FreezePeriodStatus do let(:project) { create :project } # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday."" let(:friday_2300) { '0 23 * * 5' } diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index 868382e3756..dc9aee906ea 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Group do +RSpec.describe Ci::Group do let_it_be(:project) { create(:project) } let!(:jobs) { build_list(:ci_build, 1, :success, project: project) } diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb index 610db9bf0e5..c8eac4d8765 100644 --- a/spec/models/ci/group_variable_spec.rb +++ b/spec/models/ci/group_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::GroupVariable do +RSpec.describe Ci::GroupVariable do subject { build(:ci_group_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb index 4d69b7ac2f8..344ba5bfafd 100644 --- a/spec/models/ci/instance_variable_spec.rb +++ b/spec/models/ci/instance_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::InstanceVariable do +RSpec.describe Ci::InstanceVariable do subject { build(:ci_instance_variable) } it_behaves_like "CI variable" @@ -15,21 +15,6 @@ describe Ci::InstanceVariable do subject { build(:ci_instance_variable) } end - context 'with instance level variable feature flag disabled' do - let(:plan_limits) { create(:plan_limits, :default_plan) } - - before do - stub_feature_flags(ci_instance_level_variables_limit: false) - plan_limits.update(described_class.limit_name => 1) - create(:ci_instance_variable) - end - - it 'can create new models exceeding the plan limits', :aggregate_failures do - expect { subject.save }.to change { described_class.count } - expect(subject.errors[:base]).to be_empty - end - end - describe '.unprotected' do subject { described_class.unprotected } diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 17e00533ac3..b5f9128b7c5 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::JobArtifact do +RSpec.describe Ci::JobArtifact do let(:artifact) { create(:ci_job_artifact, :archive) } describe "Associations" do @@ -110,6 +110,21 @@ describe Ci::JobArtifact do end end + describe '.associated_file_types_for' do + using RSpec::Parameterized::TableSyntax + + subject { Ci::JobArtifact.associated_file_types_for(file_type) } + + where(:file_type, :result) do + 'codequality' | %w(codequality) + 'quality' | nil + end + + with_them do + it { is_expected.to eq result } + end + end + describe '.erasable' do subject { described_class.erasable } @@ -174,18 +189,6 @@ describe Ci::JobArtifact do end end - describe '.for_ref' do - let(:first_pipeline) { create(:ci_pipeline, ref: 'first_ref') } - let(:second_pipeline) { create(:ci_pipeline, ref: 'second_ref', project: first_pipeline.project) } - let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) } - let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) } - - it 'returns job artifacts for a given pipeline ref' do - expect(described_class.for_ref(first_pipeline.ref, first_pipeline.project.id)).to eq([first_artifact]) - expect(described_class.for_ref(second_pipeline.ref, first_pipeline.project.id)).to eq([second_artifact]) - end - end - describe '.for_job_name' do it 'returns job artifacts for a given job name' do first_job = create(:ci_build, name: 'first') @@ -501,4 +504,100 @@ describe Ci::JobArtifact do end end end + + describe '.file_types' do + context 'all file types have corresponding limit' do + let_it_be(:plan_limits) { create(:plan_limits) } + + where(:file_type) do + described_class.file_types.keys + end + + with_them do + let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{file_type}" } + + it { expect(plan_limits.attributes).to include(limit_name), file_type_limit_failure_message(file_type, limit_name) } + end + end + end + + describe '.max_artifact_size' do + let(:build) { create(:ci_build) } + + subject(:max_size) { described_class.max_artifact_size(type: artifact_type, project: build.project) } + + context 'when file type is supported' do + let(:project_closest_setting) { 1024 } + let(:artifact_type) { 'junit' } + + before do + stub_feature_flags(ci_max_artifact_size_per_type: flag_enabled) + allow(build.project).to receive(:closest_setting).with(:max_artifacts_size).and_return(project_closest_setting) + end + + shared_examples_for 'basing off the project closest setting' do + it { is_expected.to eq(project_closest_setting.megabytes.to_i) } + end + + shared_examples_for 'basing off the plan limit' do + it { is_expected.to eq(max_size_for_type.megabytes.to_i) } + end + + context 'and feature flag for custom max size per type is enabled' do + let(:flag_enabled) { true } + let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{artifact_type}" } + + let!(:plan_limits) { create(:plan_limits, :default_plan) } + + context 'and plan limit is disabled for the given artifact type' do + before do + plan_limits.update!(limit_name => 0) + end + + it_behaves_like 'basing off the project closest setting' + + context 'and project closest setting results to zero' do + let(:project_closest_setting) { 0 } + + it { is_expected.to eq(0) } + end + end + + context 'and plan limit is enabled for the given artifact type' do + before do + plan_limits.update!(limit_name => max_size_for_type) + end + + context 'and plan limit is smaller than project setting' do + let(:max_size_for_type) { project_closest_setting - 1 } + + it_behaves_like 'basing off the plan limit' + end + + context 'and plan limit is smaller than project setting' do + let(:max_size_for_type) { project_closest_setting + 1 } + + it_behaves_like 'basing off the project closest setting' + end + end + end + + context 'and feature flag for custom max size per type is disabled' do + let(:flag_enabled) { false } + + it_behaves_like 'basing off the project closest setting' + end + end + end + + def file_type_limit_failure_message(type, limit_name) + <<~MSG + The artifact type `#{type}` is missing its counterpart plan limit which is expected to be named `#{limit_name}`. + + Please refer to https://docs.gitlab.com/ee/development/application_limits.html on how to add new plan limit columns. + + Take note that while existing max size plan limits default to 0, succeeding new limits are recommended to have + non-zero default values. + MSG + end end diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb index b94a914c784..4aebd3283f0 100644 --- a/spec/models/ci/job_variable_spec.rb +++ b/spec/models/ci/job_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::JobVariable do +RSpec.describe Ci::JobVariable do subject { build(:ci_job_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb index f503fc10c08..c53f6abb037 100644 --- a/spec/models/ci/legacy_stage_spec.rb +++ b/spec/models/ci/legacy_stage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::LegacyStage do +RSpec.describe Ci::LegacyStage do let(:stage) { build(:ci_stage) } let(:pipeline) { stage.pipeline } let(:stage_name) { stage.name } diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb index 89dd9b05331..18552317025 100644 --- a/spec/models/ci/persistent_ref_spec.rb +++ b/spec/models/ci/persistent_ref_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PersistentRef do +RSpec.describe Ci::PersistentRef do it 'cleans up persistent refs after pipeline finished' do pipeline = create(:ci_pipeline, :running) diff --git a/spec/models/ci/pipeline_config_spec.rb b/spec/models/ci/pipeline_config_spec.rb index 25f514ee5ab..3d033d33df3 100644 --- a/spec/models/ci/pipeline_config_spec.rb +++ b/spec/models/ci/pipeline_config_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineConfig, type: :model do +RSpec.describe Ci::PipelineConfig, type: :model do it { is_expected.to belong_to(:pipeline) } it { is_expected.to validate_presence_of(:pipeline) } diff --git a/spec/models/ci/pipeline_message_spec.rb b/spec/models/ci/pipeline_message_spec.rb new file mode 100644 index 00000000000..6c97a025625 --- /dev/null +++ b/spec/models/ci/pipeline_message_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::PipelineMessage do + describe 'validations' do + subject { described_class.new(pipeline: pipeline, content: content) } + + let(:pipeline) { create(:ci_pipeline) } + + context 'when message content is longer than the limit' do + let(:content) { 'x' * (described_class::MAX_CONTENT_LENGTH + 1) } + + it 'is truncated with ellipsis' do + subject.save! + + expect(subject.content).to end_with('x...') + expect(subject.content.length).to eq(described_class::MAX_CONTENT_LENGTH) + end + end + + context 'when message is not present' do + let(:content) { '' } + + it 'returns an error' do + expect(subject.save).to be_falsey + expect(subject.errors[:content]).to be_present + end + end + + context 'when message content is valid' do + let(:content) { 'valid message content' } + + it 'is saved with default error severity' do + subject.save! + + expect(subject.content).to eq(content) + expect(subject.severity).to eq('error') + expect(subject).to be_error + end + + it 'is persist the defined severity' do + subject.severity = :warning + + subject.save! + + expect(subject.content).to eq(content) + expect(subject.severity).to eq('warning') + expect(subject).to be_warning + end + end + end +end diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 4ba70552f01..949d5f7bd04 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineSchedule do +RSpec.describe Ci::PipelineSchedule do subject { build(:ci_pipeline_schedule) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/ci/pipeline_schedule_variable_spec.rb b/spec/models/ci/pipeline_schedule_variable_spec.rb index c96a24d5042..fd6b1d3dce0 100644 --- a/spec/models/ci/pipeline_schedule_variable_spec.rb +++ b/spec/models/ci/pipeline_schedule_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineScheduleVariable do +RSpec.describe Ci::PipelineScheduleVariable do subject { build(:ci_pipeline_schedule_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 782a4206c36..ed2466d6413 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Pipeline, :mailer do +RSpec.describe Ci::Pipeline, :mailer do include ProjectForksHelper include StubRequests @@ -219,6 +219,50 @@ describe Ci::Pipeline, :mailer do end end + describe '.outside_pipeline_family' do + subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) } + + let(:upstream_pipeline) { create(:ci_pipeline, project: project) } + let(:child_pipeline) { create(:ci_pipeline, project: project) } + + let!(:other_pipeline) { create(:ci_pipeline, project: project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: upstream_pipeline), + source_project: project, + pipeline: child_pipeline, + project: project) + end + + it 'only returns pipelines outside pipeline family' do + expect(outside_pipeline_family).to contain_exactly(other_pipeline) + end + end + + describe '.before_pipeline' do + subject(:before_pipeline) { described_class.before_pipeline(child_pipeline) } + + let!(:older_other_pipeline) { create(:ci_pipeline, project: project) } + + let!(:upstream_pipeline) { create(:ci_pipeline, project: project) } + let!(:child_pipeline) { create(:ci_pipeline, project: project) } + + let!(:other_pipeline) { create(:ci_pipeline, project: project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: upstream_pipeline), + source_project: project, + pipeline: child_pipeline, + project: project) + end + + it 'only returns older pipelines outside pipeline family' do + expect(before_pipeline).to contain_exactly(older_other_pipeline) + end + end + describe '#merge_request?' do let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) } let(:merge_request) { create(:merge_request) } @@ -1488,6 +1532,35 @@ describe Ci::Pipeline, :mailer do sha: project.commit.sha) end + describe '#lazy_ref_commit' do + let(:another) do + create(:ci_pipeline, + project: project, + ref: 'feature', + sha: project.commit('feature').sha) + end + + let(:unicode) do + create(:ci_pipeline, + project: project, + ref: 'ü/unicode/multi-byte') + end + + it 'returns the latest commit for a ref lazily' do + expect(project.repository) + .to receive(:list_commits_by_ref_name).once + .and_call_original + + pipeline.lazy_ref_commit + another.lazy_ref_commit + unicode.lazy_ref_commit + + expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha + expect(another.lazy_ref_commit.id).to eq another.sha + expect(unicode.lazy_ref_commit).to be_nil + end + end + describe '#latest?' do context 'with latest sha' do it 'returns true' do @@ -1496,17 +1569,26 @@ describe Ci::Pipeline, :mailer do end context 'with a branch name as the ref' do - it 'looks up commit with the full ref name' do - expect(pipeline.project).to receive(:commit).with('refs/heads/master').and_call_original + it 'looks up a commit for a branch' do + expect(pipeline.ref).to eq 'master' + expect(pipeline).to be_latest + end + end + + context 'with a tag name as a ref' do + it 'looks up a commit for a tag' do + expect(project.repository.branch_names).not_to include 'v1.0.0' + pipeline.update(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true) + + expect(pipeline).to be_tag expect(pipeline).to be_latest end end context 'with not latest sha' do before do - pipeline.update( - sha: project.commit("#{project.default_branch}~1").sha) + pipeline.update(sha: project.commit("#{project.default_branch}~1").sha) end it 'returns false' do @@ -1932,6 +2014,23 @@ describe Ci::Pipeline, :mailer do end end + describe '.last_finished_for_ref_id' do + let(:project) { create(:project, :repository) } + let(:branch) { project.default_branch } + let(:ref) { project.ci_refs.take } + let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] } + let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) } + let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) } + + it 'returns the expected pipeline' do + result = described_class.last_finished_for_ref_id(ref.id) + expect(result).to eq(pipeline4) + end + end + describe '.internal_sources' do subject { described_class.internal_sources } @@ -2087,7 +2186,7 @@ describe Ci::Pipeline, :mailer do it 'raises an exception' do expect { pipeline.update_legacy_status } - .to raise_error(HasStatus::UnknownStatusError) + .to raise_error(Ci::HasStatus::UnknownStatusError) end end end @@ -2580,6 +2679,55 @@ describe Ci::Pipeline, :mailer do end end + describe '#same_family_pipeline_ids' do + subject(:same_family_pipeline_ids) { pipeline.same_family_pipeline_ids } + + context 'when pipeline is not child nor parent' do + it 'returns just the pipeline id' do + expect(same_family_pipeline_ids).to contain_exactly(pipeline.id) + end + end + + context 'when pipeline is child' do + let(:parent) { create(:ci_pipeline, project: pipeline.project) } + let(:sibling) { create(:ci_pipeline, project: pipeline.project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: parent), + source_project: parent.project, + pipeline: pipeline, + project: pipeline.project) + + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: parent), + source_project: parent.project, + pipeline: sibling, + project: sibling.project) + end + + it 'returns parent sibling and self ids' do + expect(same_family_pipeline_ids).to contain_exactly(parent.id, pipeline.id, sibling.id) + end + end + + context 'when pipeline is parent' do + let(:child) { create(:ci_pipeline, project: pipeline.project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: pipeline), + source_project: pipeline.project, + pipeline: child, + project: child.project) + end + + it 'returns self and child ids' do + expect(same_family_pipeline_ids).to contain_exactly(pipeline.id, child.id) + end + end + end + describe '#stuck?' do before do create(:ci_build, :pending, pipeline: pipeline) @@ -2602,6 +2750,28 @@ describe Ci::Pipeline, :mailer do end end + describe '#add_error_message' do + let(:pipeline) { build_stubbed(:ci_pipeline) } + + it 'adds a new pipeline error message' do + pipeline.add_error_message('The error message') + + expect(pipeline.messages.map(&:content)).to contain_exactly('The error message') + end + + context 'when feature flag ci_store_pipeline_messages is disabled' do + before do + stub_feature_flags(ci_store_pipeline_messages: false) + end + + it ' does not add pipeline error message' do + pipeline.add_error_message('The error message') + + expect(pipeline.messages).to be_empty + end + end + end + describe '#has_yaml_errors?' do context 'when yaml_errors is set' do before do @@ -2825,6 +2995,16 @@ describe Ci::Pipeline, :mailer do end end + describe '#batch_lookup_report_artifact_for_file_type' do + context 'with code quality report artifact' do + let(:pipeline) { create(:ci_pipeline, :with_codequality_report, project: project) } + + it "returns the code quality artifact" do + expect(pipeline.batch_lookup_report_artifact_for_file_type(:codequality)).to eq(pipeline.job_artifacts.sample) + end + end + end + describe '#latest_report_builds' do it 'returns build with test artifacts' do test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project) @@ -2891,6 +3071,39 @@ describe Ci::Pipeline, :mailer do end end + describe '#test_report_summary' do + subject { pipeline.test_report_summary } + + context 'when pipeline has multiple builds with report results' do + let(:pipeline) { create(:ci_pipeline, :success, project: project) } + + before do + create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline, project: project) + create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline, project: project) + end + + it 'returns test report summary with collected data', :aggregate_failures do + expect(subject.total_time).to be(0.84) + expect(subject.total_count).to be(4) + expect(subject.success_count).to be(0) + expect(subject.failed_count).to be(0) + expect(subject.error_count).to be(4) + expect(subject.skipped_count).to be(0) + end + end + + context 'when pipeline does not have any builds with report results' do + it 'returns empty test report sumary', :aggregate_failures do + expect(subject.total_time).to be(0) + expect(subject.total_count).to be(0) + expect(subject.success_count).to be(0) + expect(subject.failed_count).to be(0) + expect(subject.error_count).to be(0) + expect(subject.skipped_count).to be(0) + end + end + end + describe '#test_reports' do subject { pipeline.test_reports } @@ -3069,6 +3282,32 @@ describe Ci::Pipeline, :mailer do end end end + + context 'when transitioning to success' do + context 'when feature is enabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: true) + end + + it 'calls the PipelineSuccessUnlockArtifactsWorker' do + expect(Ci::PipelineSuccessUnlockArtifactsWorker).to receive(:perform_async).with(pipeline.id) + + pipeline.succeed! + end + end + + context 'when feature is disabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: false) + end + + it 'does not call the PipelineSuccessUnlockArtifactsWorker' do + expect(Ci::PipelineSuccessUnlockArtifactsWorker).not_to receive(:perform_async) + + pipeline.succeed! + end + end + end end describe '#default_branch?' do @@ -3133,8 +3372,8 @@ describe Ci::Pipeline, :mailer do end end - describe '#error_messages' do - subject { pipeline.error_messages } + describe '#full_error_messages' do + subject { pipeline.full_error_messages } before do pipeline.valid? diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb index e8c7ce088e2..04fcaab4c2d 100644 --- a/spec/models/ci/pipeline_variable_spec.rb +++ b/spec/models/ci/pipeline_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineVariable do +RSpec.describe Ci::PipelineVariable do subject { build(:ci_pipeline_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb index e67f740279b..35764e2bbbe 100644 --- a/spec/models/ci/processable_spec.rb +++ b/spec/models/ci/processable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Processable do +RSpec.describe Ci::Processable do let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb index 3d75cb63141..fd4742a8ad2 100644 --- a/spec/models/ci/ref_spec.rb +++ b/spec/models/ci/ref_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Ref do +RSpec.describe Ci::Ref do it { is_expected.to belong_to(:project) } describe '.ensure_for' do @@ -62,6 +62,35 @@ describe Ci::Ref do end end + describe '#last_finished_pipeline_id' do + let(:pipeline_status) { :running } + let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] } + let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) } + let(:ci_ref) { pipeline.ci_ref } + + context 'when there are no finished pipelines' do + it 'returns nil' do + expect(ci_ref.last_finished_pipeline_id).to be_nil + end + end + + context 'when there are finished pipelines' do + let(:pipeline_status) { :success } + + it 'returns the pipeline id' do + expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id) + end + + context 'when the pipeline is not a ci_source' do + let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] } + + it 'returns nil' do + expect(ci_ref.last_finished_pipeline_id).to be_nil + end + end + end + end + describe '#update_status_by!' do subject { ci_ref.update_status_by!(pipeline) } diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb index ce8b03282bc..9f72d1a82e5 100644 --- a/spec/models/ci/resource_group_spec.rb +++ b/spec/models/ci/resource_group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::ResourceGroup do +RSpec.describe Ci::ResourceGroup do describe 'validation' do it 'valids when key includes allowed character' do resource_group = build(:ci_resource_group, key: 'test') diff --git a/spec/models/ci/resource_spec.rb b/spec/models/ci/resource_spec.rb index 27e512e2c45..90f26ef2b31 100644 --- a/spec/models/ci/resource_spec.rb +++ b/spec/models/ci/resource_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Resource do +RSpec.describe Ci::Resource do describe '.free' do subject { described_class.free } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 296240b1602..8247ebf1144 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Runner do +RSpec.describe Ci::Runner do it_behaves_like 'having unique enum values' describe 'validation' do @@ -713,6 +713,46 @@ describe Ci::Runner do end end + describe '#belongs_to_more_than_one_project?' do + context 'project runner' do + let(:project1) { create(:project) } + let(:project2) { create(:project) } + + context 'two projects assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) } + + it 'returns true' do + expect(runner.belongs_to_more_than_one_project?).to be_truthy + end + end + + context 'one project assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1]) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + end + + context 'group runner' do + let(:group) { create(:group) } + let(:runner) { create(:ci_runner, :group, groups: [group]) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + + context 'shared runner' do + let(:runner) { create(:ci_runner, :instance) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + end + describe '#has_tags?' do context 'when runner has tags' do subject { create(:ci_runner, tag_list: ['tag']) } diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb index 5023747b487..ccf3140650b 100644 --- a/spec/models/ci/sources/pipeline_spec.rb +++ b/spec/models/ci/sources/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Sources::Pipeline do +RSpec.describe Ci::Sources::Pipeline do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:pipeline) } diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index a1549532559..3d873a1b9c1 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Stage, :models do +RSpec.describe Ci::Stage, :models do let_it_be(:pipeline) { create(:ci_empty_pipeline) } let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) } @@ -172,7 +172,7 @@ describe Ci::Stage, :models do it 'raises an exception' do expect { stage.update_legacy_status } - .to raise_error(HasStatus::UnknownStatusError) + .to raise_error(Ci::HasStatus::UnknownStatusError) end end end diff --git a/spec/models/ci/trigger_request_spec.rb b/spec/models/ci/trigger_request_spec.rb index d04349bec92..0d462741089 100644 --- a/spec/models/ci/trigger_request_spec.rb +++ b/spec/models/ci/trigger_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::TriggerRequest do +RSpec.describe Ci::TriggerRequest do describe 'validation' do it 'be invalid if saving a variable' do trigger = build(:ci_trigger_request, variables: { TRIGGER_KEY_1: 'TRIGGER_VALUE_1' } ) diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 5b0815f8156..4ba6c6e50f7 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Trigger do +RSpec.describe Ci::Trigger do let(:project) { create :project } describe 'associations' do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 810a0ddfd2e..26a7a2596af 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Variable do +RSpec.describe Ci::Variable do subject { build(:ci_variable) } it_behaves_like "CI variable" diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb index d7fd0d06b05..7ca7f533a27 100644 --- a/spec/models/clusters/applications/cert_manager_spec.rb +++ b/spec/models/clusters/applications/cert_manager_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::CertManager do +RSpec.describe Clusters::Applications::CertManager do let(:cert_manager) { create(:clusters_applications_cert_manager) } include_examples 'cluster application core specs', :clusters_applications_cert_manager diff --git a/spec/models/clusters/applications/cilium_spec.rb b/spec/models/clusters/applications/cilium_spec.rb new file mode 100644 index 00000000000..8b01502d5c0 --- /dev/null +++ b/spec/models/clusters/applications/cilium_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Clusters::Applications::Cilium do + let(:cilium) { create(:clusters_applications_cilium) } + + include_examples 'cluster application core specs', :clusters_applications_cilium + include_examples 'cluster application status specs', :clusters_applications_cilium + include_examples 'cluster application initial status specs' + + describe '#allowed_to_uninstall?' do + subject { cilium.allowed_to_uninstall? } + + it { is_expected.to be false } + end +end diff --git a/spec/models/clusters/applications/crossplane_spec.rb b/spec/models/clusters/applications/crossplane_spec.rb index ebc675497f4..a41c5f6586b 100644 --- a/spec/models/clusters/applications/crossplane_spec.rb +++ b/spec/models/clusters/applications/crossplane_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Crossplane do +RSpec.describe Clusters::Applications::Crossplane do let(:crossplane) { create(:clusters_applications_crossplane) } include_examples 'cluster application core specs', :clusters_applications_crossplane diff --git a/spec/models/clusters/applications/elastic_stack_spec.rb b/spec/models/clusters/applications/elastic_stack_spec.rb index 50042a4e29a..62123ffa542 100644 --- a/spec/models/clusters/applications/elastic_stack_spec.rb +++ b/spec/models/clusters/applications/elastic_stack_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::ElasticStack do +RSpec.describe Clusters::Applications::ElasticStack do include KubernetesHelpers include_examples 'cluster application core specs', :clusters_applications_elastic_stack @@ -27,6 +27,20 @@ describe Clusters::Applications::ElasticStack do expect(subject.preinstall).to be_empty end + context 'within values.yaml' do + let(:values_yaml_content) {subject.files[:"values.yaml"]} + + it 'contains the disabled index lifecycle management' do + expect(values_yaml_content).to include "setup.ilm.enabled: false" + end + + it 'contains daily indices with respective template' do + expect(values_yaml_content).to include "index: \"filebeat-%{[agent.version]}-%{+yyyy.MM.dd}\"" + expect(values_yaml_content).to include "setup.template.name: 'filebeat'" + expect(values_yaml_content).to include "setup.template.pattern: 'filebeat-*'" + end + end + context 'on a non rbac enabled cluster' do before do elastic_stack.cluster.platform_kubernetes.abac! diff --git a/spec/models/clusters/applications/fluentd_spec.rb b/spec/models/clusters/applications/fluentd_spec.rb index 4e9548990ed..be7b4a87947 100644 --- a/spec/models/clusters/applications/fluentd_spec.rb +++ b/spec/models/clusters/applications/fluentd_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Fluentd do +RSpec.describe Clusters::Applications::Fluentd do let(:waf_log_enabled) { true } let(:cilium_log_enabled) { true } let(:fluentd) { create(:clusters_applications_fluentd, waf_log_enabled: waf_log_enabled, cilium_log_enabled: cilium_log_enabled) } diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb index 87454e1d3e2..6d2ecaa6d47 100644 --- a/spec/models/clusters/applications/helm_spec.rb +++ b/spec/models/clusters/applications/helm_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Helm do +RSpec.describe Clusters::Applications::Helm do include_examples 'cluster application core specs', :clusters_applications_helm describe '.available' do diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index 8aee4eec0d3..d1138f5fa2d 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Ingress do +RSpec.describe Clusters::Applications::Ingress do let(:ingress) { create(:clusters_applications_ingress) } it_behaves_like 'having unique enum values' diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index 937db9217f3..3cf24f1a9ef 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Jupyter do +RSpec.describe Clusters::Applications::Jupyter do include_examples 'cluster application core specs', :clusters_applications_jupyter include_examples 'cluster application status specs', :clusters_applications_jupyter include_examples 'cluster application version specs', :clusters_applications_jupyter diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 7ff7644e703..b14161ce8e6 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Knative do +RSpec.describe Clusters::Applications::Knative do let(:knative) { create(:clusters_applications_knative) } include_examples 'cluster application core specs', :clusters_applications_knative diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index 1ed9e207b6b..1215b38a9a2 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Prometheus do +RSpec.describe Clusters::Applications::Prometheus do include KubernetesHelpers include StubRequests diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 6ee6711ec4b..fbabfd25b2f 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Applications::Runner do +RSpec.describe Clusters::Applications::Runner do let(:ci_runner) { create(:ci_runner) } include_examples 'cluster application core specs', :clusters_applications_runner diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 4dd74976028..4807957152c 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Cluster, :use_clean_rails_memory_store_caching do +RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers include KubernetesHelpers @@ -10,6 +10,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do subject { build(:cluster) } + it { is_expected.to include_module(HasEnvironmentScope) } it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:management_project).class_name('::Project') } it { is_expected.to have_many(:cluster_projects) } @@ -289,6 +290,79 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do describe 'validations' do subject { cluster.valid? } + context 'when validates unique_environment_scope' do + context 'for a project cluster' do + let(:project) { create(:project) } + + before do + create(:cluster, projects: [project], environment_scope: 'product/*') + end + + context 'when identical environment scope exists in project' do + let(:cluster) { build(:cluster, projects: [project], environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'when identical environment scope does not exist in project' do + let(:cluster) { build(:cluster, projects: [project], environment_scope: '*') } + + it { is_expected.to be_truthy } + end + + context 'when identical environment scope exists in different project' do + let(:project2) { create(:project) } + let(:cluster) { build(:cluster, projects: [project2], environment_scope: 'product/*') } + + it { is_expected.to be_truthy } + end + end + + context 'for a group cluster' do + let(:group) { create(:group) } + + before do + create(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') + end + + context 'when identical environment scope exists in group' do + let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'when identical environment scope does not exist in group' do + let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: '*') } + + it { is_expected.to be_truthy } + end + + context 'when identical environment scope exists in different group' do + let(:cluster) { build(:cluster, :group, environment_scope: 'product/*') } + + it { is_expected.to be_truthy } + end + end + + context 'for an instance cluster' do + before do + create(:cluster, :instance, environment_scope: 'product/*') + end + + context 'identical environment scope exists' do + let(:cluster) { build(:cluster, :instance, environment_scope: 'product/*') } + + it { is_expected.to be_falsey } + end + + context 'identical environment scope does not exist' do + let(:cluster) { build(:cluster, :instance, environment_scope: '*') } + + it { is_expected.to be_truthy } + end + end + end + context 'when validates name' do context 'when provided by user' do let!(:cluster) { build(:cluster, :provided_by_user, name: name) } @@ -1111,13 +1185,23 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do context 'cluster is enabled' do let(:cluster) { create(:cluster, :provided_by_user, :group) } + let(:gl_k8s_node_double) { double(Gitlab::Kubernetes::Node) } + let(:expected_nodes) { nil } before do - stub_kubeclient_nodes_and_nodes_metrics(cluster.platform.api_url) + stub_kubeclient_discover(cluster.platform.api_url) + allow(Gitlab::Kubernetes::Node).to receive(:new).with(cluster).and_return(gl_k8s_node_double) + allow(gl_k8s_node_double).to receive(:all).and_return([]) end context 'connection to the cluster is successful' do - it { is_expected.to eq(connection_status: :connected, nodes: [kube_node.merge(kube_node_metrics)]) } + before do + allow(gl_k8s_node_double).to receive(:all).and_return(expected_nodes) + end + + let(:expected_nodes) { [kube_node.merge(kube_node_metrics)] } + + it { is_expected.to eq(connection_status: :connected, nodes: expected_nodes) } end context 'cluster cannot be reached' do @@ -1126,7 +1210,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do .and_raise(SocketError) end - it { is_expected.to eq(connection_status: :unreachable, nodes: nil) } + it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) } end context 'cluster cannot be authenticated to' do @@ -1135,7 +1219,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do .and_raise(OpenSSL::X509::CertificateError.new("Certificate error")) end - it { is_expected.to eq(connection_status: :authentication_failure, nodes: nil) } + it { is_expected.to eq(connection_status: :authentication_failure, nodes: expected_nodes) } end describe 'Kubeclient::HttpError' do @@ -1147,18 +1231,18 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do .and_raise(Kubeclient::HttpError.new(error_code, error_message, nil)) end - it { is_expected.to eq(connection_status: :authentication_failure, nodes: nil) } + it { is_expected.to eq(connection_status: :authentication_failure, nodes: expected_nodes) } context 'generic timeout' do let(:error_message) { 'Timed out connecting to server'} - it { is_expected.to eq(connection_status: :unreachable, nodes: nil) } + it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) } end context 'gateway timeout' do let(:error_message) { '504 Gateway Timeout for GET https://kubernetes.example.com/api/v1'} - it { is_expected.to eq(connection_status: :unreachable, nodes: nil) } + it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) } end end @@ -1168,12 +1252,12 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do .and_raise(StandardError) end - it { is_expected.to eq(connection_status: :unknown_failure, nodes: nil) } + it { is_expected.to eq(connection_status: :unknown_failure, nodes: expected_nodes) } it 'notifies Sentry' do expect(Gitlab::ErrorTracking).to receive(:track_exception) .with(instance_of(StandardError), hash_including(cluster_id: cluster.id)) - .twice + .once subject end diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb index 1957e1fc5ee..5ac561eb2d0 100644 --- a/spec/models/clusters/clusters_hierarchy_spec.rb +++ b/spec/models/clusters/clusters_hierarchy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::ClustersHierarchy do +RSpec.describe Clusters::ClustersHierarchy do describe '#base_and_ancestors' do def base_and_ancestors(clusterable, include_management_project: true) described_class.new(clusterable, include_management_project: include_management_project).base_and_ancestors diff --git a/spec/models/clusters/group_spec.rb b/spec/models/clusters/group_spec.rb index ba145342cb8..3b541c40938 100644 --- a/spec/models/clusters/group_spec.rb +++ b/spec/models/clusters/group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Group do +RSpec.describe Clusters::Group do it { is_expected.to belong_to(:cluster) } it { is_expected.to belong_to(:group) } end diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index f0e6dd53664..adccc72d13d 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Platforms::Kubernetes do +RSpec.describe Clusters::Platforms::Kubernetes do include KubernetesHelpers it { is_expected.to belong_to(:cluster) } @@ -204,6 +204,52 @@ describe Clusters::Platforms::Kubernetes do end it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) } + + context 'ca_pem is a single certificate' do + let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem')) } + let(:kubernetes) do + build(:cluster_platform_kubernetes, + :configured, + namespace: 'a-namespace', + cluster: cluster, + ca_pem: ca_pem) + end + + it 'adds it to cert_store' do + cert = OpenSSL::X509::Certificate.new(ca_pem) + cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store] + + expect(cert_store.verify(cert)).to be true + end + end + + context 'ca_pem is a chain' do + let(:cert_chain) { File.read(Rails.root.join('spec/fixtures/clusters/chain_certificates.pem')) } + let(:kubernetes) do + build(:cluster_platform_kubernetes, + :configured, + namespace: 'a-namespace', + cluster: cluster, + ca_pem: cert_chain) + end + + it 'includes chain of certificates' do + cert1_file = File.read(Rails.root.join('spec/fixtures/clusters/root_certificate.pem')) + cert1 = OpenSSL::X509::Certificate.new(cert1_file) + + cert2_file = File.read(Rails.root.join('spec/fixtures/clusters/intermediate_certificate.pem')) + cert2 = OpenSSL::X509::Certificate.new(cert2_file) + + cert3_file = File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem')) + cert3 = OpenSSL::X509::Certificate.new(cert3_file) + + cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store] + + expect(cert_store.verify(cert1)).to be true + expect(cert_store.verify(cert2)).to be true + expect(cert_store.verify(cert3)).to be true + end + end end describe '#rbac?' do diff --git a/spec/models/clusters/project_spec.rb b/spec/models/clusters/project_spec.rb index 671af085d10..e16dfa47898 100644 --- a/spec/models/clusters/project_spec.rb +++ b/spec/models/clusters/project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Project do +RSpec.describe Clusters::Project do it { is_expected.to belong_to(:cluster) } it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:kubernetes_namespaces) } diff --git a/spec/models/clusters/providers/aws_spec.rb b/spec/models/clusters/providers/aws_spec.rb index 05d6e63288e..3b4a48cc5be 100644 --- a/spec/models/clusters/providers/aws_spec.rb +++ b/spec/models/clusters/providers/aws_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Providers::Aws do +RSpec.describe Clusters::Providers::Aws do it { is_expected.to belong_to(:cluster) } it { is_expected.to validate_length_of(:key_name).is_at_least(1).is_at_most(255) } diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb index e2fd777d131..ad9ada04875 100644 --- a/spec/models/clusters/providers/gcp_spec.rb +++ b/spec/models/clusters/providers/gcp_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Clusters::Providers::Gcp do +RSpec.describe Clusters::Providers::Gcp do it { is_expected.to belong_to(:cluster) } it { is_expected.to validate_presence_of(:zone) } diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb index d49b71db5f8..f4e86f3292b 100644 --- a/spec/models/commit_collection_spec.rb +++ b/spec/models/commit_collection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CommitCollection do +RSpec.describe CommitCollection do let(:project) { create(:project, :repository) } let(:commit) { project.commit("c1c67abbaf91f624347bb3ae96eabe3a1b742478") } @@ -75,6 +75,18 @@ describe CommitCollection do end end + describe '#with_markdown_cache' do + let(:commits) { [commit] } + let(:collection) { described_class.new(project, commits) } + + it 'preloads commits cache markdown' do + aggregate_failures do + expect(Commit).to receive(:preload_markdown_cache!).with(commits) + expect(collection.with_markdown_cache).to eq(collection) + end + end + end + describe 'enrichment methods' do let(:gitaly_commit) { commit } let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) } diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 245e47fa17b..3fb8708c884 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CommitRange do +RSpec.describe CommitRange do describe 'modules' do subject { described_class } diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index ddda04faaf1..cfa87b3e39e 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Commit do +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) } @@ -675,7 +675,10 @@ eos end describe '#work_in_progress?' do - ['squash! ', 'fixup! ', 'wip: ', 'WIP: ', '[WIP] '].each do |wip_prefix| + [ + 'squash! ', 'fixup! ', 'wip: ', 'WIP: ', '[WIP] ', + 'draft: ', 'Draft - ', '[Draft] ', '(draft) ', 'Draft: ' + ].each do |wip_prefix| it "detects the '#{wip_prefix}' prefix" do commit.message = "#{wip_prefix}#{commit.message}" @@ -689,6 +692,12 @@ eos expect(commit).to be_work_in_progress end + it "detects WIP for a commit just saying 'draft'" do + commit.message = "draft" + + expect(commit).to be_work_in_progress + end + it "doesn't detect WIP for a commit that begins with 'FIXUP! '" do commit.message = "FIXUP! #{commit.message}" diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 85fc503a1ca..cd0110a787b 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CommitStatus do +RSpec.describe CommitStatus do let_it_be(:project) { create(:project, :repository) } let_it_be(:pipeline) do diff --git a/spec/models/commit_with_pipeline_spec.rb b/spec/models/commit_with_pipeline_spec.rb index e0bb29fec7b..ff451527929 100644 --- a/spec/models/commit_with_pipeline_spec.rb +++ b/spec/models/commit_with_pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CommitWithPipeline do +RSpec.describe CommitWithPipeline do let(:project) { create(:project, :public, :repository) } let(:commit) { described_class.new(project.commit) } diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb index 43c3580bed2..d395aa359e5 100644 --- a/spec/models/compare_spec.rb +++ b/spec/models/compare_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Compare do +RSpec.describe Compare do include RepoHelpers let(:project) { create(:project, :public, :repository) } diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb index 5c1694e3737..24eb3e8a1e6 100644 --- a/spec/models/concerns/access_requestable_spec.rb +++ b/spec/models/concerns/access_requestable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AccessRequestable do +RSpec.describe AccessRequestable do describe 'Group' do describe '#request_access' do let(:group) { create(:group, :public) } diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb new file mode 100644 index 00000000000..8fda8bccf09 --- /dev/null +++ b/spec/models/concerns/approvable_base_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ApprovableBase do + describe '#approved_by?' do + let(:merge_request) { create(:merge_request) } + let(:user) { create(:user) } + + subject { merge_request.approved_by?(user) } + + context 'when a user has not approved' do + it 'returns false' do + is_expected.to be_falsy + end + end + + context 'when a user has approved' do + let!(:approval) { create(:approval, merge_request: merge_request, user: user) } + + it 'returns false' do + is_expected.to be_truthy + end + end + + context 'when a user is nil' do + let(:user) { nil } + + it 'returns false' do + is_expected.to be_falsy + end + end + end +end diff --git a/spec/models/concerns/atomic_internal_id_spec.rb b/spec/models/concerns/atomic_internal_id_spec.rb index 93bf7ec10dd..8c3537f1dcc 100644 --- a/spec/models/concerns/atomic_internal_id_spec.rb +++ b/spec/models/concerns/atomic_internal_id_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AtomicInternalId do +RSpec.describe AtomicInternalId do let(:milestone) { build(:milestone) } let(:iid) { double('iid', to_i: 42) } let(:external_iid) { 100 } diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb index 96e867dbc97..8a8eeea39dc 100644 --- a/spec/models/concerns/avatarable_spec.rb +++ b/spec/models/concerns/avatarable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Avatarable do +RSpec.describe Avatarable do let(:project) { create(:project, :with_avatar) } let(:gitlab_host) { "https://gitlab.example.com" } diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb index 29f911fcb04..b5b3772ecb6 100644 --- a/spec/models/concerns/awardable_spec.rb +++ b/spec/models/concerns/awardable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Awardable do +RSpec.describe Awardable do let!(:issue) { create(:issue) } let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) } diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb index d2373926802..a8fcb714c64 100644 --- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb +++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BatchDestroyDependentAssociations do +RSpec.describe BatchDestroyDependentAssociations do class TestProject < ActiveRecord::Base self.table_name = 'projects' diff --git a/spec/models/concerns/blob_language_from_git_attributes_spec.rb b/spec/models/concerns/blob_language_from_git_attributes_spec.rb index 4cb8f042b1d..c07ee15e841 100644 --- a/spec/models/concerns/blob_language_from_git_attributes_spec.rb +++ b/spec/models/concerns/blob_language_from_git_attributes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlobLanguageFromGitAttributes do +RSpec.describe BlobLanguageFromGitAttributes do include FakeBlobHelpers let(:project) { build(:project, :repository) } diff --git a/spec/models/concerns/blocks_json_serialization_spec.rb b/spec/models/concerns/blocks_json_serialization_spec.rb index 32870461019..d811b47fa35 100644 --- a/spec/models/concerns/blocks_json_serialization_spec.rb +++ b/spec/models/concerns/blocks_json_serialization_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BlocksJsonSerialization do +RSpec.describe BlocksJsonSerialization do before do stub_const('DummyModel', Class.new) DummyModel.class_eval do diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb index 07d6cee487f..82b0c00b396 100644 --- a/spec/models/concerns/bulk_insert_safe_spec.rb +++ b/spec/models/concerns/bulk_insert_safe_spec.rb @@ -2,57 +2,7 @@ require 'spec_helper' -describe BulkInsertSafe do - class BulkInsertItem < ActiveRecord::Base - include BulkInsertSafe - include ShaAttribute - - validates :name, :enum_value, :secret_value, :sha_value, :jsonb_value, presence: true - - ENUM_VALUES = { - case_1: 1 - }.freeze - - sha_attribute :sha_value - - enum enum_value: ENUM_VALUES - - attr_encrypted :secret_value, - mode: :per_attribute_iv, - algorithm: 'aes-256-gcm', - key: Settings.attr_encrypted_db_key_base_32, - insecure_mode: false - - default_value_for :enum_value, 'case_1' - default_value_for :secret_value, 'my-secret' - default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' - default_value_for :jsonb_value, { "key" => "value" } - - def self.valid_list(count) - Array.new(count) { |n| new(name: "item-#{n}") } - end - - def self.invalid_list(count) - Array.new(count) { new } - end - end - - module InheritedUnsafeMethods - extend ActiveSupport::Concern - - included do - after_save -> { "unsafe" } - end - end - - module InheritedSafeMethods - extend ActiveSupport::Concern - - included do - after_initialize -> { "safe" } - end - end - +RSpec.describe BulkInsertSafe do before(:all) do ActiveRecord::Schema.define do create_table :bulk_insert_items, force: true do |t| @@ -66,100 +16,155 @@ describe BulkInsertSafe do t.index :name, unique: true end end - - BulkInsertItem.reset_column_information end after(:all) do ActiveRecord::Schema.define do drop_table :bulk_insert_items, force: true end + end + + let_it_be(:bulk_insert_item_class) do + Class.new(ActiveRecord::Base) do + self.table_name = 'bulk_insert_items' - BulkInsertItem.reset_column_information + include BulkInsertSafe + include ShaAttribute + + validates :name, :enum_value, :secret_value, :sha_value, :jsonb_value, presence: true + + sha_attribute :sha_value + + enum enum_value: { case_1: 1 } + + attr_encrypted :secret_value, + mode: :per_attribute_iv, + algorithm: 'aes-256-gcm', + key: Settings.attr_encrypted_db_key_base_32, + insecure_mode: false + + default_value_for :enum_value, 'case_1' + default_value_for :secret_value, 'my-secret' + default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' + default_value_for :jsonb_value, { "key" => "value" } + + def self.name + 'BulkInsertItem' + end + + def self.valid_list(count) + Array.new(count) { |n| new(name: "item-#{n}") } + end + + def self.invalid_list(count) + Array.new(count) { new } + end + end end - describe BulkInsertItem do - it_behaves_like 'a BulkInsertSafe model', described_class do - let(:valid_items_for_bulk_insertion) { described_class.valid_list(10) } - let(:invalid_items_for_bulk_insertion) { described_class.invalid_list(10) } + describe 'BulkInsertItem' do + it_behaves_like 'a BulkInsertSafe model' do + let(:target_class) { bulk_insert_item_class.dup } + let(:valid_items_for_bulk_insertion) { target_class.valid_list(10) } + let(:invalid_items_for_bulk_insertion) { target_class.invalid_list(10) } end context 'when inheriting class methods' do + let(:inherited_unsafe_methods_module) do + Module.new do + extend ActiveSupport::Concern + + included do + after_save -> { "unsafe" } + end + end + end + + let(:inherited_safe_methods_module) do + Module.new do + extend ActiveSupport::Concern + + included do + after_initialize -> { "safe" } + end + end + end + it 'raises an error when method is not bulk-insert safe' do - expect { described_class.include(InheritedUnsafeMethods) } - .to raise_error(described_class::MethodNotAllowedError) + expect { bulk_insert_item_class.include(inherited_unsafe_methods_module) } + .to raise_error(bulk_insert_item_class::MethodNotAllowedError) end it 'does not raise an error when method is bulk-insert safe' do - expect { described_class.include(InheritedSafeMethods) }.not_to raise_error + expect { bulk_insert_item_class.include(inherited_safe_methods_module) }.not_to raise_error end end context 'primary keys' do it 'raises error if primary keys are set prior to insertion' do - item = described_class.new(name: 'valid', id: 10) + item = bulk_insert_item_class.new(name: 'valid', id: 10) - expect { described_class.bulk_insert!([item]) } - .to raise_error(described_class::PrimaryKeySetError) + expect { bulk_insert_item_class.bulk_insert!([item]) } + .to raise_error(bulk_insert_item_class::PrimaryKeySetError) end end describe '.bulk_insert!' do it 'inserts items in the given number of batches' do - items = described_class.valid_list(10) + items = bulk_insert_item_class.valid_list(10) expect(ActiveRecord::InsertAll).to receive(:new).twice.and_call_original - described_class.bulk_insert!(items, batch_size: 5) + bulk_insert_item_class.bulk_insert!(items, batch_size: 5) end it 'items can be properly fetched from database' do - items = described_class.valid_list(10) + items = bulk_insert_item_class.valid_list(10) - described_class.bulk_insert!(items) + bulk_insert_item_class.bulk_insert!(items) - attribute_names = described_class.attribute_names - %w[id created_at updated_at] - expect(described_class.last(items.size).pluck(*attribute_names)).to eq( + attribute_names = bulk_insert_item_class.attribute_names - %w[id created_at updated_at] + expect(bulk_insert_item_class.last(items.size).pluck(*attribute_names)).to eq( items.pluck(*attribute_names)) end it 'rolls back the transaction when any item is invalid' do # second batch is bad - all_items = described_class.valid_list(10) + - described_class.invalid_list(10) + all_items = bulk_insert_item_class.valid_list(10) + + bulk_insert_item_class.invalid_list(10) expect do - described_class.bulk_insert!(all_items, batch_size: 2) rescue nil - end.not_to change { described_class.count } + bulk_insert_item_class.bulk_insert!(all_items, batch_size: 2) rescue nil + end.not_to change { bulk_insert_item_class.count } end it 'does nothing and returns an empty array when items are empty' do - expect(described_class.bulk_insert!([])).to eq([]) - expect(described_class.count).to eq(0) + expect(bulk_insert_item_class.bulk_insert!([])).to eq([]) + expect(bulk_insert_item_class.count).to eq(0) end context 'with returns option set' do context 'when is set to :ids' do it 'return an array with the primary key values for all inserted records' do - items = described_class.valid_list(1) + items = bulk_insert_item_class.valid_list(1) - expect(described_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer)) + expect(bulk_insert_item_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer)) end end context 'when is set to nil' do it 'returns an empty array' do - items = described_class.valid_list(1) + items = bulk_insert_item_class.valid_list(1) - expect(described_class.bulk_insert!(items, returns: nil)).to eq([]) + expect(bulk_insert_item_class.bulk_insert!(items, returns: nil)).to eq([]) end end context 'when is set to anything else' do it 'raises an error' do - items = described_class.valid_list(1) + items = bulk_insert_item_class.valid_list(1) - expect { described_class.bulk_insert!([items], returns: [:id, :name]) } + expect { bulk_insert_item_class.bulk_insert!([items], returns: [:id, :name]) } .to raise_error(ArgumentError, "returns needs to be :ids or nil") end end @@ -167,20 +172,20 @@ describe BulkInsertSafe do end context 'when duplicate items are to be inserted' do - let!(:existing_object) { described_class.create!(name: 'duplicate', secret_value: 'old value') } - let(:new_object) { described_class.new(name: 'duplicate', secret_value: 'new value') } + let!(:existing_object) { bulk_insert_item_class.create!(name: 'duplicate', secret_value: 'old value') } + let(:new_object) { bulk_insert_item_class.new(name: 'duplicate', secret_value: 'new value') } describe '.bulk_insert!' do context 'when skip_duplicates is set to false' do it 'raises an exception' do - expect { described_class.bulk_insert!([new_object], skip_duplicates: false) } + expect { bulk_insert_item_class.bulk_insert!([new_object], skip_duplicates: false) } .to raise_error(ActiveRecord::RecordNotUnique) end end context 'when skip_duplicates is set to true' do it 'does not update existing object' do - described_class.bulk_insert!([new_object], skip_duplicates: true) + bulk_insert_item_class.bulk_insert!([new_object], skip_duplicates: true) expect(existing_object.reload.secret_value).to eq('old value') end @@ -189,7 +194,7 @@ describe BulkInsertSafe do describe '.bulk_upsert!' do it 'updates existing object' do - described_class.bulk_upsert!([new_object], unique_by: %w[name]) + bulk_insert_item_class.bulk_upsert!([new_object], unique_by: %w[name]) expect(existing_object.reload.secret_value).to eq('new value') end diff --git a/spec/models/concerns/bulk_insertable_associations_spec.rb b/spec/models/concerns/bulk_insertable_associations_spec.rb index 6359b2c57ef..5a40639e493 100644 --- a/spec/models/concerns/bulk_insertable_associations_spec.rb +++ b/spec/models/concerns/bulk_insertable_associations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BulkInsertableAssociations do +RSpec.describe BulkInsertableAssociations do class BulkFoo < ApplicationRecord include BulkInsertSafe diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index c46ebcf324c..5f8c65e429e 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CacheMarkdownField, :clean_gitlab_redis_cache do +RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do let(:ar_class) do Class.new(ActiveRecord::Base) do self.table_name = 'issues' diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb index 6694b2aba22..f2877bed9cf 100644 --- a/spec/models/concerns/cacheable_attributes_spec.rb +++ b/spec/models/concerns/cacheable_attributes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CacheableAttributes do +RSpec.describe CacheableAttributes do let(:minimal_test_class) do Class.new do include ActiveModel::Model diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb index 9819f656f0d..521b47c63fd 100644 --- a/spec/models/concerns/case_sensitivity_spec.rb +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CaseSensitivity do +RSpec.describe CaseSensitivity do describe '.iwhere' do let(:connection) { ActiveRecord::Base.connection } let(:model) do diff --git a/spec/models/concerns/checksummable_spec.rb b/spec/models/concerns/checksummable_spec.rb index 017077bd297..b469b2e5c18 100644 --- a/spec/models/concerns/checksummable_spec.rb +++ b/spec/models/concerns/checksummable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Checksummable do +RSpec.describe Checksummable do describe ".hexdigest" do let(:fake_class) do Class.new do diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb index e41d75568f7..e6dbf403b63 100644 --- a/spec/models/concerns/chronic_duration_attribute_spec.rb +++ b/spec/models/concerns/chronic_duration_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -shared_examples 'ChronicDurationAttribute reader' do +RSpec.shared_examples 'ChronicDurationAttribute reader' do it 'contains dynamically created reader method' do expect(subject.class).to be_public_method_defined(virtual_field) end @@ -22,7 +22,7 @@ shared_examples 'ChronicDurationAttribute reader' do end end -shared_examples 'ChronicDurationAttribute writer' do +RSpec.shared_examples 'ChronicDurationAttribute writer' do it 'contains dynamically created writer method' do expect(subject.class).to be_public_method_defined("#{virtual_field}=") end @@ -94,7 +94,7 @@ shared_examples 'ChronicDurationAttribute writer' do end end -describe 'ChronicDurationAttribute' do +RSpec.describe 'ChronicDurationAttribute' do context 'when default value is not set' do let(:source_field) {:maximum_timeout} let(:virtual_field) {:maximum_timeout_human_readable} @@ -118,7 +118,7 @@ describe 'ChronicDurationAttribute' do end end -describe 'ChronicDurationAttribute - reader' do +RSpec.describe 'ChronicDurationAttribute - reader' do let(:source_field) {:timeout} let(:virtual_field) {:timeout_human_readable} diff --git a/spec/models/concerns/ci/has_ref_spec.rb b/spec/models/concerns/ci/has_ref_spec.rb index b98f915018b..69f2fdb21e1 100644 --- a/spec/models/concerns/ci/has_ref_spec.rb +++ b/spec/models/concerns/ci/has_ref_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::HasRef do +RSpec.describe Ci::HasRef do describe '#branch?' do let(:build) { create(:ci_build) } diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/ci/has_status_spec.rb index 68047f24ec3..fe46b63781d 100644 --- a/spec/models/concerns/has_status_spec.rb +++ b/spec/models/concerns/ci/has_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe HasStatus do +RSpec.describe Ci::HasStatus do describe '.slow_composite_status' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/concerns/ci/has_variable_spec.rb b/spec/models/concerns/ci/has_variable_spec.rb index c132fe47c3c..b5390281064 100644 --- a/spec/models/concerns/ci/has_variable_spec.rb +++ b/spec/models/concerns/ci/has_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::HasVariable do +RSpec.describe Ci::HasVariable do subject { build(:ci_variable) } it { is_expected.to validate_presence_of(:key) } diff --git a/spec/models/concerns/ci/maskable_spec.rb b/spec/models/concerns/ci/maskable_spec.rb index 01861b39165..840a08b6060 100644 --- a/spec/models/concerns/ci/maskable_spec.rb +++ b/spec/models/concerns/ci/maskable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Maskable do +RSpec.describe Ci::Maskable do let(:variable) { build(:ci_variable) } describe 'masked value validations' do diff --git a/spec/models/concerns/delete_with_limit_spec.rb b/spec/models/concerns/delete_with_limit_spec.rb index 52085f970f3..0259a1ea4fb 100644 --- a/spec/models/concerns/delete_with_limit_spec.rb +++ b/spec/models/concerns/delete_with_limit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeleteWithLimit do +RSpec.describe DeleteWithLimit do describe '.delete_with_limit' do it 'deletes a limited amount of rows' do create_list(:web_hook_log, 4) diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb index 9164c3a75c5..2bb6aa27e21 100644 --- a/spec/models/concerns/deployment_platform_spec.rb +++ b/spec/models/concerns/deployment_platform_spec.rb @@ -2,12 +2,247 @@ require 'spec_helper' -describe DeploymentPlatform do +RSpec.describe DeploymentPlatform do let(:project) { create(:project) } describe '#deployment_platform' do subject { project.deployment_platform } + context 'multiple clusters' do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + + shared_examples 'matching environment scope' do + it 'returns environment specific cluster' do + is_expected.to eq(cluster.platform_kubernetes) + end + end + + shared_examples 'not matching environment scope' do + it 'returns default cluster' do + is_expected.to eq(default_cluster.platform_kubernetes) + end + end + + context 'multiple clusters use the same management project' do + let(:management_project) { create(:project, group: group) } + + let!(:default_cluster) do + create(:cluster_for_group, groups: [group], environment_scope: '*', management_project: management_project) + end + + let!(:cluster) do + create(:cluster_for_group, groups: [group], environment_scope: 'review/*', management_project: management_project) + end + + let(:environment) { 'review/name' } + + subject { management_project.deployment_platform(environment: environment) } + + it_behaves_like 'matching environment scope' + end + + context 'when project does not have a cluster but has group clusters' do + let!(:default_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, groups: [group], environment_scope: '*') + end + + let!(:cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: 'review/*', groups: [group]) + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + + context 'when group belongs to a parent group' do + let(:parent_group) { create(:group) } + let(:group) { create(:group, parent: parent_group) } + + context 'when parent_group has a cluster with default scope' do + let!(:parent_group_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: '*', groups: [parent_group]) + end + + it_behaves_like 'matching environment scope' + end + + context 'when parent_group has a cluster that is an exact match' do + let!(:parent_group_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, environment_scope: 'review/name', groups: [parent_group]) + end + + it_behaves_like 'matching environment scope' + end + end + end + + context 'with instance clusters' do + let!(:default_cluster) do + create(:cluster, :provided_by_user, :instance, environment_scope: '*') + end + + let!(:cluster) do + create(:cluster, :provided_by_user, :instance, environment_scope: 'review/*') + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + end + + context 'when environment is specified' do + let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') } + let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) } + + let!(:group_default_cluster) do + create(:cluster, :provided_by_user, + cluster_type: :group_type, groups: [group], environment_scope: '*') + end + + let(:environment) { 'review/name' } + + subject { project.deployment_platform(environment: environment) } + + context 'when environment scope is exactly matched' do + before do + cluster.update!(environment_scope: 'review/name') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope is matched by wildcard' do + before do + cluster.update!(environment_scope: 'review/*') + end + + it_behaves_like 'matching environment scope' + end + + context 'when environment scope does not match' do + before do + cluster.update!(environment_scope: 'review/*/special') + end + + it_behaves_like 'not matching environment scope' + end + + context 'when environment scope has _' do + it 'does not treat it as wildcard' do + cluster.update!(environment_scope: 'foo_bar/*') + + is_expected.to eq(default_cluster.platform_kubernetes) + end + + context 'when environment name contains an underscore' do + let(:environment) { 'foo_bar/test' } + + it 'matches literally for _' do + cluster.update!(environment_scope: 'foo_bar/*') + + is_expected.to eq(cluster.platform_kubernetes) + end + end + end + + # The environment name and scope cannot have % at the moment, + # but we're considering relaxing it and we should also make sure + # it doesn't break in case some data sneaked in somehow as we're + # not checking this integrity in database level. + context 'when environment scope has %' do + it 'does not treat it as wildcard' do + cluster.update_attribute(:environment_scope, '*%*') + + is_expected.to eq(default_cluster.platform_kubernetes) + end + + context 'when environment name contains a percent char' do + let(:environment) { 'foo%bar/test' } + + it 'matches literally for %' do + cluster.update_attribute(:environment_scope, 'foo%bar/*') + + is_expected.to eq(cluster.platform_kubernetes) + end + end + end + + context 'when perfectly matched cluster exists' do + let!(:perfectly_matched_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'review/name') } + + it 'returns perfectly matched cluster as highest precedence' do + is_expected.to eq(perfectly_matched_cluster.platform_kubernetes) + end + end + end + + context 'with multiple clusters and multiple environments' do + let!(:cluster_1) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'staging/*') } + let!(:cluster_2) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'test/*') } + + let(:environment_1) { 'staging/name' } + let(:environment_2) { 'test/name' } + + it 'returns the appropriate cluster' do + expect(project.deployment_platform(environment: environment_1)).to eq(cluster_1.platform_kubernetes) + expect(project.deployment_platform(environment: environment_2)).to eq(cluster_2.platform_kubernetes) + end + end + end + context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do it { is_expected.to be_nil } end diff --git a/spec/models/concerns/deprecated_assignee_spec.rb b/spec/models/concerns/deprecated_assignee_spec.rb index e394de0aa34..630d9ea601f 100644 --- a/spec/models/concerns/deprecated_assignee_spec.rb +++ b/spec/models/concerns/deprecated_assignee_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeprecatedAssignee do +RSpec.describe DeprecatedAssignee do let(:user) { create(:user) } describe '#assignee_id=' do diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index f091861bd41..dd5d422f12d 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiscussionOnDiff do +RSpec.describe DiscussionOnDiff do subject { create(:diff_note_on_merge_request, line_number: 18).to_discussion } describe "#truncated_diff_lines" do diff --git a/spec/models/concerns/each_batch_spec.rb b/spec/models/concerns/each_batch_spec.rb index ee3d9aea505..3c93c8a7a79 100644 --- a/spec/models/concerns/each_batch_spec.rb +++ b/spec/models/concerns/each_batch_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe EachBatch do +RSpec.describe EachBatch do describe '.each_batch' do let(:model) do Class.new(ActiveRecord::Base) do diff --git a/spec/models/concerns/editable_spec.rb b/spec/models/concerns/editable_spec.rb index 4a4a3ca5687..1d26629d0aa 100644 --- a/spec/models/concerns/editable_spec.rb +++ b/spec/models/concerns/editable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Editable do +RSpec.describe Editable do describe '#edited?' do let(:issue) { create(:issue, last_edited_at: nil) } let(:edited_issue) { create(:issue, created_at: 3.days.ago, last_edited_at: 2.days.ago) } diff --git a/spec/models/concerns/expirable_spec.rb b/spec/models/concerns/expirable_spec.rb index f4f5eab5b86..b20d759fc3f 100644 --- a/spec/models/concerns/expirable_spec.rb +++ b/spec/models/concerns/expirable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Expirable do +RSpec.describe Expirable do describe 'ProjectMember' do let(:no_expire) { create(:project_member) } let(:expire_later) { create(:project_member, expires_at: Time.current + 6.days) } diff --git a/spec/models/concerns/faster_cache_keys_spec.rb b/spec/models/concerns/faster_cache_keys_spec.rb index 7830acbae3d..ab6e809b3f7 100644 --- a/spec/models/concerns/faster_cache_keys_spec.rb +++ b/spec/models/concerns/faster_cache_keys_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe FasterCacheKeys do +RSpec.describe FasterCacheKeys do describe '#cache_key' do it 'returns a String' do # We're using a fixed string here so it's easier to set an expectation for diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb index 89720e3652c..cc01820cc97 100644 --- a/spec/models/concerns/featurable_spec.rb +++ b/spec/models/concerns/featurable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Featurable do +RSpec.describe Featurable do let_it_be(:user) { create(:user) } let(:project) { create(:project) } let(:feature_class) { subject.class } diff --git a/spec/models/concerns/feature_gate_spec.rb b/spec/models/concerns/feature_gate_spec.rb index 276d3d9e1d5..6106708a32d 100644 --- a/spec/models/concerns/feature_gate_spec.rb +++ b/spec/models/concerns/feature_gate_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe FeatureGate do +RSpec.describe FeatureGate do describe 'User' do describe '#flipper_id' do context 'when user is not persisted' do diff --git a/spec/models/concerns/from_union_spec.rb b/spec/models/concerns/from_union_spec.rb index 735e14b47ec..9819a6ec3de 100644 --- a/spec/models/concerns/from_union_spec.rb +++ b/spec/models/concerns/from_union_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe FromUnion do +RSpec.describe FromUnion do describe '.from_union' do let(:model) do Class.new(ActiveRecord::Base) do diff --git a/spec/models/concerns/group_descendant_spec.rb b/spec/models/concerns/group_descendant_spec.rb index 47419770d0f..b29fa910ee6 100644 --- a/spec/models/concerns/group_descendant_spec.rb +++ b/spec/models/concerns/group_descendant_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupDescendant do +RSpec.describe GroupDescendant do let(:parent) { create(:group) } let(:subgroup) { create(:group, parent: parent) } let(:subsub_group) { create(:group, parent: subgroup) } diff --git a/spec/models/concerns/has_environment_scope_spec.rb b/spec/models/concerns/has_environment_scope_spec.rb index a6e1ba59263..0cc997709c9 100644 --- a/spec/models/concerns/has_environment_scope_spec.rb +++ b/spec/models/concerns/has_environment_scope_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe HasEnvironmentScope do +RSpec.describe HasEnvironmentScope do subject { build(:ci_variable) } it { is_expected.to allow_value('*').for(:environment_scope) } diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb index f12eee414f9..9496bb57b8b 100644 --- a/spec/models/concerns/has_user_type_spec.rb +++ b/spec/models/concerns/has_user_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe User do +RSpec.describe User do specify 'types consistency checks', :aggregate_failures do expect(described_class::USER_TYPES.keys) .to match_array(%w[human ghost alert_bot project_bot support_bot service_user visual_review_bot migration_bot]) diff --git a/spec/models/concerns/ignorable_columns_spec.rb b/spec/models/concerns/ignorable_columns_spec.rb index 018b1296c62..a5eff154a0b 100644 --- a/spec/models/concerns/ignorable_columns_spec.rb +++ b/spec/models/concerns/ignorable_columns_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IgnorableColumns do +RSpec.describe IgnorableColumns do let(:record_class) do Class.new(ApplicationRecord) do include IgnorableColumns diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 74ee7a87b7b..96d3e2b7b1b 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Issuable do +RSpec.describe Issuable do include ProjectForksHelper let(:issuable_class) { Issue } @@ -416,6 +416,27 @@ describe Issuable do describe '#to_hook_data' do let(:builder) { double } + context 'when old_associations is empty' do + let(:label) { create(:label) } + + before do + issue.update!(labels: [label]) + issue.assignees << user + issue.spend_time(duration: 2, user_id: user.id, spent_at: Time.current) + expect(Gitlab::HookData::IssuableBuilder) + .to receive(:new).with(issue).and_return(builder) + end + + it 'delegates to Gitlab::HookData::IssuableBuilder#build and does not set labels, assignees, nor total_time_spent' do + expect(builder).to receive(:build).with( + user: user, + changes: {}) + + # In some cases, old_associations is empty, e.g. on a close event + issue.to_hook_data(user) + end + end + context 'labels are updated' do let(:labels) { create_list(:label, 2) } diff --git a/spec/models/concerns/limitable_spec.rb b/spec/models/concerns/limitable_spec.rb index ca0a257be7a..753e2a8ee5e 100644 --- a/spec/models/concerns/limitable_spec.rb +++ b/spec/models/concerns/limitable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Limitable do +RSpec.describe Limitable do let(:minimal_test_class) do Class.new do include ActiveModel::Model diff --git a/spec/models/concerns/loaded_in_group_list_spec.rb b/spec/models/concerns/loaded_in_group_list_spec.rb index 509811822e0..c37943022ba 100644 --- a/spec/models/concerns/loaded_in_group_list_spec.rb +++ b/spec/models/concerns/loaded_in_group_list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LoadedInGroupList do +RSpec.describe LoadedInGroupList do let(:parent) { create(:group) } subject(:found_group) { Group.with_selects_for_list.find_by(id: parent.id) } diff --git a/spec/models/concerns/manual_inverse_association_spec.rb b/spec/models/concerns/manual_inverse_association_spec.rb index ee32e3b165b..1349d2cc680 100644 --- a/spec/models/concerns/manual_inverse_association_spec.rb +++ b/spec/models/concerns/manual_inverse_association_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ManualInverseAssociation do +RSpec.describe ManualInverseAssociation do let(:model) do Class.new(MergeRequest) do belongs_to :manual_association, class_name: 'MergeRequestDiff', foreign_key: :latest_merge_request_diff_id diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index 03fd1c69654..758b5aa2ce4 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Mentionable do +RSpec.describe Mentionable do before do stub_const('Example', Class.new) Example.class_eval do @@ -67,7 +67,7 @@ describe Mentionable do end end -describe Issue, "Mentionable" do +RSpec.describe Issue, "Mentionable" do describe '#mentioned_users' do let!(:user) { create(:user, username: 'stranger') } let!(:user2) { create(:user, username: 'john') } @@ -222,7 +222,7 @@ describe Issue, "Mentionable" do end end -describe Commit, 'Mentionable' do +RSpec.describe Commit, 'Mentionable' do let(:project) { create(:project, :public, :repository) } let(:commit) { project.commit } @@ -291,7 +291,7 @@ describe Commit, 'Mentionable' do end end -describe MergeRequest, 'Mentionable' do +RSpec.describe MergeRequest, 'Mentionable' do describe '#store_mentions!' do it_behaves_like 'mentions in description', :merge_request it_behaves_like 'mentions in notes', :merge_request do @@ -312,7 +312,7 @@ describe MergeRequest, 'Mentionable' do end end -describe Snippet, 'Mentionable' do +RSpec.describe Snippet, 'Mentionable' do describe '#store_mentions!' do it_behaves_like 'mentions in description', :project_snippet it_behaves_like 'mentions in notes', :project_snippet do @@ -329,7 +329,7 @@ describe Snippet, 'Mentionable' do end end -describe PersonalSnippet, 'Mentionable' do +RSpec.describe PersonalSnippet, 'Mentionable' do describe '#store_mentions!' do it_behaves_like 'mentions in description', :personal_snippet it_behaves_like 'mentions in notes', :personal_snippet do @@ -346,7 +346,7 @@ describe PersonalSnippet, 'Mentionable' do end end -describe DesignManagement::Design do +RSpec.describe DesignManagement::Design do describe '#store_mentions!' do it_behaves_like 'mentions in notes', :design do let(:note) { create(:diff_note_on_design) } diff --git a/spec/models/concerns/milestoneable_spec.rb b/spec/models/concerns/milestoneable_spec.rb index 0b19c0542ee..15352a1453c 100644 --- a/spec/models/concerns/milestoneable_spec.rb +++ b/spec/models/concerns/milestoneable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Milestoneable do +RSpec.describe Milestoneable do let(:user) { create(:user) } let(:milestone) { create(:milestone, project: project) } diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb index 8c43a12aa15..58cd054efd5 100644 --- a/spec/models/concerns/milestoneish_spec.rb +++ b/spec/models/concerns/milestoneish_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Milestone, 'Milestoneish' do +RSpec.describe Milestone, 'Milestoneish' do let(:author) { create(:user) } let(:assignee) { create(:user) } let(:non_member) { create(:user) } diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index 5c8c5425ca7..bb7374bf46c 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Noteable do +RSpec.describe Noteable do let!(:active_diff_note1) { create(:diff_note_on_merge_request) } let(:project) { active_diff_note1.project } subject { active_diff_note1.noteable } @@ -262,4 +262,44 @@ describe Noteable do end end end + + describe "#has_any_diff_note_positions?" do + let(:source_branch) { "compare-with-merge-head-source" } + let(:target_branch) { "compare-with-merge-head-target" } + let(:merge_request) { create(:merge_request, source_branch: source_branch, target_branch: target_branch) } + + let!(:note) do + path = "files/markdown/ruby-style-guide.md" + + position = Gitlab::Diff::Position.new( + old_path: path, + new_path: path, + new_line: 508, + diff_refs: merge_request.diff_refs + ) + + create(:diff_note_on_merge_request, project: merge_request.project, position: position, noteable: merge_request) + end + + before do + MergeRequests::MergeToRefService.new(merge_request.project, merge_request.author).execute(merge_request) + Discussions::CaptureDiffNotePositionsService.new(merge_request).execute + end + + it "returns true when it has diff note positions" do + expect(merge_request.has_any_diff_note_positions?).to be(true) + end + + it "returns false when it has notes but no diff note positions" do + DiffNotePosition.where(note: note).find_each(&:delete) + + expect(merge_request.has_any_diff_note_positions?).to be(false) + end + + it "returns false when it has no notes" do + merge_request.notes.find_each(&:destroy) + + expect(merge_request.has_any_diff_note_positions?).to be(false) + end + end end diff --git a/spec/models/concerns/optionally_search_spec.rb b/spec/models/concerns/optionally_search_spec.rb index e1eb4cf8cd2..c8e2e6da51f 100644 --- a/spec/models/concerns/optionally_search_spec.rb +++ b/spec/models/concerns/optionally_search_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe OptionallySearch do +RSpec.describe OptionallySearch do describe '.search' do let(:model) do Class.new do diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb index 3d5937c4fc6..3376e337dc9 100644 --- a/spec/models/concerns/participable_spec.rb +++ b/spec/models/concerns/participable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Participable do +RSpec.describe Participable do let(:model) do Class.new do include Participable diff --git a/spec/models/concerns/partitioned_table_spec.rb b/spec/models/concerns/partitioned_table_spec.rb new file mode 100644 index 00000000000..3343b273ba2 --- /dev/null +++ b/spec/models/concerns/partitioned_table_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe PartitionedTable do + describe '.partitioned_by' do + subject { my_class.partitioned_by(key, strategy: :monthly) } + + let(:key) { :foo } + + let(:my_class) do + Class.new do + include PartitionedTable + end + end + + it 'assigns the MonthlyStrategy as the partitioning strategy' do + subject + + expect(my_class.partitioning_strategy).to be_a(Gitlab::Database::Partitioning::MonthlyStrategy) + end + + it 'passes the partitioning key to the strategy instance' do + subject + + expect(my_class.partitioning_strategy.partitioning_key).to eq(key) + end + + it 'registers itself with the PartitionCreator' do + expect(Gitlab::Database::Partitioning::PartitionCreator).to receive(:register).with(my_class) + + subject + end + end +end diff --git a/spec/models/concerns/presentable_spec.rb b/spec/models/concerns/presentable_spec.rb index 9db868dd348..871e122e409 100644 --- a/spec/models/concerns/presentable_spec.rb +++ b/spec/models/concerns/presentable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Presentable do +RSpec.describe Presentable do let(:build) { Ci::Build.new } describe '#present' do diff --git a/spec/models/concerns/project_api_compatibility_spec.rb b/spec/models/concerns/project_api_compatibility_spec.rb index f5722f88aac..7a69406cb71 100644 --- a/spec/models/concerns/project_api_compatibility_spec.rb +++ b/spec/models/concerns/project_api_compatibility_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectAPICompatibility do +RSpec.describe ProjectAPICompatibility do let(:project) { create(:project) } # git_strategy diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb index 8346c4ad4cc..ba70ff563a8 100644 --- a/spec/models/concerns/project_features_compatibility_spec.rb +++ b/spec/models/concerns/project_features_compatibility_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectFeaturesCompatibility do +RSpec.describe ProjectFeaturesCompatibility do let(:project) { create(:project) } let(:features_enabled) { %w(issues wiki builds merge_requests snippets) } let(:features) { features_enabled + %w(repository pages) } diff --git a/spec/models/concerns/prometheus_adapter_spec.rb b/spec/models/concerns/prometheus_adapter_spec.rb index fdc98ba74b8..e795e2b06cb 100644 --- a/spec/models/concerns/prometheus_adapter_spec.rb +++ b/spec/models/concerns/prometheus_adapter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PrometheusAdapter, :use_clean_rails_memory_store_caching do +RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do include PrometheusHelpers include ReactiveCachingHelpers diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb index f63ad958ed3..750a5eba303 100644 --- a/spec/models/concerns/protected_ref_access_spec.rb +++ b/spec/models/concerns/protected_ref_access_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProtectedRefAccess do +RSpec.describe ProtectedRefAccess do include ExternalAuthorizationServiceHelpers subject(:protected_ref_access) do diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index cfca383e0b0..b12ad82920f 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ReactiveCaching, :use_clean_rails_memory_store_caching do +RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do include ExclusiveLeaseHelpers include ReactiveCachingHelpers @@ -285,38 +285,30 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do go! end - context 'when calculated object size exceeds default reactive_cache_hard_limit' do - let(:calculation) { -> { 'a' * 2 * 1.megabyte } } + context 'when reactive_cache_hard_limit is set' do + let(:test_class) { Class.new(cache_class_test) { self.reactive_cache_hard_limit = 1.megabyte } } + let(:instance) { test_class.new(666, &calculation) } + + context 'when cache size is over the overridden limit' do + let(:calculation) { -> { 'a' * 2 * 1.megabyte } } - shared_examples 'ExceededReactiveCacheLimit' do it 'raises ExceededReactiveCacheLimit exception and does not cache new data' do expect { go! }.to raise_exception(ReactiveCaching::ExceededReactiveCacheLimit) expect(read_reactive_cache(instance)).not_to eq(calculation.call) end - end - context 'when reactive_cache_hard_limit feature flag is enabled' do - it_behaves_like 'ExceededReactiveCacheLimit' - - context 'when reactive_cache_hard_limit is overridden' do - let(:test_class) { Class.new(cache_class_test) { self.reactive_cache_hard_limit = 3.megabytes } } - let(:instance) { test_class.new(666, &calculation) } + context 'when reactive_cache_limit_enabled? is overridden to return false' do + before do + allow(instance).to receive(:reactive_cache_limit_enabled?).and_return(false) + end it_behaves_like 'successful cache' - - context 'when cache size is over the overridden limit' do - let(:calculation) { -> { 'a' * 4 * 1.megabyte } } - - it_behaves_like 'ExceededReactiveCacheLimit' - end end end - context 'when reactive_cache_limit feature flag is disabled' do - before do - stub_feature_flags(reactive_cache_limit: false) - end + context 'when cache size is within the overridden limit' do + let(:calculation) { -> { 'Smaller than 1Mb reactive_cache_hard_limit' } } it_behaves_like 'successful cache' end @@ -377,7 +369,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do it { expect(subject.reactive_cache_refresh_interval).to be_a(ActiveSupport::Duration) } it { expect(subject.reactive_cache_lifetime).to be_a(ActiveSupport::Duration) } it { expect(subject.reactive_cache_key).to respond_to(:call) } - it { expect(subject.reactive_cache_hard_limit).to be_a(Integer) } + it { expect(subject.reactive_cache_hard_limit).to be_nil } it { expect(subject.reactive_cache_worker_finder).to respond_to(:call) } end end diff --git a/spec/models/concerns/redactable_spec.rb b/spec/models/concerns/redactable_spec.rb index 3f6a2e2410c..bb59e04adf1 100644 --- a/spec/models/concerns/redactable_spec.rb +++ b/spec/models/concerns/redactable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Redactable do +RSpec.describe Redactable do before do stub_commonmark_sourcepos_disabled end diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 1cf6afcc167..c270f23defb 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe RedisCacheable do +RSpec.describe RedisCacheable do let(:model) do Struct.new(:id, :attributes) do def read_attribute(attribute) diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb index 95553fb13a6..c91ddfee944 100644 --- a/spec/models/concerns/resolvable_discussion_spec.rb +++ b/spec/models/concerns/resolvable_discussion_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Discussion, ResolvableDiscussion do +RSpec.describe Discussion, ResolvableDiscussion do subject { described_class.new([first_note, second_note, third_note]) } let(:first_note) { create(:discussion_note_on_merge_request) } diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb index 12e50ac807e..69c58a5cfe5 100644 --- a/spec/models/concerns/resolvable_note_spec.rb +++ b/spec/models/concerns/resolvable_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Note, ResolvableNote do +RSpec.describe Note, ResolvableNote do let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index c891fdcb6b5..15d754861b2 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Group, 'Routable' do +RSpec.describe Group, 'Routable' do let!(:group) { create(:group, name: 'foo') } describe 'Validations' do @@ -164,7 +164,7 @@ describe Group, 'Routable' do end end -describe Project, 'Routable' do +RSpec.describe Project, 'Routable' do describe '#full_path' do let(:project) { build_stubbed(:project) } diff --git a/spec/models/concerns/safe_url_spec.rb b/spec/models/concerns/safe_url_spec.rb index e523e6a15e4..3d38c05bf11 100644 --- a/spec/models/concerns/safe_url_spec.rb +++ b/spec/models/concerns/safe_url_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SafeUrl do +RSpec.describe SafeUrl do describe '#safe_url' do let(:safe_url_test_class) do Class.new do diff --git a/spec/models/concerns/schedulable_spec.rb b/spec/models/concerns/schedulable_spec.rb index 38ae2112e01..875c2d80e55 100644 --- a/spec/models/concerns/schedulable_spec.rb +++ b/spec/models/concerns/schedulable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Schedulable do +RSpec.describe Schedulable do shared_examples 'before_save callback' do it 'updates next_run_at' do expect { object.save! }.to change { object.next_run_at } diff --git a/spec/models/concerns/sha256_attribute_spec.rb b/spec/models/concerns/sha256_attribute_spec.rb index 213723c2dcb..c247865d77f 100644 --- a/spec/models/concerns/sha256_attribute_spec.rb +++ b/spec/models/concerns/sha256_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Sha256Attribute do +RSpec.describe Sha256Attribute do let(:model) { Class.new { include Sha256Attribute } } before do diff --git a/spec/models/concerns/sha_attribute_spec.rb b/spec/models/concerns/sha_attribute_spec.rb index 0d4dbfb215e..50748efcda4 100644 --- a/spec/models/concerns/sha_attribute_spec.rb +++ b/spec/models/concerns/sha_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ShaAttribute do +RSpec.describe ShaAttribute do let(:model) { Class.new { include ShaAttribute } } before do diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb index a1fe5c0928d..bbfdaeec64c 100644 --- a/spec/models/concerns/sortable_spec.rb +++ b/spec/models/concerns/sortable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Sortable do +RSpec.describe Sortable do describe '.order_by' do let(:arel_table) { Group.arel_table } let(:relation) { Group.all } diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb index a8d27e174b7..d4fcb2e99eb 100644 --- a/spec/models/concerns/spammable_spec.rb +++ b/spec/models/concerns/spammable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Spammable do +RSpec.describe Spammable do let(:issue) { create(:issue, description: 'Test Desc.') } describe 'Associations' do diff --git a/spec/models/concerns/stepable_spec.rb b/spec/models/concerns/stepable_spec.rb index 51356c3eaf6..e442e4f0664 100644 --- a/spec/models/concerns/stepable_spec.rb +++ b/spec/models/concerns/stepable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Stepable do +RSpec.describe Stepable do let(:described_class) do Class.new do include Stepable diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb index 5c0d1042e06..812f0a015f7 100644 --- a/spec/models/concerns/strip_attribute_spec.rb +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe StripAttribute do +RSpec.describe StripAttribute do let(:milestone) { create(:milestone) } describe ".strip_attributes" do diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb index f189cd7633c..2a43e748e58 100644 --- a/spec/models/concerns/subscribable_spec.rb +++ b/spec/models/concerns/subscribable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Subscribable, 'Subscribable' do +RSpec.describe Subscribable, 'Subscribable' do let(:project) { create(:project) } let(:resource) { create(:issue, project: project) } let(:user_1) { create(:user) } diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 36eb8fdaba4..e0e764fc63c 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -shared_examples 'TokenAuthenticatable' do +RSpec.shared_examples 'TokenAuthenticatable' do describe 'dynamically defined methods' do it { expect(described_class).to respond_to("find_by_#{token_field}") } it { is_expected.to respond_to("ensure_#{token_field}") } @@ -11,7 +11,7 @@ shared_examples 'TokenAuthenticatable' do end end -describe User, 'TokenAuthenticatable' do +RSpec.describe User, 'TokenAuthenticatable' do let(:token_field) { :feed_token } it_behaves_like 'TokenAuthenticatable' @@ -23,7 +23,7 @@ describe User, 'TokenAuthenticatable' do end end -describe ApplicationSetting, 'TokenAuthenticatable' do +RSpec.describe ApplicationSetting, 'TokenAuthenticatable' do let(:token_field) { :runners_registration_token } let(:settings) { described_class.new } @@ -100,7 +100,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do end end -describe PersonalAccessToken, 'TokenAuthenticatable' do +RSpec.describe PersonalAccessToken, 'TokenAuthenticatable' do shared_examples 'changes personal access token' do it 'sets new token' do subject @@ -205,7 +205,7 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do end end -describe Ci::Build, 'TokenAuthenticatable' do +RSpec.describe Ci::Build, 'TokenAuthenticatable' do let(:token_field) { :token } let(:build) { FactoryBot.build(:ci_build) } diff --git a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb index 7332da309d5..bccef9b9554 100644 --- a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb +++ b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe TokenAuthenticatableStrategies::Base do +RSpec.describe TokenAuthenticatableStrategies::Base do let(:instance) { double(:instance) } let(:field) { double(:field) } diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb index 70f41981b3b..f6b8cf7def4 100644 --- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb +++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe TokenAuthenticatableStrategies::Encrypted do +RSpec.describe TokenAuthenticatableStrategies::Encrypted do let(:model) { double(:model) } let(:instance) { double(:instance) } diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/models/concerns/uniquify_spec.rb index 9ba35702ba6..9b79e4d4154 100644 --- a/spec/models/concerns/uniquify_spec.rb +++ b/spec/models/concerns/uniquify_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Uniquify do +RSpec.describe Uniquify do let(:uniquify) { described_class.new } describe "#string" do diff --git a/spec/models/concerns/usage_statistics_spec.rb b/spec/models/concerns/usage_statistics_spec.rb index f99f0a13317..15ccd08eda9 100644 --- a/spec/models/concerns/usage_statistics_spec.rb +++ b/spec/models/concerns/usage_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UsageStatistics do +RSpec.describe UsageStatistics do describe '.distinct_count_by' do let_it_be(:issue_1) { create(:issue) } let_it_be(:issue_2) { create(:issue) } diff --git a/spec/models/concerns/where_composite_spec.rb b/spec/models/concerns/where_composite_spec.rb index 1c0951d90d0..fb23e6bfe1d 100644 --- a/spec/models/concerns/where_composite_spec.rb +++ b/spec/models/concerns/where_composite_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WhereComposite do +RSpec.describe WhereComposite do describe '.where_composite' do let_it_be(:test_table_name) { "test_table_#{SecureRandom.hex(10)}" } diff --git a/spec/models/concerns/x509_serial_number_attribute_spec.rb b/spec/models/concerns/x509_serial_number_attribute_spec.rb index 18a1d85204c..88550823748 100644 --- a/spec/models/concerns/x509_serial_number_attribute_spec.rb +++ b/spec/models/concerns/x509_serial_number_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe X509SerialNumberAttribute do +RSpec.describe X509SerialNumberAttribute do let(:model) { Class.new { include X509SerialNumberAttribute } } before do diff --git a/spec/models/container_registry/event_spec.rb b/spec/models/container_registry/event_spec.rb index 54ff218f2a8..21a3ab5363a 100644 --- a/spec/models/container_registry/event_spec.rb +++ b/spec/models/container_registry/event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ContainerRegistry::Event do +RSpec.describe ContainerRegistry::Event do using RSpec::Parameterized::TableSyntax let_it_be(:group) { create(:group, name: 'group') } diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index 4f23a905e93..953f92d103b 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ContainerRepository do +RSpec.describe ContainerRepository do let(:group) { create(:group, name: 'group') } let(:project) { create(:project, path: 'test', group: group) } diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb new file mode 100644 index 00000000000..2b569b6097d --- /dev/null +++ b/spec/models/custom_emoji_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe CustomEmoji do + describe 'Associations' do + it { is_expected.to belong_to(:namespace) } + it { is_expected.to have_db_column(:file) } + it { is_expected.to validate_length_of(:name).is_at_most(36) } + it { is_expected.to validate_presence_of(:name) } + end + + describe 'exclusion of duplicated emoji' do + let(:emoji_name) { Gitlab::Emoji.emojis_names.sample } + + it 'disallows emoji names of built-in emoji' do + new_emoji = build(:custom_emoji, name: emoji_name) + + expect(new_emoji).not_to be_valid + expect(new_emoji.errors.messages).to eq(name: ["#{emoji_name} is already being used for another emoji"]) + end + + it 'disallows duplicate custom emoji names within namespace' do + old_emoji = create(:custom_emoji) + new_emoji = build(:custom_emoji, name: old_emoji.name, namespace: old_emoji.namespace) + + expect(new_emoji).not_to be_valid + expect(new_emoji.errors.messages).to eq(name: ["has already been taken"]) + end + end +end diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index f6ab8e0ece6..8900c49a662 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#code' do +RSpec.describe 'CycleAnalytics#code' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index b4ab763e0e6..9372ef5f0e6 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#issue' do +RSpec.describe 'CycleAnalytics#issue' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index 6765b2e2cbc..364694a11e1 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#plan' do +RSpec.describe 'CycleAnalytics#plan' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index 2f2bcd63acd..cf4d57d6b73 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#production' do +RSpec.describe 'CycleAnalytics#production' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/project_level_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb index bb296351a29..c2d421c03d8 100644 --- a/spec/models/cycle_analytics/project_level_spec.rb +++ b/spec/models/cycle_analytics/project_level_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CycleAnalytics::ProjectLevel do +RSpec.describe CycleAnalytics::ProjectLevel do let_it_be(:project) { create(:project, :repository) } let_it_be(:from_date) { 10.days.ago } let_it_be(:user) { project.owner } diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 25e8f1441d3..6ebbcebd71d 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#review' do +RSpec.describe 'CycleAnalytics#review' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index effbc7056cc..024625d229f 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#staging' do +RSpec.describe 'CycleAnalytics#staging' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index 7e7ba4d9994..7010d69f8a4 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'CycleAnalytics#test' do +RSpec.describe 'CycleAnalytics#test' do extend CycleAnalyticsHelpers::TestGeneration let_it_be(:project) { create(:project, :repository) } diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index ec6cfb6b826..00114a94b56 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeployKey, :mailer do +RSpec.describe DeployKey, :mailer do describe "Associations" do it { is_expected.to have_many(:deploy_keys_projects) } it { is_expected.to have_many(:projects) } diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index 1dbae78a01d..7dd4d3129de 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeployKeysProject do +RSpec.describe DeployKeysProject do describe "Associations" do it { is_expected.to belong_to(:deploy_key) } it { is_expected.to belong_to(:project) } @@ -13,6 +13,21 @@ describe DeployKeysProject do it { is_expected.to validate_presence_of(:deploy_key) } end + describe '.with_deploy_keys' do + subject(:scoped_query) { described_class.with_deploy_keys.last } + + it 'includes deploy_keys in query' do + project = create(:project) + create(:deploy_keys_project, project: project, deploy_key: create(:deploy_key)) + + includes_query_count = ActiveRecord::QueryRecorder.new { scoped_query }.count + deploy_key_query_count = ActiveRecord::QueryRecorder.new { scoped_query.deploy_key }.count + + expect(includes_query_count).to eq(2) + expect(deploy_key_query_count).to eq(0) + end + end + describe "Destroying" do let(:project) { create(:project) } subject { create(:deploy_keys_project, project: project) } diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb index 819e2850644..9fd3751be13 100644 --- a/spec/models/deploy_token_spec.rb +++ b/spec/models/deploy_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeployToken do +RSpec.describe DeployToken do subject(:deploy_token) { create(:deploy_token) } it { is_expected.to have_many :project_deploy_tokens } diff --git a/spec/models/deployment_cluster_spec.rb b/spec/models/deployment_cluster_spec.rb index 8bb09e9a510..dc9cbe4b082 100644 --- a/spec/models/deployment_cluster_spec.rb +++ b/spec/models/deployment_cluster_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeploymentCluster do +RSpec.describe DeploymentCluster do let(:cluster) { create(:cluster) } let(:deployment) { create(:deployment) } let(:kubernetes_namespace) { 'an-example-namespace' } diff --git a/spec/models/deployment_merge_request_spec.rb b/spec/models/deployment_merge_request_spec.rb index fd5be52d47c..29834691fa4 100644 --- a/spec/models/deployment_merge_request_spec.rb +++ b/spec/models/deployment_merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeploymentMergeRequest do +RSpec.describe DeploymentMergeRequest do let(:mr) { create(:merge_request, :merged) } let(:deployment) { create(:deployment, :success, project: project) } let(:project) { mr.project } diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb index 5a4ae0bbe79..d0474777eb7 100644 --- a/spec/models/deployment_metrics_spec.rb +++ b/spec/models/deployment_metrics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DeploymentMetrics do +RSpec.describe DeploymentMetrics do describe '#has_metrics?' do subject { described_class.new(deployment.project, deployment).has_metrics? } diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index ac2a4c9877d..b320390711e 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Deployment do +RSpec.describe Deployment do subject { build(:deployment) } it { is_expected.to belong_to(:project).required } diff --git a/spec/models/description_version_spec.rb b/spec/models/description_version_spec.rb index 5ec34c0cde4..7c094f7a0a0 100644 --- a/spec/models/description_version_spec.rb +++ b/spec/models/description_version_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DescriptionVersion do +RSpec.describe DescriptionVersion do describe 'associations' do it { is_expected.to belong_to :issue } it { is_expected.to belong_to :merge_request } diff --git a/spec/models/design_management/action_spec.rb b/spec/models/design_management/action_spec.rb index 753c31b1549..59c58191718 100644 --- a/spec/models/design_management/action_spec.rb +++ b/spec/models/design_management/action_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe DesignManagement::Action do +RSpec.describe DesignManagement::Action do describe 'relations' do it { is_expected.to belong_to(:design) } it { is_expected.to belong_to(:version) } diff --git a/spec/models/design_management/design_action_spec.rb b/spec/models/design_management/design_action_spec.rb index da4ad41dfcb..958b1dd9124 100644 --- a/spec/models/design_management/design_action_spec.rb +++ b/spec/models/design_management/design_action_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe DesignManagement::DesignAction do +RSpec.describe DesignManagement::DesignAction do describe 'validations' do describe 'the design' do let(:fail_validation) { raise_error(/design/i) } diff --git a/spec/models/design_management/design_at_version_spec.rb b/spec/models/design_management/design_at_version_spec.rb index f6fa8df243c..2c640ee5c2c 100644 --- a/spec/models/design_management/design_at_version_spec.rb +++ b/spec/models/design_management/design_at_version_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DesignManagement::DesignAtVersion do +RSpec.describe DesignManagement::DesignAtVersion do include DesignManagementTestHelpers let_it_be(:issue, reload: true) { create(:issue) } diff --git a/spec/models/design_management/design_collection_spec.rb b/spec/models/design_management/design_collection_spec.rb index bd48f742042..c5e290da759 100644 --- a/spec/models/design_management/design_collection_spec.rb +++ b/spec/models/design_management/design_collection_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe DesignManagement::DesignCollection do +RSpec.describe DesignManagement::DesignCollection do include DesignManagementTestHelpers let_it_be(:issue, reload: true) { create(:issue) } diff --git a/spec/models/design_management/design_spec.rb b/spec/models/design_management/design_spec.rb index bc1f54f057e..345147390c0 100644 --- a/spec/models/design_management/design_spec.rb +++ b/spec/models/design_management/design_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DesignManagement::Design do +RSpec.describe DesignManagement::Design do include DesignManagementTestHelpers let_it_be(:issue) { create(:issue) } diff --git a/spec/models/design_management/repository_spec.rb b/spec/models/design_management/repository_spec.rb index 996316eeec9..0115e0c139c 100644 --- a/spec/models/design_management/repository_spec.rb +++ b/spec/models/design_management/repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DesignManagement::Repository do +RSpec.describe DesignManagement::Repository do let(:project) { create(:project) } let(:repository) { described_class.new(project) } diff --git a/spec/models/design_management/version_spec.rb b/spec/models/design_management/version_spec.rb index ab6958ea94a..cd52f4129dc 100644 --- a/spec/models/design_management/version_spec.rb +++ b/spec/models/design_management/version_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe DesignManagement::Version do +RSpec.describe DesignManagement::Version do let_it_be(:issue) { create(:issue) } describe 'relations' do diff --git a/spec/models/design_user_mention_spec.rb b/spec/models/design_user_mention_spec.rb index 03c77c73c8d..944a82c5edf 100644 --- a/spec/models/design_user_mention_spec.rb +++ b/spec/models/design_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DesignUserMention do +RSpec.describe DesignUserMention do describe 'associations' do it { is_expected.to belong_to(:design) } it { is_expected.to belong_to(:note) } diff --git a/spec/models/dev_ops_score/metric_spec.rb b/spec/models/dev_ops_score/metric_spec.rb index 89212d5ca26..60001d0667d 100644 --- a/spec/models/dev_ops_score/metric_spec.rb +++ b/spec/models/dev_ops_score/metric_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DevOpsScore::Metric do +RSpec.describe DevOpsScore::Metric do let(:conv_dev_index) { create(:dev_ops_score_metric) } describe '#percentage_score' do diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb index cfeb4382927..26b311fe629 100644 --- a/spec/models/diff_discussion_spec.rb +++ b/spec/models/diff_discussion_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiffDiscussion do +RSpec.describe DiffDiscussion do include RepoHelpers subject { described_class.new([diff_note]) } diff --git a/spec/models/diff_note_position_spec.rb b/spec/models/diff_note_position_spec.rb index d93e0af5526..aa9e6b4e824 100644 --- a/spec/models/diff_note_position_spec.rb +++ b/spec/models/diff_note_position_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiffNotePosition, type: :model do +RSpec.describe DiffNotePosition, type: :model do describe '.create_or_update_by' do context 'when a diff note' do let(:note) { create(:diff_note_on_merge_request) } diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb index 8bfe2ac7a6c..8a6176bf045 100644 --- a/spec/models/diff_note_spec.rb +++ b/spec/models/diff_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiffNote do +RSpec.describe DiffNote do include RepoHelpers let_it_be(:merge_request) { create(:merge_request) } diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb index 0a1c4c5560e..57c62788ee9 100644 --- a/spec/models/diff_viewer/base_spec.rb +++ b/spec/models/diff_viewer/base_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiffViewer::Base do +RSpec.describe DiffViewer::Base do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb index 0a14eae26f3..686dd1249be 100644 --- a/spec/models/diff_viewer/server_side_spec.rb +++ b/spec/models/diff_viewer/server_side_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DiffViewer::ServerSide do +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 950bdec4d00..021940be0c2 100644 --- a/spec/models/discussion_spec.rb +++ b/spec/models/discussion_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Discussion do +RSpec.describe Discussion do subject { described_class.new([first_note, second_note, third_note]) } let(:first_note) { create(:diff_note_on_merge_request) } diff --git a/spec/models/draft_note_spec.rb b/spec/models/draft_note_spec.rb index b880d3c5b97..64b06bf5c8f 100644 --- a/spec/models/draft_note_spec.rb +++ b/spec/models/draft_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DraftNote do +RSpec.describe DraftNote do include RepoHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index f7b194abcee..ffdc621dd4c 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Email do +RSpec.describe Email do describe 'modules' do subject { described_class } diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index b93da518b68..c449a3c3c47 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Environment, :use_clean_rails_memory_store_caching do +RSpec.describe Environment, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers using RSpec::Parameterized::TableSyntax include RepoHelpers @@ -18,6 +18,7 @@ describe Environment, :use_clean_rails_memory_store_caching do it { is_expected.to belong_to(:project).required } it { is_expected.to have_many(:deployments) } it { is_expected.to have_many(:metrics_dashboard_annotations) } + it { is_expected.to have_many(:alert_management_alerts) } it { is_expected.to delegate_method(:stop_action).to(:last_deployment) } it { is_expected.to delegate_method(:manual_actions).to(:last_deployment) } @@ -847,6 +848,20 @@ describe Environment, :use_clean_rails_memory_store_caching do subject { environment.calculate_reactive_cache } + it 'overrides default reactive_cache_hard_limit to 10 Mb' do + expect(described_class.reactive_cache_hard_limit).to eq(10.megabyte) + end + + it 'overrides reactive_cache_limit_enabled? with a FF' do + environment_with_enabled_ff = FactoryBot.build(:environment) + environment_with_disabled_ff = FactoryBot.build(:environment) + + stub_feature_flags(reactive_caching_limit_environment: environment_with_enabled_ff.project) + + expect(environment_with_enabled_ff.send(:reactive_cache_limit_enabled?)).to be_truthy + expect(environment_with_disabled_ff.send(:reactive_cache_limit_enabled?)).to be_falsey + end + it 'returns cache data from the deployment platform' do expect(environment.deployment_platform).to receive(:calculate_reactive_cache_for) .with(environment).and_return(pods: %w(pod1 pod2)) diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb index 10283b54796..7eefb8f714a 100644 --- a/spec/models/environment_status_spec.rb +++ b/spec/models/environment_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe EnvironmentStatus do +RSpec.describe EnvironmentStatus do include ProjectForksHelper let(:deployment) { create(:deployment, :succeed, :review_app) } diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb index b564c48a9c1..72ed11f6c74 100644 --- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb +++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ErrorTracking::ProjectErrorTrackingSetting do +RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do include ReactiveCachingHelpers include Gitlab::Routing diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb index 6d1954700bf..aca2a8c3a2f 100644 --- a/spec/models/event_collection_spec.rb +++ b/spec/models/event_collection_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -describe EventCollection do +RSpec.describe EventCollection do + include DesignManagementTestHelpers + describe '#to_a' do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project_empty_repo, group: group) } @@ -10,6 +12,10 @@ describe EventCollection do let_it_be(:user) { create(:user) } let_it_be(:merge_request) { create(:merge_request) } + before do + enable_design_management + end + context 'with project events' do let_it_be(:push_event_payloads) do Array.new(9) do @@ -21,11 +27,13 @@ describe EventCollection do let_it_be(:merge_request_events) { create_list(:event, 10, :commented, project: project, target: merge_request) } 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 most_recent_20_events = [ wiki_page_event, + design_event, closed_issue_event, *push_events, *merge_request_events @@ -36,40 +44,23 @@ describe EventCollection do expect(events).to match_array(most_recent_20_events) end - context 'the wiki_events feature flag is disabled' do - before do - stub_feature_flags(wiki_events: false) - end - - it 'omits the wiki page events when using to_a' do - events = described_class.new(projects).to_a - - expect(events).not_to include(wiki_page_event) - end - - it 'omits the wiki page events when using all_project_events' do - events = described_class.new(projects).all_project_events + it 'includes the wiki page events when using to_a' do + events = described_class.new(projects).to_a - expect(events).not_to include(wiki_page_event) - end + expect(events).to include(wiki_page_event) end - context 'the wiki_events feature flag is enabled' do - before do - stub_feature_flags(wiki_events: true) - end - - it 'includes the wiki page events when using to_a' do - events = described_class.new(projects).to_a + it 'includes the design events' do + collection = described_class.new(projects) - expect(events).to include(wiki_page_event) - end + expect(collection.to_a).to include(design_event) + expect(collection.all_project_events).to include(design_event) + end - it 'includes the wiki page events when using all_project_events' do - events = described_class.new(projects).all_project_events + it 'includes the wiki page events when using all_project_events' do + events = described_class.new(projects).all_project_events - expect(events).to include(wiki_page_event) - end + expect(events).to include(wiki_page_event) end it 'applies a limit to the number of events' do @@ -81,7 +72,7 @@ describe EventCollection do it 'can paginate through events' do events = described_class.new(projects, offset: 20).to_a - expect(events.length).to eq(1) + expect(events.length).to eq(2) end it 'returns an empty Array when crossing the maximum page number' do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 14066b1e9d2..96baeab6809 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Event do +RSpec.describe Event do describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:target) } @@ -643,15 +643,6 @@ describe Event do end end - describe '.not_design' do - it 'does not contain the design events' do - non_design_events = events.reject(&:design?) - - expect(events).not_to match_array(non_design_events) - expect(described_class.not_design).to match_array(non_design_events) - end - end - describe '.for_wiki_page' do it 'only contains the wiki page events' do wiki_events = events.select(&:wiki_page?) @@ -661,15 +652,6 @@ describe Event do end end - describe '.not_wiki_page' do - it 'does not contain the wiki page events' do - non_wiki_events = events.reject(&:wiki_page?) - - expect(events).not_to match_array(non_wiki_events) - expect(described_class.not_wiki_page).to match_array(non_wiki_events) - end - end - describe '.for_wiki_meta' do it 'finds events for a given wiki page metadata object' do event = events.select(&:wiki_page?).first diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index b8d85d49b07..47b13ff50cf 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ExternalIssue do +RSpec.describe ExternalIssue do let(:project) { double('project', id: 1, to_reference: 'namespace1/project1') } let(:issue) { described_class.new('EXT-1234', project) } diff --git a/spec/models/external_pull_request_spec.rb b/spec/models/external_pull_request_spec.rb index e85d5b2f6c7..e0822fc177a 100644 --- a/spec/models/external_pull_request_spec.rb +++ b/spec/models/external_pull_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ExternalPullRequest do +RSpec.describe ExternalPullRequest do let(:project) { create(:project) } let(:source_branch) { 'the-branch' } let(:status) { :open } diff --git a/spec/models/fork_network_member_spec.rb b/spec/models/fork_network_member_spec.rb index d7a0dd5be65..b34eb7964ca 100644 --- a/spec/models/fork_network_member_spec.rb +++ b/spec/models/fork_network_member_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ForkNetworkMember do +RSpec.describe ForkNetworkMember do describe 'validations' do it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_presence_of(:fork_network) } diff --git a/spec/models/fork_network_spec.rb b/spec/models/fork_network_spec.rb index 5ec0f8d6b02..c2ef1fdcb5f 100644 --- a/spec/models/fork_network_spec.rb +++ b/spec/models/fork_network_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ForkNetwork do +RSpec.describe ForkNetwork do include ProjectForksHelper describe '#add_root_as_member' do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index c8ed898122b..6fe5a1407a9 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GenericCommitStatus do +RSpec.describe GenericCommitStatus do let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:external_url) { 'http://example.gitlab.com/status' } diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb index b9c914e2506..7ecde04e3df 100644 --- a/spec/models/gpg_key_spec.rb +++ b/spec/models/gpg_key_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GpgKey do +RSpec.describe GpgKey do describe "associations" do it { is_expected.to belong_to(:user) } it { is_expected.to have_many(:subkeys) } diff --git a/spec/models/gpg_key_subkey_spec.rb b/spec/models/gpg_key_subkey_spec.rb index 5f80cc02924..c1d9e2bde43 100644 --- a/spec/models/gpg_key_subkey_spec.rb +++ b/spec/models/gpg_key_subkey_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GpgKeySubkey do +RSpec.describe GpgKeySubkey do subject { build(:gpg_key_subkey) } describe 'associations' do diff --git a/spec/models/grafana_integration_spec.rb b/spec/models/grafana_integration_spec.rb index 662e8b1dd61..79f102919ac 100644 --- a/spec/models/grafana_integration_spec.rb +++ b/spec/models/grafana_integration_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GrafanaIntegration do +RSpec.describe GrafanaIntegration do describe 'associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/group_custom_attribute_spec.rb b/spec/models/group_custom_attribute_spec.rb index 7d60c74b62b..1d8afa71377 100644 --- a/spec/models/group_custom_attribute_spec.rb +++ b/spec/models/group_custom_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupCustomAttribute do +RSpec.describe GroupCustomAttribute do describe 'assocations' do it { is_expected.to belong_to(:group) } end diff --git a/spec/models/group_deploy_key_spec.rb b/spec/models/group_deploy_key_spec.rb index 3ba56c7e504..3fe71cc4699 100644 --- a/spec/models/group_deploy_key_spec.rb +++ b/spec/models/group_deploy_key_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupDeployKey do +RSpec.describe GroupDeployKey do it { is_expected.to validate_presence_of(:user) } it 'is of type DeployKey' do diff --git a/spec/models/group_group_link_spec.rb b/spec/models/group_group_link_spec.rb index 54e622b2f22..03cc9d7e64c 100644 --- a/spec/models/group_group_link_spec.rb +++ b/spec/models/group_group_link_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupGroupLink do +RSpec.describe GroupGroupLink do let_it_be(:group) { create(:group) } let_it_be(:shared_group) { create(:group) } let_it_be(:group_group_link) do diff --git a/spec/models/group_import_state_spec.rb b/spec/models/group_import_state_spec.rb index 9d9cb1e8391..4404ef64966 100644 --- a/spec/models/group_import_state_spec.rb +++ b/spec/models/group_import_state_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupImportState do +RSpec.describe GroupImportState do describe 'validations' do let_it_be(:group) { create(:group) } diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb index a3a5c631c3d..ec9244d5eb5 100644 --- a/spec/models/group_label_spec.rb +++ b/spec/models/group_label_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupLabel do +RSpec.describe GroupLabel do describe 'relationships' do it { is_expected.to belong_to(:group) } end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 93cb6d83489..4184f2d07cc 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Group do +RSpec.describe Group do let!(:group) { create(:group) } describe 'associations' do @@ -1313,4 +1313,231 @@ describe Group do expect(groups).to contain_exactly(parent_group1, parent_group2, child_group1, child_group2, child_group3) end end + + describe '#shared_runners_allowed?' do + using RSpec::Parameterized::TableSyntax + + where(:shared_runners_enabled, :allow_descendants_override, :expected_shared_runners_allowed) do + true | false | true + true | true | true + false | false | false + false | true | true + end + + with_them do + let!(:group) { create(:group, shared_runners_enabled: shared_runners_enabled, allow_descendants_override_disabled_shared_runners: allow_descendants_override) } + + it 'returns the expected result' do + expect(group.shared_runners_allowed?).to eq(expected_shared_runners_allowed) + end + end + end + + describe '#parent_allows_shared_runners?' do + context 'when parent group is present' do + using RSpec::Parameterized::TableSyntax + + where(:shared_runners_enabled, :allow_descendants_override, :expected_shared_runners_allowed) do + true | false | true + true | true | true + false | false | false + false | true | true + end + + with_them do + let!(:parent_group) { create(:group, shared_runners_enabled: shared_runners_enabled, allow_descendants_override_disabled_shared_runners: allow_descendants_override) } + let!(:group) { create(:group, parent: parent_group) } + + it 'returns the expected result' do + expect(group.parent_allows_shared_runners?).to eq(expected_shared_runners_allowed) + end + end + end + + context 'when parent group is missing' do + let!(:group) { create(:group) } + + it 'returns true' do + expect(group.parent_allows_shared_runners?).to be_truthy + end + end + end + + describe '#parent_enabled_shared_runners?' do + subject { group.parent_enabled_shared_runners? } + + context 'when parent group is present' do + context 'When shared Runners are disabled' do + let!(:parent_group) { create(:group, :shared_runners_disabled) } + let!(:group) { create(:group, parent: parent_group) } + + it { is_expected.to be_falsy } + end + + context 'When shared Runners are enabled' do + let!(:parent_group) { create(:group) } + let!(:group) { create(:group, parent: parent_group) } + + it { is_expected.to be_truthy } + end + end + + context 'when parent group is missing' do + let!(:group) { create(:group) } + + it { is_expected.to be_truthy } + end + end + + describe '#enable_shared_runners!' do + subject { group.enable_shared_runners! } + + context 'group that its ancestors have shared runners disabled' do + let_it_be(:parent) { create(:group, :shared_runners_disabled) } + let_it_be(:group) { create(:group, :shared_runners_disabled, parent: parent) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: group) } + + it 'raises error and does not enable shared Runners' do + expect { subject } + .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners disabled for the parent group') + .and not_change { parent.reload.shared_runners_enabled } + .and not_change { group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + + context 'root group with shared runners disabled' do + let_it_be(:group) { create(:group, :shared_runners_disabled) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) } + + it 'enables shared Runners only for itself' do + expect { subject } + .to change { group.reload.shared_runners_enabled }.from(false).to(true) + .and not_change { sub_group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + end + + describe '#disable_shared_runners!' do + let_it_be(:group) { create(:group) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent: group) } + let_it_be(:sub_group_2) { create(:group, parent: group) } + let_it_be(:project) { create(:project, group: group, shared_runners_enabled: true) } + let_it_be(:project_2) { create(:project, group: sub_group_2, shared_runners_enabled: true) } + + subject { group.disable_shared_runners! } + + it 'disables shared Runners for all descendant groups and projects' do + expect { subject } + .to change { group.reload.shared_runners_enabled }.from(true).to(false) + .and not_change { group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { sub_group.reload.shared_runners_enabled } + .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners } + .and change { sub_group_2.reload.shared_runners_enabled }.from(true).to(false) + .and not_change { sub_group_2.reload.allow_descendants_override_disabled_shared_runners } + .and change { project.reload.shared_runners_enabled }.from(true).to(false) + .and change { project_2.reload.shared_runners_enabled }.from(true).to(false) + end + end + + describe '#allow_descendants_override_disabled_shared_runners!' do + subject { group.allow_descendants_override_disabled_shared_runners! } + + context 'top level group' do + let_it_be(:group) { create(:group, :shared_runners_disabled) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) } + + it 'enables allow descendants to override only for itself' do + expect { subject } + .to change { group.reload.allow_descendants_override_disabled_shared_runners }.from(false).to(true) + .and not_change { group.reload.shared_runners_enabled } + .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { sub_group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + + context 'group that its ancestors have shared Runners disabled but allows to override' do + let_it_be(:parent) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners) } + let_it_be(:group) { create(:group, :shared_runners_disabled, parent: parent) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: group) } + + it 'enables allow descendants to override' do + expect { subject } + .to not_change { parent.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { parent.reload.shared_runners_enabled } + .and change { group.reload.allow_descendants_override_disabled_shared_runners }.from(false).to(true) + .and not_change { group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + + context 'when parent does not allow' do + let_it_be(:parent) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false ) } + let_it_be(:group) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent: parent) } + + it 'raises error and does not allow descendants to override' do + expect { subject } + .to raise_error(described_class::UpdateSharedRunnersError, 'Group level shared Runners not allowed') + .and not_change { parent.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { parent.reload.shared_runners_enabled } + .and not_change { group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { group.reload.shared_runners_enabled } + end + end + + context 'top level group that has shared Runners enabled' do + let_it_be(:group) { create(:group, shared_runners_enabled: true) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) } + + it 'raises error and does not change config' do + expect { subject } + .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners enabled') + .and not_change { group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { group.reload.shared_runners_enabled } + .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { sub_group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + end + + describe '#disallow_descendants_override_disabled_shared_runners!' do + subject { group.disallow_descendants_override_disabled_shared_runners! } + + context 'top level group' do + let_it_be(:group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners ) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent: group) } + let_it_be(:project) { create(:project, shared_runners_enabled: true, group: sub_group) } + + it 'disables allow project to override for descendants and disables project shared Runners' do + expect { subject } + .to not_change { group.reload.shared_runners_enabled } + .and change { group.reload.allow_descendants_override_disabled_shared_runners }.from(true).to(false) + .and not_change { sub_group.reload.shared_runners_enabled } + .and change { sub_group.reload.allow_descendants_override_disabled_shared_runners }.from(true).to(false) + .and change { project.reload.shared_runners_enabled }.from(true).to(false) + end + end + + context 'top level group that has shared Runners enabled' do + let_it_be(:group) { create(:group, shared_runners_enabled: true) } + let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) } + let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) } + + it 'results error and does not change config' do + expect { subject } + .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners enabled') + .and not_change { group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { group.reload.shared_runners_enabled } + .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners } + .and not_change { sub_group.reload.shared_runners_enabled } + .and not_change { project.reload.shared_runners_enabled } + end + end + end end diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb index 57eb077031c..975b64cb855 100644 --- a/spec/models/guest_spec.rb +++ b/spec/models/guest_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Guest do +RSpec.describe Guest do let_it_be(:public_project, reload: true) { create(:project, :public) } let_it_be(:private_project) { create(:project, :private) } let_it_be(:internal_project) { create(:project, :internal) } diff --git a/spec/models/hooks/active_hook_filter_spec.rb b/spec/models/hooks/active_hook_filter_spec.rb index 1249c793f7f..1f693ce9fde 100644 --- a/spec/models/hooks/active_hook_filter_spec.rb +++ b/spec/models/hooks/active_hook_filter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ActiveHookFilter do +RSpec.describe ActiveHookFilter do subject(:filter) { described_class.new(hook) } describe '#matches?' do diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb index ccf8171049d..69fbc4c3b4f 100644 --- a/spec/models/hooks/project_hook_spec.rb +++ b/spec/models/hooks/project_hook_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectHook do +RSpec.describe ProjectHook do describe 'associations' do it { is_expected.to belong_to :project } end diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index 936c2fbad27..f7045d7ac5e 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ServiceHook do +RSpec.describe ServiceHook do describe 'associations' do it { is_expected.to belong_to :service } end diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index 2e836c19e3c..e56d08c1847 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe SystemHook do +RSpec.describe SystemHook do context 'default attributes' do let(:system_hook) { build(:system_hook) } diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb index 128601794cf..8dd9cf9e84a 100644 --- a/spec/models/hooks/web_hook_log_spec.rb +++ b/spec/models/hooks/web_hook_log_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WebHookLog do +RSpec.describe WebHookLog do it { is_expected.to belong_to(:web_hook) } it { is_expected.to serialize(:request_headers).as(Hash) } diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 025c11d6407..3fc1ad6eb0d 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WebHook do +RSpec.describe WebHook do let(:hook) { build(:project_hook) } describe 'associations' do diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index 9f120775a3c..696d33b7beb 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Identity do +RSpec.describe Identity do describe 'relations' do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/import_export_upload_spec.rb b/spec/models/import_export_upload_spec.rb index 18a714f4d98..46a611852ab 100644 --- a/spec/models/import_export_upload_spec.rb +++ b/spec/models/import_export_upload_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ImportExportUpload do +RSpec.describe ImportExportUpload do subject { described_class.new(project: create(:project)) } shared_examples 'stores the Import/Export file' do |method| diff --git a/spec/models/import_failure_spec.rb b/spec/models/import_failure_spec.rb index d286a4ad314..cdef125e890 100644 --- a/spec/models/import_failure_spec.rb +++ b/spec/models/import_failure_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ImportFailure do +RSpec.describe ImportFailure do describe 'Scopes' do let_it_be(:project) { create(:project) } let_it_be(:correlation_id) { 'ABC' } diff --git a/spec/models/incident_management/project_incident_management_setting_spec.rb b/spec/models/incident_management/project_incident_management_setting_spec.rb index ac3f97e2d89..effd89e970c 100644 --- a/spec/models/incident_management/project_incident_management_setting_spec.rb +++ b/spec/models/incident_management/project_incident_management_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IncidentManagement::ProjectIncidentManagementSetting do +RSpec.describe IncidentManagement::ProjectIncidentManagementSetting do let_it_be(:project) { create(:project, :repository, create_templates: :issue) } describe 'Associations' do @@ -108,4 +108,42 @@ describe IncidentManagement::ProjectIncidentManagementSetting do it_behaves_like 'no content' end end + + describe '#pagerduty_token' do + let(:active) { true } + + subject do + create(:project_incident_management_setting, project: project, pagerduty_active: active, pagerduty_token: token) + end + + context 'when token already set' do + let(:token) { SecureRandom.hex } + + it 'reads the token' do + expect(subject.pagerduty_token).to eq(token) + expect(subject.encrypted_pagerduty_token).not_to be_nil + expect(subject.encrypted_pagerduty_token_iv).not_to be_nil + end + end + + context 'when not set' do + let(:token) { nil } + + context 'when PagerDuty webhook is active' do + it 'generates a token before validation' do + expect(subject).to be_valid + expect(subject.pagerduty_token).to match(/\A\h{32}\z/) + end + end + + context 'when PagerDuty webhook is not active' do + let(:active) { false } + + it 'does not generate a token before validation' do + expect(subject).to be_valid + expect(subject.pagerduty_token).to be_nil + end + end + end + end end diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb index 747e9dc2faa..383e548c324 100644 --- a/spec/models/instance_configuration_spec.rb +++ b/spec/models/instance_configuration_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe InstanceConfiguration do +RSpec.describe InstanceConfiguration do context 'without cache' do describe '#settings' do describe '#ssh_algorithms_hashes' do diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index 3042fd15a7b..87ba0f3f7e6 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -3,8 +3,9 @@ require 'spec_helper' RSpec.describe Integration do - let(:project_1) { create(:project) } - let(:project_2) { create(:project) } + let!(:project_1) { create(:project) } + let!(:project_2) { create(:project) } + let!(:project_3) { create(:project) } let(:instance_integration) { create(:jira_service, :instance) } before do @@ -18,4 +19,10 @@ RSpec.describe Integration do expect(Project.with_custom_integration_for(instance_integration)).to contain_exactly(project_2) end end + + describe '#ids_without_integration' do + it 'returns projects ids without an integration' do + expect(Project.ids_without_integration(instance_integration, 100)).to contain_exactly(project_3.id) + end + end end diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb index 0dfb59cf43a..751e8724872 100644 --- a/spec/models/internal_id_spec.rb +++ b/spec/models/internal_id_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe InternalId do +RSpec.describe InternalId do let(:project) { create(:project) } let(:usage) { :issues } let(:issue) { build(:issue, project: project) } diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb index dc22d26e2f9..966e4321378 100644 --- a/spec/models/issue/metrics_spec.rb +++ b/spec/models/issue/metrics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Issue::Metrics do +RSpec.describe Issue::Metrics do let(:project) { create(:project) } subject { create(:issue, project: project) } diff --git a/spec/models/issue_assignee_spec.rb b/spec/models/issue_assignee_spec.rb index 2d59ba15101..df8e91cd133 100644 --- a/spec/models/issue_assignee_spec.rb +++ b/spec/models/issue_assignee_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IssueAssignee do +RSpec.describe IssueAssignee do let(:issue) { create(:issue) } subject { issue.issue_assignees.build(assignee: create(:user)) } @@ -15,4 +15,37 @@ describe IssueAssignee do describe 'validations' do it { is_expected.to validate_uniqueness_of(:assignee).scoped_to(:issue_id) } end + + describe 'scopes' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:project_issue) { create(:issue, project: project, assignee_ids: [user.id]) } + + before do + issue.update!(assignee_ids: [user.id]) + end + + context 'in_projects' do + it 'returns issue assignees for given project' do + expect(IssueAssignee.count).to eq 2 + + assignees = IssueAssignee.in_projects([project]) + + expect(assignees.count).to eq 1 + expect(assignees.first.user_id).to eq project_issue.issue_assignees.first.user_id + expect(assignees.first.issue_id).to eq project_issue.issue_assignees.first.issue_id + end + end + + context 'on_issues' do + it 'returns issue assignees for given issues' do + expect(IssueAssignee.count).to eq 2 + + assignees = IssueAssignee.on_issues([project_issue]) + + expect(assignees.count).to eq 1 + expect(assignees.first.issue_id).to eq project_issue.issue_assignees.first.issue_id + end + end + end end diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb index 7fc635f100f..d67bd8debce 100644 --- a/spec/models/issue_collection_spec.rb +++ b/spec/models/issue_collection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IssueCollection do +RSpec.describe IssueCollection do let(:user) { create(:user) } let(:project) { create(:project) } let(:issue1) { create(:issue, project: project) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 291cccd72db..80041d2e859 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Issue do +RSpec.describe Issue do include ExternalAuthorizationServiceHelpers describe "Associations" do @@ -95,29 +95,6 @@ describe Issue do end end - describe 'locking' do - using RSpec::Parameterized::TableSyntax - - where(:lock_version) do - [ - [0], - ["0"] - ] - end - - with_them do - it 'works when an issue has a NULL lock_version' do - issue = create(:issue) - - described_class.where(id: issue.id).update_all('lock_version = NULL') - - issue.update!(lock_version: lock_version, title: 'locking test') - - expect(issue.reload.title).to eq('locking test') - end - end - end - describe '.simple_sorts' do it 'includes all keys' do expect(described_class.simple_sorts.keys).to include( @@ -406,6 +383,22 @@ describe Issue do end end + describe '#from_service_desk?' do + subject { issue.from_service_desk? } + + context 'when issue author is support bot' do + let(:issue) { create(:issue, author: ::User.support_bot) } + + it { is_expected.to be_truthy } + end + + context 'when issue author is not support bot' do + let(:issue) { create(:issue) } + + it { is_expected.to be_falsey } + end + end + describe '#suggested_branch_name' do let(:repository) { double } @@ -1002,6 +995,16 @@ describe Issue do end end + describe '.service_desk' do + it 'returns the service desk issue' do + service_desk_issue = create(:issue, author: ::User.support_bot) + regular_issue = create(:issue) + + expect(described_class.service_desk).to include(service_desk_issue) + expect(described_class.service_desk).not_to include(regular_issue) + end + end + it_behaves_like 'throttled touch' do subject { create(:issue, updated_at: 1.hour.ago) } end diff --git a/spec/models/iteration_spec.rb b/spec/models/iteration_spec.rb index ae14adf9106..ef638330208 100644 --- a/spec/models/iteration_spec.rb +++ b/spec/models/iteration_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Iteration do +RSpec.describe Iteration do let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) } @@ -45,6 +45,14 @@ describe Iteration do it { is_expected.to be_valid } end + context 'when updated iteration dates overlap with its own dates' do + it 'is valid' do + existing_iteration.start_date = 5.days.from_now + + expect(existing_iteration).to be_valid + end + end + context 'when dates overlap' do context 'same group' do context 'when start_date is in range' do @@ -138,6 +146,25 @@ describe Iteration do end end + context 'time scopes' do + let_it_be(:project) { create(:project, :empty_repo) } + let_it_be(:iteration_1) { create(:iteration, :skip_future_date_validation, project: project, start_date: 3.days.ago, due_date: 1.day.from_now) } + let_it_be(:iteration_2) { create(:iteration, :skip_future_date_validation, project: project, start_date: 10.days.ago, due_date: 4.days.ago) } + let_it_be(:iteration_3) { create(:iteration, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) } + + describe 'start_date_passed' do + it 'returns iterations where start_date is in the past but due_date is in the future' do + expect(described_class.start_date_passed).to contain_exactly(iteration_1) + end + end + + describe 'due_date_passed' do + it 'returns iterations where due date is in the past' do + expect(described_class.due_date_passed).to contain_exactly(iteration_2) + end + end + end + describe '.within_timeframe' do let_it_be(:now) { Time.current } let_it_be(:project) { create(:project, :empty_repo) } diff --git a/spec/models/jira_import_state_spec.rb b/spec/models/jira_import_state_spec.rb index d2535636c63..e982b7353ba 100644 --- a/spec/models/jira_import_state_spec.rb +++ b/spec/models/jira_import_state_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe JiraImportState do +RSpec.describe JiraImportState do describe "associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:user) } diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 8cdedbcdedf..1e14864676c 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Key, :mailer do +RSpec.describe Key, :mailer do describe "Associations" do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index 7a179dcb419..a95481f3083 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LabelLink do +RSpec.describe LabelLink do it { expect(build(:label_link)).to be_valid } it { is_expected.to belong_to(:label) } diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb index 34560acfa9e..0bf202ce2b1 100644 --- a/spec/models/label_note_spec.rb +++ b/spec/models/label_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LabelNote do +RSpec.describe LabelNote do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } let_it_be(:label) { create(:label, project: project) } diff --git a/spec/models/label_priority_spec.rb b/spec/models/label_priority_spec.rb index 1a93468290f..db961d5a4e6 100644 --- a/spec/models/label_priority_spec.rb +++ b/spec/models/label_priority_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LabelPriority do +RSpec.describe LabelPriority do describe 'relationships' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:label) } diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index dc878c2d3c0..e1abfd9d8e5 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Label do +RSpec.describe Label do describe 'modules' do it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Subscribable) } diff --git a/spec/models/legacy_diff_discussion_spec.rb b/spec/models/legacy_diff_discussion_spec.rb index 49ea319fbd1..4f90fe7d7f0 100644 --- a/spec/models/legacy_diff_discussion_spec.rb +++ b/spec/models/legacy_diff_discussion_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LegacyDiffDiscussion do +RSpec.describe LegacyDiffDiscussion do subject { create(:legacy_diff_note_on_merge_request).to_discussion } describe '#reply_attributes' do diff --git a/spec/models/lfs_download_object_spec.rb b/spec/models/lfs_download_object_spec.rb index d7522fbb969..d1c323cd177 100644 --- a/spec/models/lfs_download_object_spec.rb +++ b/spec/models/lfs_download_object_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LfsDownloadObject do +RSpec.describe LfsDownloadObject do let(:oid) { 'cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411' } let(:link) { 'http://www.example.com' } let(:size) { 1 } diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb index 0a47ded43fb..d3f79c7c7cf 100644 --- a/spec/models/lfs_file_lock_spec.rb +++ b/spec/models/lfs_file_lock_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LfsFileLock do +RSpec.describe LfsFileLock do let_it_be(:lfs_file_lock, reload: true) { create(:lfs_file_lock) } subject { lfs_file_lock } diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index 09a64dabb08..36d45f17392 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LfsObject do +RSpec.describe LfsObject do context 'scopes' do describe '.not_existing_in_project' do it 'contains only lfs objects not linked to the project' do diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb index 31300828a43..71009a6f28f 100644 --- a/spec/models/lfs_objects_project_spec.rb +++ b/spec/models/lfs_objects_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LfsObjectsProject do +RSpec.describe LfsObjectsProject do let_it_be(:project) { create(:project) } subject do diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb index 7037277e580..515f728f515 100644 --- a/spec/models/license_template_spec.rb +++ b/spec/models/license_template_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe LicenseTemplate do +RSpec.describe LicenseTemplate do describe '#content' do it 'calls a proc exactly once if provided' do content_proc = -> { 'bar' } diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb index bc9124e73af..37158584062 100644 --- a/spec/models/list_spec.rb +++ b/spec/models/list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe List do +RSpec.describe List do it_behaves_like 'having unique enum values' describe 'relationships' do diff --git a/spec/models/list_user_preference_spec.rb b/spec/models/list_user_preference_spec.rb index 10a7bf41f4e..fde0481e301 100644 --- a/spec/models/list_user_preference_spec.rb +++ b/spec/models/list_user_preference_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ListUserPreference do +RSpec.describe ListUserPreference do let_it_be(:user) { create(:user) } let_it_be(:list) { create(:list) } diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 7c40bb24b56..f155c240fb2 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Member do +RSpec.describe Member do include ExclusiveLeaseHelpers using RSpec::Parameterized::TableSyntax @@ -88,6 +88,28 @@ describe Member do expect(child_member).to be_valid end end + + 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 + it 'is valid' do + expect(new_member).to be_valid + end + end + + context 'already member of a project' do + before do + unrelated_project = create(:project) + unrelated_project.add_maintainer(project_bot) + end + + it 'is not valid' do + expect(new_member).not_to be_valid + end + end + end end describe 'Scopes & finders' do diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index fdb71b7ec7d..9af620e70a5 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -2,52 +2,20 @@ require 'spec_helper' -describe GroupMember do +RSpec.describe GroupMember do context 'scopes' do - shared_examples '.count_users_by_group_id' do - it 'counts users by group ID' do - user_1 = create(:user) - user_2 = create(:user) - group_1 = create(:group) - group_2 = create(:group) - - group_1.add_owner(user_1) - group_1.add_owner(user_2) - group_2.add_owner(user_1) - - expect(described_class.count_users_by_group_id).to eq(group_1.id => 2, - group_2.id => 1) - end - end - - describe '.count_users_by_group_id with optimized_count_users_by_group_id feature flag on' do - before do - stub_feature_flags(optimized_count_users_by_group_id: true) - end - - it_behaves_like '.count_users_by_group_id' - - it 'does not JOIN users' do - scope = described_class.all - expect(scope).not_to receive(:joins).with(:user) - - scope.count_users_by_group_id - end - end - - describe '.count_users_by_group_id with optimized_count_users_by_group_id feature flag off' do - before do - stub_feature_flags(optimized_count_users_by_group_id: false) - end - - it_behaves_like '.count_users_by_group_id' - - it 'does JOIN users' do - scope = described_class.all - expect(scope).to receive(:joins).with(:user).and_call_original - - scope.count_users_by_group_id - end + it 'counts users by group ID' do + user_1 = create(:user) + user_2 = create(:user) + group_1 = create(:group) + group_2 = create(:group) + + group_1.add_owner(user_1) + group_1.add_owner(user_2) + group_2.add_owner(user_1) + + expect(described_class.count_users_by_group_id).to eq(group_1.id => 2, + group_2.id => 1) end describe '.of_ldap_type' do diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index fdb9457b211..f25f8933184 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectMember do +RSpec.describe ProjectMember do describe 'associations' do it { is_expected.to belong_to(:project).with_foreign_key(:source_id) } end diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb index bd97cabc11e..4d9e768ecc6 100644 --- a/spec/models/merge_request/metrics_spec.rb +++ b/spec/models/merge_request/metrics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequest::Metrics do +RSpec.describe MergeRequest::Metrics do describe 'associations' do it { is_expected.to belong_to(:merge_request) } it { is_expected.to belong_to(:latest_closed_by).class_name('User') } diff --git a/spec/models/merge_request_assignee_spec.rb b/spec/models/merge_request_assignee_spec.rb index d6aab15d990..d287392bf7f 100644 --- a/spec/models/merge_request_assignee_spec.rb +++ b/spec/models/merge_request_assignee_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestAssignee do +RSpec.describe MergeRequestAssignee do let(:merge_request) { create(:merge_request) } subject { merge_request.merge_request_assignees.build(assignee: create(:user)) } @@ -15,4 +15,26 @@ describe MergeRequestAssignee do describe 'validations' do it { is_expected.to validate_uniqueness_of(:assignee).scoped_to(:merge_request_id) } end + + describe 'scopes' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:project_merge_request) { create(:merge_request, target_project: project, source_project: project, assignee_ids: [user.id]) } + + before do + merge_request.update!(assignee_ids: [user.id]) + end + + context 'in_projects' do + it 'returns issue assignees for given project' do + expect(MergeRequestAssignee.count).to eq 2 + + assignees = MergeRequestAssignee.in_projects([project]) + + expect(assignees.count).to eq 1 + expect(assignees.first.user_id).to eq project_merge_request.merge_request_assignees.first.user_id + expect(assignees.first.merge_request_id).to eq project_merge_request.merge_request_assignees.first.merge_request_id + end + end + end end diff --git a/spec/models/merge_request_context_commit_diff_file_spec.rb b/spec/models/merge_request_context_commit_diff_file_spec.rb index 37d44662326..7a098639b57 100644 --- a/spec/models/merge_request_context_commit_diff_file_spec.rb +++ b/spec/models/merge_request_context_commit_diff_file_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestContextCommitDiffFile do +RSpec.describe MergeRequestContextCommitDiffFile do describe 'associations' do it { is_expected.to belong_to(:merge_request_context_commit) } end diff --git a/spec/models/merge_request_context_commit_spec.rb b/spec/models/merge_request_context_commit_spec.rb index 5a1bf9874ac..29ef2fab9ad 100644 --- a/spec/models/merge_request_context_commit_spec.rb +++ b/spec/models/merge_request_context_commit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestContextCommit do +RSpec.describe MergeRequestContextCommit do let(:merge_request) { create(:merge_request) } let(:project) { merge_request.project } let(:raw_repository) { project.repository.raw_repository } diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb index 62430b08c5c..5ea0145e60f 100644 --- a/spec/models/merge_request_diff_commit_spec.rb +++ b/spec/models/merge_request_diff_commit_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestDiffCommit do +RSpec.describe MergeRequestDiffCommit do let(:merge_request) { create(:merge_request) } let(:project) { merge_request.project } diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb index 40f7be5dc8f..25971f63338 100644 --- a/spec/models/merge_request_diff_file_spec.rb +++ b/spec/models/merge_request_diff_file_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestDiffFile do +RSpec.describe MergeRequestDiffFile do it_behaves_like 'a BulkInsertSafe model', MergeRequestDiffFile do let(:valid_items_for_bulk_insertion) { build_list(:merge_request_diff_file, 10) } let(:invalid_items_for_bulk_insertion) { [] } # class does not have any validations defined diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 0839dde696a..d153ccedf8c 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestDiff do +RSpec.describe MergeRequestDiff do using RSpec::Parameterized::TableSyntax include RepoHelpers @@ -162,7 +162,8 @@ describe MergeRequestDiff do let(:uploader) { ExternalDiffUploader } let(:file_store) { uploader::Store::LOCAL } let(:remote_store) { uploader::Store::REMOTE } - let(:diff) { create(:merge_request).merge_request_diff } + let(:merge_request) { create(:merge_request) } + let(:diff) { merge_request.merge_request_diff } it 'converts from in-database to external file storage' do expect(diff).not_to be_stored_externally @@ -177,6 +178,30 @@ describe MergeRequestDiff do expect(diff.external_diff_store).to eq(file_store) end + it 'safely handles a transaction error when migrating to external storage' do + expect(diff).not_to be_stored_externally + expect(diff.external_diff).not_to be_exists + + stub_external_diffs_setting(enabled: true) + + expect(diff).not_to receive(:save!) + expect(Gitlab::Database) + .to receive(:bulk_insert) + .with('merge_request_diff_files', anything) + .and_raise(ActiveRecord::Rollback) + + expect { diff.migrate_files_to_external_storage! }.not_to change(diff, :merge_request_diff_files) + + diff.reload + + expect(diff).not_to be_stored_externally + + # The diff is written outside of the transaction, which is desirable to + # avoid long transaction times when migrating, but it does mean we can + # leave the file dangling on failure + expect(diff.external_diff).to be_exists + end + it 'converts from in-database to external object storage' do expect(diff).not_to be_stored_externally @@ -209,6 +234,33 @@ describe MergeRequestDiff do diff.migrate_files_to_external_storage! end + + context 'diff adds an empty file' do + let(:project) { create(:project, :test_repo) } + let(:merge_request) do + create( + :merge_request, + source_project: project, + target_project: project, + source_branch: 'empty-file', + target_branch: 'master' + ) + end + + it 'migrates the diff to object storage' do + create_file_in_repo(project, 'master', 'empty-file', 'empty-file', '') + + expect(diff).not_to be_stored_externally + + stub_external_diffs_setting(enabled: true) + stub_external_diffs_object_storage(uploader, direct_upload: true) + + diff.migrate_files_to_external_storage! + + expect(diff).to be_stored_externally + expect(diff.external_diff_store).to eq(remote_store) + end + end end describe '#migrate_files_to_database!' do @@ -476,7 +528,7 @@ describe MergeRequestDiff do include_examples 'merge request diffs' end - describe 'external diffs always enabled' do + describe 'external diffs on disk always enabled' do before do stub_external_diffs_setting(enabled: true, when: 'always') end @@ -484,6 +536,63 @@ describe MergeRequestDiff do include_examples 'merge request diffs' end + describe 'external diffs in object storage always enabled' do + let(:uploader) { ExternalDiffUploader } + let(:remote_store) { uploader::Store::REMOTE } + + subject(:diff) { merge_request.merge_request_diff } + + before do + stub_external_diffs_setting(enabled: true, when: 'always') + stub_external_diffs_object_storage(uploader, direct_upload: true) + end + + # We can't use the full merge request diffs shared examples here because + # reading from the fake object store isn't implemented yet + + context 'empty diff' do + let(:merge_request) { create(:merge_request, :without_diffs) } + + it 'creates an empty diff' do + expect(diff.state).to eq('empty') + expect(diff).not_to be_stored_externally + end + end + + context 'normal diff' do + let(:merge_request) { create(:merge_request) } + + it 'creates a diff in object storage' do + expect(diff).to be_stored_externally + expect(diff.state).to eq('collected') + expect(diff.external_diff_store).to eq(remote_store) + end + end + + context 'diff adding an empty file' do + let(:project) { create(:project, :test_repo) } + let(:merge_request) do + create( + :merge_request, + source_project: project, + target_project: project, + source_branch: 'empty-file', + target_branch: 'master' + ) + end + + it 'creates a diff in object storage' do + create_file_in_repo(project, 'master', 'empty-file', 'empty-file', '') + + diff.reload + + expect(diff).to be_stored_externally + expect(diff.state).to eq('collected') + expect(diff.external_diff_store).to eq(remote_store) + end + end + end + describe 'exernal diffs enabled for outdated diffs' do before do stub_external_diffs_setting(enabled: true, when: 'outdated') diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 582cdc7b419..06febddef0c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequest do +RSpec.describe MergeRequest do include RepoHelpers include ProjectForksHelper include ReactiveCachingHelpers @@ -12,6 +12,8 @@ describe MergeRequest do subject { create(:merge_request) } describe 'associations' do + subject { build_stubbed(:merge_request) } + it { is_expected.to belong_to(:target_project).class_name('Project') } it { is_expected.to belong_to(:source_project).class_name('Project') } it { is_expected.to belong_to(:merge_user).class_name("User") } @@ -55,29 +57,6 @@ describe MergeRequest do end end - describe 'locking' do - using RSpec::Parameterized::TableSyntax - - where(:lock_version) do - [ - [0], - ["0"] - ] - end - - with_them do - it 'works when a merge request has a NULL lock_version' do - merge_request = create(:merge_request) - - described_class.where(id: merge_request.id).update_all('lock_version = NULL') - - merge_request.update!(lock_version: lock_version, title: 'locking test') - - expect(merge_request.reload.title).to eq('locking test') - end - end - end - describe '#squash_in_progress?' do let(:repo_path) do Gitlab::GitalyClient::StorageSettings.allow_disk_access do @@ -195,6 +174,8 @@ describe MergeRequest do end describe 'validation' do + subject { build_stubbed(:merge_request) } + it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:source_branch) } @@ -280,6 +261,21 @@ describe MergeRequest do expect(MergeRequest::Metrics.count).to eq(1) end + + it 'does not create duplicated metrics records when MR is concurrently updated' do + merge_request = create(:merge_request) + + merge_request.metrics.destroy + + instance1 = MergeRequest.find(merge_request.id) + instance2 = MergeRequest.find(merge_request.id) + + instance1.ensure_metrics + instance2.ensure_metrics + + metrics_records = MergeRequest::Metrics.where(merge_request_id: merge_request.id) + expect(metrics_records.size).to eq(1) + end end end @@ -1092,13 +1088,43 @@ describe MergeRequest do end describe "#work_in_progress?" do - ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| + subject { build_stubbed(:merge_request) } + + [ + 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP: [WIP] WIP:', + 'draft:', 'Draft: ', '[Draft]', '[DRAFT] ', 'Draft - ' + ].each do |wip_prefix| it "detects the '#{wip_prefix}' prefix" do subject.title = "#{wip_prefix}#{subject.title}" + expect(subject.work_in_progress?).to eq true end end + it "detects merge request title just saying 'wip'" do + subject.title = "wip" + + expect(subject.work_in_progress?).to eq true + end + + it "detects merge request title just saying 'draft'" do + subject.title = "draft" + + expect(subject.work_in_progress?).to eq true + end + + it 'does not detect WIP in the middle of the title' do + subject.title = 'Something with WIP in the middle' + + expect(subject.work_in_progress?).to eq false + end + + it 'does not detect Draft in the middle of the title' do + subject.title = 'Something with Draft in the middle' + + expect(subject.work_in_progress?).to eq false + end + it "doesn't detect WIP for words starting with WIP" do subject.title = "Wipwap #{subject.title}" expect(subject.work_in_progress?).to eq false @@ -1115,7 +1141,12 @@ describe MergeRequest do end describe "#wipless_title" do - ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| + subject { build_stubbed(:merge_request) } + + [ + 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP: [WIP] WIP:', + 'draft:', 'Draft: ', '[Draft]', '[DRAFT] ', 'Draft - ' + ].each do |wip_prefix| it "removes the '#{wip_prefix}' prefix" do wipless_title = subject.title subject.title = "#{wip_prefix}#{subject.title}" @@ -1133,14 +1164,14 @@ describe MergeRequest do end describe "#wip_title" do - it "adds the WIP: prefix to the title" do - wip_title = "WIP: #{subject.title}" + it "adds the Draft: prefix to the title" do + wip_title = "Draft: #{subject.title}" expect(subject.wip_title).to eq wip_title end - it "does not add the WIP: prefix multiple times" do - wip_title = "WIP: #{subject.title}" + it "does not add the Draft: prefix multiple times" do + wip_title = "Draft: #{subject.title}" subject.title = subject.wip_title subject.title = subject.wip_title @@ -1170,6 +1201,12 @@ describe MergeRequest do expect(subject.can_remove_source_branch?(user)).to be_falsey end + it "can't be removed because source project has been deleted" do + subject.source_project = nil + + expect(subject.can_remove_source_branch?(user)).to be_falsey + end + it "can't remove a root ref" do subject.update(source_branch: 'master', target_branch: 'feature') @@ -1196,6 +1233,29 @@ describe MergeRequest do end end + describe "#source_branch_exists?" do + let(:merge_request) { subject } + let(:repository) { merge_request.source_project.repository } + + context 'when the source project is set' do + it 'memoizes the value and returns the result' do + expect(repository).to receive(:branch_exists?).once.with(merge_request.source_branch).and_return(true) + + 2.times { expect(merge_request.source_branch_exists?).to eq(true) } + end + end + + context 'when the source project is not set' do + before do + merge_request.source_project = nil + end + + it 'returns false' do + expect(merge_request.source_branch_exists?).to eq(false) + end + end + end + describe '#default_merge_commit_message' do it 'includes merge information as the title' do request = build(:merge_request, source_branch: 'source', target_branch: 'target') @@ -2426,7 +2486,7 @@ describe MergeRequest do context 'when working in progress' do before do - subject.title = 'WIP MR' + subject.title = '[Draft] MR' end it 'returns false' do diff --git a/spec/models/metrics/dashboard/annotation_spec.rb b/spec/models/metrics/dashboard/annotation_spec.rb index 3cba31ffdfe..bd4baeb8851 100644 --- a/spec/models/metrics/dashboard/annotation_spec.rb +++ b/spec/models/metrics/dashboard/annotation_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Metrics::Dashboard::Annotation do +RSpec.describe Metrics::Dashboard::Annotation do using RSpec::Parameterized::TableSyntax describe 'associations' do diff --git a/spec/models/metrics/users_starred_dashboard_spec.rb b/spec/models/metrics/users_starred_dashboard_spec.rb index 6cb14ae569e..c89344c0a1c 100644 --- a/spec/models/metrics/users_starred_dashboard_spec.rb +++ b/spec/models/metrics/users_starred_dashboard_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Metrics::UsersStarredDashboard do +RSpec.describe Metrics::UsersStarredDashboard do describe 'associations' do it { is_expected.to belong_to(:project).inverse_of(:metrics_users_starred_dashboards) } it { is_expected.to belong_to(:user).inverse_of(:metrics_users_starred_dashboards) } diff --git a/spec/models/milestone_note_spec.rb b/spec/models/milestone_note_spec.rb index aad65cf0346..db1a7ca05f8 100644 --- a/spec/models/milestone_note_spec.rb +++ b/spec/models/milestone_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MilestoneNote do +RSpec.describe MilestoneNote do describe '.from_event' do let(:author) { create(:user) } let(:project) { create(:project, :repository) } @@ -11,9 +11,7 @@ describe MilestoneNote do subject { described_class.from_event(event, resource: noteable, resource_parent: project) } - it_behaves_like 'a system note', exclude_project: true do - let(:action) { 'milestone' } - end + it_behaves_like 'a synthetic note', 'milestone' context 'with a remove milestone event' do let(:milestone) { create(:milestone) } diff --git a/spec/models/milestone_release_spec.rb b/spec/models/milestone_release_spec.rb index 28cec7bbc17..3c781545d8a 100644 --- a/spec/models/milestone_release_spec.rb +++ b/spec/models/milestone_release_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MilestoneRelease do +RSpec.describe MilestoneRelease do let(:project) { create(:project) } let(:release) { create(:release, project: project) } let(:milestone) { create(:milestone, project: project) } diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 33f84da27f6..b52b035e130 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Milestone do +RSpec.describe Milestone do it_behaves_like 'a timebox', :milestone describe 'MilestoneStruct#serializable_hash' do diff --git a/spec/models/namespace/root_storage_size_spec.rb b/spec/models/namespace/root_storage_size_spec.rb deleted file mode 100644 index a8048b7f637..00000000000 --- a/spec/models/namespace/root_storage_size_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Namespace::RootStorageSize, type: :model do - let(:namespace) { create(:namespace) } - let(:current_size) { 50.megabytes } - let(:limit) { 100 } - let(:model) { described_class.new(namespace) } - let(:create_statistics) { create(:namespace_root_storage_statistics, namespace: namespace, storage_size: current_size)} - - before do - create_statistics - - stub_application_setting(namespace_storage_size_limit: limit) - end - - describe '#above_size_limit?' do - subject { model.above_size_limit? } - - context 'when limit is 0' do - let(:limit) { 0 } - - it { is_expected.to eq(false) } - end - - context 'when below limit' do - it { is_expected.to eq(false) } - end - - context 'when above limit' do - let(:current_size) { 101.megabytes } - - it { is_expected.to eq(true) } - end - end - - describe '#usage_ratio' do - subject { model.usage_ratio } - - it { is_expected.to eq(0.5) } - - context 'when limit is 0' do - let(:limit) { 0 } - - it { is_expected.to eq(0) } - end - - context 'when there are no root_storage_statistics' do - let(:create_statistics) { nil } - - it { is_expected.to eq(0) } - end - end - - describe '#current_size' do - subject { model.current_size } - - it { is_expected.to eq(current_size) } - end - - describe '#limit' do - subject { model.limit } - - it { is_expected.to eq(limit.megabytes) } - end -end diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index 9e12831a704..ce6f875ee09 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -43,6 +43,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do total_build_artifacts_size = stat1.build_artifacts_size + stat2.build_artifacts_size total_packages_size = stat1.packages_size + stat2.packages_size total_storage_size = stat1.storage_size + stat2.storage_size + total_snippets_size = stat1.snippets_size + stat2.snippets_size expect(root_storage_statistics.repository_size).to eq(total_repository_size) expect(root_storage_statistics.wiki_size).to eq(total_wiki_size) @@ -50,6 +51,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do expect(root_storage_statistics.build_artifacts_size).to eq(total_build_artifacts_size) expect(root_storage_statistics.packages_size).to eq(total_packages_size) expect(root_storage_statistics.storage_size).to eq(total_storage_size) + expect(root_storage_statistics.snippets_size).to eq(total_snippets_size) end it 'works when there are no projects' do @@ -64,10 +66,20 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do expect(root_storage_statistics.build_artifacts_size).to eq(0) expect(root_storage_statistics.packages_size).to eq(0) expect(root_storage_statistics.storage_size).to eq(0) + expect(root_storage_statistics.snippets_size).to eq(0) + end + end + + shared_examples 'does not include personal snippets' do + specify do + expect(root_storage_statistics).not_to receive(:from_personal_snippets) + + root_storage_statistics.recalculate! end end it_behaves_like 'data refresh' + it_behaves_like 'does not include personal snippets' context 'with subgroups' do let(:subgroup1) { create(:group, parent: namespace)} @@ -77,12 +89,45 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do let(:project2) { create(:project, namespace: subgroup2) } it_behaves_like 'data refresh' + it_behaves_like 'does not include personal snippets' end context 'with a personal namespace' do - let(:namespace) { create(:user).namespace } + let_it_be(:user) { create(:user) } + let(:namespace) { user.namespace } it_behaves_like 'data refresh' + + context 'when user has personal snippets' do + let(:total_project_snippets_size) { stat1.snippets_size + stat2.snippets_size } + + it 'aggregates personal and project snippets size' do + # This is just a a snippet authored by other user + # to ensure we only pick snippets from the namespace + # user + create(:personal_snippet, :repository).statistics.refresh! + + snippets = create_list(:personal_snippet, 3, :repository, author: user) + snippets.each { |s| s.statistics.refresh! } + + total_personal_snippets_size = snippets.map { |s| s.statistics.repository_size }.sum + + root_storage_statistics.recalculate! + + expect(root_storage_statistics.snippets_size).to eq(total_personal_snippets_size + total_project_snippets_size) + end + + context 'when personal snippets do not have statistics' do + it 'does not raise any error' do + snippets = create_list(:personal_snippet, 2, :repository, author: user) + snippets.last.statistics.refresh! + + root_storage_statistics.recalculate! + + expect(root_storage_statistics.snippets_size).to eq(total_project_snippets_size + snippets.last.statistics.repository_size) + end + end + end end end end diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb new file mode 100644 index 00000000000..71b0e974106 --- /dev/null +++ b/spec/models/namespace/traversal_hierarchy_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Namespace::TraversalHierarchy, type: :model do + let_it_be(:root, reload: true) { create(:namespace, :with_hierarchy) } + + describe '.for_namespace' do + let(:hierarchy) { described_class.for_namespace(namespace) } + + context 'with root group' do + let(:namespace) { root } + + it { expect(hierarchy.root).to eq root } + end + + context 'with child group' do + let(:namespace) { root.children.first.children.first } + + it { expect(hierarchy.root).to eq root } + end + + context 'with group outside of hierarchy' do + let(:namespace) { create(:namespace) } + + it { expect(hierarchy.root).not_to eq root } + end + end + + describe '.new' do + let(:hierarchy) { described_class.new(namespace) } + + context 'with root group' do + let(:namespace) { root } + + it { expect(hierarchy.root).to eq root } + end + + context 'with child group' do + let(:namespace) { root.children.first } + + it { expect { hierarchy }.to raise_error(StandardError, 'Must specify a root node') } + end + end + + describe '#incorrect_traversal_ids' do + subject { described_class.new(root).incorrect_traversal_ids } + + it { is_expected.to match_array Namespace.all } + end + + describe '#sync_traversal_ids!' do + let(:hierarchy) { described_class.new(root) } + + before do + hierarchy.sync_traversal_ids! + root.reload + end + + it_behaves_like 'hierarchy with traversal_ids' + it { expect(hierarchy.incorrect_traversal_ids).to be_empty } + end +end diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb new file mode 100644 index 00000000000..257d78dfa2c --- /dev/null +++ b/spec/models/namespace_setting_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe NamespaceSetting, type: :model do + it { is_expected.to belong_to(:namespace) } +end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index ed7ef8b2b8e..ad4c8448745 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Namespace do +RSpec.describe Namespace do include ProjectForksHelper include GitHelpers @@ -17,6 +17,8 @@ describe Namespace do it { is_expected.to have_many :children } it { is_expected.to have_one :root_storage_statistics } it { is_expected.to have_one :aggregation_schedule } + it { is_expected.to have_one :namespace_settings } + it { is_expected.to have_many :custom_emoji } end describe 'validations' do @@ -64,6 +66,36 @@ describe Namespace do it { expect(group).to be_valid } end end + + describe '1 char path length' do + it 'does not allow to create one' do + namespace = build(:namespace, path: 'j') + + expect(namespace).not_to be_valid + expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)') + end + + it 'does not allow to update one' do + namespace = create(:namespace) + namespace.update(path: 'j') + + expect(namespace).not_to be_valid + expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)') + end + + it 'allows updating other attributes for existing record' do + namespace = build(:namespace, path: 'j') + namespace.save(validate: false) + namespace.reload + + expect(namespace.path).to eq('j') + + namespace.update(name: 'something new') + + expect(namespace).to be_valid + expect(namespace.name).to eq('something new') + end + end end describe 'delegate' do @@ -153,7 +185,8 @@ describe Namespace do wiki_size: 505, lfs_objects_size: 202, build_artifacts_size: 303, - packages_size: 404)) + packages_size: 404, + snippets_size: 605)) end let(:project2) do @@ -164,7 +197,8 @@ describe Namespace do wiki_size: 50, lfs_objects_size: 20, build_artifacts_size: 30, - packages_size: 40)) + packages_size: 40, + snippets_size: 60)) end it "sums all project storage counters in the namespace" do @@ -172,12 +206,13 @@ describe Namespace do project2 statistics = described_class.with_statistics.find(namespace.id) - expect(statistics.storage_size).to eq 1665 + expect(statistics.storage_size).to eq 2330 expect(statistics.repository_size).to eq 111 expect(statistics.wiki_size).to eq 555 expect(statistics.lfs_objects_size).to eq 222 expect(statistics.build_artifacts_size).to eq 333 expect(statistics.packages_size).to eq 444 + expect(statistics.snippets_size).to eq 665 end it "correctly handles namespaces without projects" do @@ -189,6 +224,7 @@ describe Namespace do expect(statistics.lfs_objects_size).to eq 0 expect(statistics.build_artifacts_size).to eq 0 expect(statistics.packages_size).to eq 0 + expect(statistics.snippets_size).to eq 0 end end @@ -849,8 +885,13 @@ describe Namespace do end describe '#root_ancestor' do + let!(:root_group) { create(:group) } + + it 'returns root_ancestor for root group without a query' do + expect { root_group.root_ancestor }.not_to exceed_query_limit(0) + end + it 'returns the top most ancestor' do - root_group = create(:group) nested_group = create(:group, parent: root_group) deep_nested_group = create(:group, parent: nested_group) very_deep_nested_group = create(:group, parent: deep_nested_group) diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb index 232172fde76..a393aace39c 100644 --- a/spec/models/network/graph_spec.rb +++ b/spec/models/network/graph_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Network::Graph do +RSpec.describe Network::Graph do let(:project) { create(:project, :repository) } let!(:note_on_commit) { create(:note_on_commit, project: project) } diff --git a/spec/models/note_diff_file_spec.rb b/spec/models/note_diff_file_spec.rb index 11108016b8e..1ece1dfea59 100644 --- a/spec/models/note_diff_file_spec.rb +++ b/spec/models/note_diff_file_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe NoteDiffFile do +RSpec.describe NoteDiffFile do describe 'associations' do it { is_expected.to belong_to(:diff_note) } end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index af3fdcfaa2e..e6e6a8c35cf 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Note do +RSpec.describe Note do include RepoHelpers describe 'associations' do diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb index 05aeafaa4d4..8429f577dc6 100644 --- a/spec/models/notification_recipient_spec.rb +++ b/spec/models/notification_recipient_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe NotificationRecipient do +RSpec.describe NotificationRecipient do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } let(:target) { create(:issue, project: project) } diff --git a/spec/models/oauth_access_grant_spec.rb b/spec/models/oauth_access_grant_spec.rb index 955dae906f3..ca67944752d 100644 --- a/spec/models/oauth_access_grant_spec.rb +++ b/spec/models/oauth_access_grant_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe OauthAccessGrant do +RSpec.describe OauthAccessGrant do let(:user) { create(:user) } let(:application) { create(:oauth_application, owner: user) } diff --git a/spec/models/oauth_access_token_spec.rb b/spec/models/oauth_access_token_spec.rb index 0a1c576a5e7..65a7f6410cf 100644 --- a/spec/models/oauth_access_token_spec.rb +++ b/spec/models/oauth_access_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe OauthAccessToken do +RSpec.describe OauthAccessToken do let(:user) { create(:user) } let(:app_one) { create(:oauth_application) } let(:app_two) { create(:oauth_application) } diff --git a/spec/models/packages/composer/metadatum_spec.rb b/spec/models/packages/composer/metadatum_spec.rb new file mode 100644 index 00000000000..ae53532696b --- /dev/null +++ b/spec/models/packages/composer/metadatum_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Composer::Metadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:package) } + it { is_expected.to validate_presence_of(:target_sha) } + it { is_expected.to validate_presence_of(:composer_json) } + end +end diff --git a/spec/models/packages/conan/file_metadatum_spec.rb b/spec/models/packages/conan/file_metadatum_spec.rb new file mode 100644 index 00000000000..a66a2813196 --- /dev/null +++ b/spec/models/packages/conan/file_metadatum_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Conan::FileMetadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package_file) } + end + + describe 'validations' do + let(:package_file) { create(:conan_package_file, :conan_recipe_file) } + + it { is_expected.to validate_presence_of(:package_file) } + it { is_expected.to validate_presence_of(:recipe_revision) } + + describe '#recipe_revision' do + it { is_expected.to allow_value("0").for(:recipe_revision) } + it { is_expected.not_to allow_value(nil).for(:recipe_revision) } + end + + describe '#package_revision_for_package_file' do + context 'recipe file' do + let(:conan_file_metadatum) { build(:conan_file_metadatum, :recipe_file, package_file: package_file) } + + it 'is valid with empty value' do + conan_file_metadatum.package_revision = nil + + expect(conan_file_metadatum).to be_valid + end + + it 'is invalid with value' do + conan_file_metadatum.package_revision = '0' + + expect(conan_file_metadatum).to be_invalid + end + end + + context 'package file' do + let(:conan_file_metadatum) { build(:conan_file_metadatum, :package_file, package_file: package_file) } + + it 'is valid with default value' do + conan_file_metadatum.package_revision = '0' + + expect(conan_file_metadatum).to be_valid + end + + it 'is invalid with non-default value' do + conan_file_metadatum.package_revision = 'foo' + + expect(conan_file_metadatum).to be_invalid + end + end + end + + describe '#conan_package_reference_for_package_file' do + context 'recipe file' do + let(:conan_file_metadatum) { build(:conan_file_metadatum, :recipe_file, package_file: package_file) } + + it 'is valid with empty value' do + conan_file_metadatum.conan_package_reference = nil + + expect(conan_file_metadatum).to be_valid + end + + it 'is invalid with value' do + conan_file_metadatum.conan_package_reference = '123456789' + + expect(conan_file_metadatum).to be_invalid + end + end + + context 'package file' do + let(:conan_file_metadatum) { build(:conan_file_metadatum, :package_file, package_file: package_file) } + + it 'is valid with acceptable value' do + conan_file_metadatum.conan_package_reference = '123456asdf' + + expect(conan_file_metadatum).to be_valid + end + + it 'is invalid with invalid value' do + conan_file_metadatum.conan_package_reference = 'foo@bar' + + expect(conan_file_metadatum).to be_invalid + end + + it 'is invalid when nil' do + conan_file_metadatum.conan_package_reference = nil + + expect(conan_file_metadatum).to be_invalid + end + end + end + + describe '#conan_package_type' do + it 'validates package of type conan' do + package = build('package') + package_file = build('package_file', package: package) + conan_file_metadatum = build('conan_file_metadatum', package_file: package_file) + + expect(conan_file_metadatum).not_to be_valid + expect(conan_file_metadatum.errors.to_a).to contain_exactly('Package type must be Conan') + end + end + end +end diff --git a/spec/models/packages/conan/metadatum_spec.rb b/spec/models/packages/conan/metadatum_spec.rb new file mode 100644 index 00000000000..112f395818b --- /dev/null +++ b/spec/models/packages/conan/metadatum_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Conan::Metadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package) } + end + + describe 'validations' do + let(:fifty_one_characters) { 'f_a' * 17} + + it { is_expected.to validate_presence_of(:package) } + it { is_expected.to validate_presence_of(:package_username) } + it { is_expected.to validate_presence_of(:package_channel) } + + describe '#package_username' do + it { is_expected.to allow_value("my-package+username").for(:package_username) } + it { is_expected.to allow_value("my_package.username").for(:package_username) } + it { is_expected.to allow_value("_my-package.username123").for(:package_username) } + it { is_expected.to allow_value("my").for(:package_username) } + it { is_expected.not_to allow_value('+my_package').for(:package_username) } + it { is_expected.not_to allow_value('.my_package').for(:package_username) } + it { is_expected.not_to allow_value('-my_package').for(:package_username) } + it { is_expected.not_to allow_value('m').for(:package_username) } + it { is_expected.not_to allow_value(fifty_one_characters).for(:package_username) } + it { is_expected.not_to allow_value("my/package").for(:package_username) } + it { is_expected.not_to allow_value("my(package)").for(:package_username) } + it { is_expected.not_to allow_value("my@package").for(:package_username) } + end + + describe '#package_channel' do + it { is_expected.to allow_value("beta").for(:package_channel) } + it { is_expected.to allow_value("stable+1.0").for(:package_channel) } + it { is_expected.to allow_value("my").for(:package_channel) } + it { is_expected.to allow_value("my_channel.beta").for(:package_channel) } + it { is_expected.to allow_value("_my-channel.beta123").for(:package_channel) } + it { is_expected.not_to allow_value('+my_channel').for(:package_channel) } + it { is_expected.not_to allow_value('.my_channel').for(:package_channel) } + it { is_expected.not_to allow_value('-my_channel').for(:package_channel) } + it { is_expected.not_to allow_value('m').for(:package_channel) } + it { is_expected.not_to allow_value(fifty_one_characters).for(:package_channel) } + it { is_expected.not_to allow_value("my/channel").for(:package_channel) } + it { is_expected.not_to allow_value("my(channel)").for(:package_channel) } + it { is_expected.not_to allow_value("my@channel").for(:package_channel) } + end + + describe '#conan_package_type' do + it 'will not allow a package with a different package_type' do + package = build('package') + conan_metadatum = build('conan_metadatum', package: package) + + expect(conan_metadatum).not_to be_valid + expect(conan_metadatum.errors.to_a).to include('Package type must be Conan') + end + end + end + + describe '#recipe' do + let(:package) { create(:conan_package) } + + it 'returns the recipe' do + expect(package.conan_recipe).to eq("#{package.name}/#{package.version}@#{package.conan_metadatum.package_username}/#{package.conan_metadatum.package_channel}") + end + end + + describe '#recipe_url' do + let(:package) { create(:conan_package) } + + it 'returns the recipe url' do + expect(package.conan_recipe_path).to eq("#{package.name}/#{package.version}/#{package.conan_metadatum.package_username}/#{package.conan_metadatum.package_channel}") + end + end + + describe '.package_username_from' do + let(:full_path) { 'foo/bar/baz-buz' } + + it 'returns the username formatted package path' do + expect(described_class.package_username_from(full_path: full_path)).to eq('foo+bar+baz-buz') + end + end + + describe '.full_path_from' do + let(:username) { 'foo+bar+baz-buz' } + + it 'returns the username formatted package path' do + expect(described_class.full_path_from(package_username: username)).to eq('foo/bar/baz-buz') + end + end +end diff --git a/spec/models/packages/dependency_link_spec.rb b/spec/models/packages/dependency_link_spec.rb new file mode 100644 index 00000000000..d8fde8f5eb3 --- /dev/null +++ b/spec/models/packages/dependency_link_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::DependencyLink, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package).inverse_of(:dependency_links) } + it { is_expected.to belong_to(:dependency).inverse_of(:dependency_links) } + it { is_expected.to have_one(:nuget_metadatum).inverse_of(:dependency_link) } + end + + describe 'validations' do + subject { create(:packages_dependency_link) } + + it { is_expected.to validate_presence_of(:package) } + it { is_expected.to validate_presence_of(:dependency) } + + context 'package_id and package_dependency_id uniqueness for dependency_type' do + it 'is not valid' do + exisiting_link = subject + link = build( + :packages_dependency_link, + package: exisiting_link.package, + dependency: exisiting_link.dependency, + dependency_type: exisiting_link.dependency_type + ) + + expect(link).not_to be_valid + expect(link.errors.to_a).to include("Dependency type has already been taken") + end + end + end + + context 'with multiple links' do + let_it_be(:link1) { create(:packages_dependency_link) } + let_it_be(:link2) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :devDependencies) } + let_it_be(:link3) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :bundleDependencies) } + + subject { described_class } + + describe '.with_dependency_type' do + it 'returns links of the given type' do + expect(subject.with_dependency_type(:bundleDependencies)).to eq([link3]) + end + end + + describe '.for_package' do + let_it_be(:link1) { create(:packages_dependency_link) } + let_it_be(:link2) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :devDependencies) } + let_it_be(:link3) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :bundleDependencies) } + + it 'returns the link for the given package' do + expect(subject.for_package(link1.package)).to eq([link1]) + end + end + end +end diff --git a/spec/models/packages/dependency_spec.rb b/spec/models/packages/dependency_spec.rb new file mode 100644 index 00000000000..fa6b0fd1848 --- /dev/null +++ b/spec/models/packages/dependency_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Dependency, type: :model do + describe 'relationships' do + it { is_expected.to have_many(:dependency_links) } + end + + describe 'validations' do + subject { create(:packages_dependency) } + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_presence_of(:version_pattern) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to(:version_pattern) } + end + + describe '.ids_for_package_names_and_version_patterns' do + let_it_be(:package_dependency1) { create(:packages_dependency, name: 'foo', version_pattern: '~1.0.0') } + let_it_be(:package_dependency2) { create(:packages_dependency, name: 'bar', version_pattern: '~2.5.0') } + let_it_be(:expected_ids) { [package_dependency1.id, package_dependency2.id] } + let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2) } + let(:chunk_size) { 50 } + let(:rows_limit) { 50 } + + subject { Packages::Dependency.ids_for_package_names_and_version_patterns(names_and_version_patterns, chunk_size, rows_limit) } + + it { is_expected.to match_array(expected_ids) } + + context 'with unknown names' do + let(:names_and_version_patterns) { { unknown: '~1.0.0' } } + + it { is_expected.to be_empty } + end + + context 'with unknown version patterns' do + let(:names_and_version_patterns) { { 'foo' => '~1.0.0beta' } } + + it { is_expected.to be_empty } + end + + context 'with a name bigger than column size' do + let_it_be(:big_name) { 'a' * (Packages::Dependency::MAX_STRING_LENGTH + 1) } + let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2).merge(big_name => '~1.0.0') } + + it { is_expected.to match_array(expected_ids) } + end + + context 'with a version pattern bigger than column size' do + let_it_be(:big_version_pattern) { 'a' * (Packages::Dependency::MAX_STRING_LENGTH + 1) } + let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2).merge('test' => big_version_pattern) } + + it { is_expected.to match_array(expected_ids) } + end + + context 'with too big parameter' do + let(:size) { (Packages::Dependency::MAX_CHUNKED_QUERIES_COUNT * chunk_size) + 1 } + let(:names_and_version_patterns) { Hash[(1..size).map { |v| [v, v] }] } + + it { expect { subject }.to raise_error(ArgumentError, 'Too many names_and_version_patterns') } + end + + context 'with parameters size' do + let_it_be(:package_dependency3) { create(:packages_dependency, name: 'foo3', version_pattern: '~1.5.3') } + let_it_be(:package_dependency4) { create(:packages_dependency, name: 'foo4', version_pattern: '~1.5.4') } + let_it_be(:package_dependency5) { create(:packages_dependency, name: 'foo5', version_pattern: '~1.5.5') } + let_it_be(:package_dependency6) { create(:packages_dependency, name: 'foo6', version_pattern: '~1.5.6') } + let_it_be(:package_dependency7) { create(:packages_dependency, name: 'foo7', version_pattern: '~1.5.7') } + let(:expected_ids) { [package_dependency1.id, package_dependency2.id, package_dependency3.id, package_dependency4.id, package_dependency5.id, package_dependency6.id, package_dependency7.id] } + let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2, package_dependency3, package_dependency4, package_dependency5, package_dependency6, package_dependency7) } + + context 'above the chunk size' do + let(:chunk_size) { 2 } + + it { is_expected.to match_array(expected_ids) } + end + + context 'selecting too many rows' do + let(:rows_limit) { 2 } + + it { expect { subject }.to raise_error(ArgumentError, 'Too many Dependencies selected') } + end + end + end + + describe '.for_package_names_and_version_patterns' do + let_it_be(:package_dependency1) { create(:packages_dependency, name: 'foo', version_pattern: '~1.0.0') } + let_it_be(:package_dependency2) { create(:packages_dependency, name: 'bar', version_pattern: '~2.5.0') } + let_it_be(:expected_array) { [package_dependency1, package_dependency2] } + let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2) } + + subject { Packages::Dependency.for_package_names_and_version_patterns(names_and_version_patterns) } + + it { is_expected.to match_array(expected_array) } + + context 'with unknown names' do + let(:names_and_version_patterns) { { unknown: '~1.0.0' } } + + it { is_expected.to be_empty } + end + + context 'with unknown version patterns' do + let(:names_and_version_patterns) { { 'foo' => '~1.0.0beta' } } + + it { is_expected.to be_empty } + end + end + + def build_names_and_version_patterns(*package_dependencies) + result = Hash.new { |h, dependency| h[dependency.name] = dependency.version_pattern } + package_dependencies.each { |dependency| result[dependency] } + result + end +end diff --git a/spec/models/packages/go/module_spec.rb b/spec/models/packages/go/module_spec.rb new file mode 100644 index 00000000000..03af4cf4b70 --- /dev/null +++ b/spec/models/packages/go/module_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Go::Module, type: :model do + before do + stub_feature_flags(go_proxy_disable_gomod_validation: false) + end + + describe '#path_valid?' do + context 'with root path' do + let_it_be(:package) { create(:go_module) } + + context 'with major version 0' do + it('returns true') { expect(package.path_valid?(0)).to eq(true) } + end + + context 'with major version 1' do + it('returns true') { expect(package.path_valid?(1)).to eq(true) } + end + + context 'with major version 2' do + it('returns false') { expect(package.path_valid?(2)).to eq(false) } + end + end + + context 'with path ./v2' do + let_it_be(:package) { create(:go_module, path: '/v2') } + + context 'with major version 0' do + it('returns false') { expect(package.path_valid?(0)).to eq(false) } + end + + context 'with major version 1' do + it('returns false') { expect(package.path_valid?(1)).to eq(false) } + end + + context 'with major version 2' do + it('returns true') { expect(package.path_valid?(2)).to eq(true) } + end + end + end + + describe '#gomod_valid?' do + let_it_be(:package) { create(:go_module) } + + context 'with good gomod' do + it('returns true') { expect(package.gomod_valid?("module #{package.name}")).to eq(true) } + end + + context 'with bad gomod' do + it('returns false') { expect(package.gomod_valid?("module #{package.name}/v2")).to eq(false) } + end + + context 'with empty gomod' do + it('returns false') { expect(package.gomod_valid?("")).to eq(false) } + end + end +end diff --git a/spec/models/packages/go/module_version_spec.rb b/spec/models/packages/go/module_version_spec.rb new file mode 100644 index 00000000000..c4c6a07d9e9 --- /dev/null +++ b/spec/models/packages/go/module_version_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Go::ModuleVersion, type: :model do + let_it_be(:user) { create :user } + let_it_be(:project) { create :project_empty_repo, creator: user, path: 'my-go-lib' } + let_it_be(:mod) { create :go_module, project: project } + + before :all do + create :go_module_commit, :files, project: project, tag: 'v1.0.0', files: { 'README.md' => 'Hi' } + create :go_module_commit, :module, project: project, tag: 'v1.0.1' + create :go_module_commit, :package, project: project, tag: 'v1.0.2', path: 'pkg' + create :go_module_commit, :module, project: project, tag: 'v1.0.3', name: 'mod' + create :go_module_commit, :files, project: project, files: { 'y.go' => "package a\n" } + create :go_module_commit, :module, project: project, name: 'v2' + create :go_module_commit, :files, project: project, tag: 'v2.0.0', files: { 'v2/x.go' => "package a\n" } + end + + shared_examples '#files' do |desc, *entries| + it "returns #{desc}" do + actual = version.files.map { |x| x }.to_set + expect(actual).to eq(entries.to_set) + end + end + + shared_examples '#archive' do |desc, *entries| + it "returns an archive of #{desc}" do + expected = entries.map { |e| "#{version.full_name}/#{e}" }.to_set + + actual = Set[] + Zip::InputStream.open(StringIO.new(version.archive.string)) do |zip| + while (entry = zip.get_next_entry) + actual.add(entry.name) + end + end + + expect(actual).to eq(expected) + end + end + + describe '#name' do + context 'with ref and name specified' do + let_it_be(:version) { create :go_module_version, mod: mod, name: 'foobar', commit: project.repository.head_commit, ref: project.repository.find_tag('v1.0.0') } + it('returns that name') { expect(version.name).to eq('foobar') } + end + + context 'with ref specified and name unspecified' do + let_it_be(:version) { create :go_module_version, mod: mod, commit: project.repository.head_commit, ref: project.repository.find_tag('v1.0.0') } + it('returns the name of the ref') { expect(version.name).to eq('v1.0.0') } + end + + context 'with ref and name unspecified' do + let_it_be(:version) { create :go_module_version, mod: mod, commit: project.repository.head_commit } + it('returns nil') { expect(version.name).to eq(nil) } + end + end + + describe '#gomod' do + context 'with go.mod missing' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.0' } + it('returns nil') { expect(version.gomod).to eq(nil) } + end + + context 'with go.mod present' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.1' } + it('returns the contents of go.mod') { expect(version.gomod).to eq("module #{mod.name}\n") } + end + end + + describe '#files' do + context 'with a root module' do + context 'with an empty module path' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.2' } + it_behaves_like '#files', 'all the files', 'README.md', 'go.mod', 'a.go', 'pkg/b.go' + end + end + + context 'with a root module and a submodule' do + context 'with an empty module path' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' } + it_behaves_like '#files', 'files excluding the submodule', 'README.md', 'go.mod', 'a.go', 'pkg/b.go' + end + + context 'with the submodule\'s path' do + let_it_be(:mod) { create :go_module, project: project, path: 'mod' } + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' } + it_behaves_like '#files', 'the submodule\'s files', 'mod/go.mod', 'mod/a.go' + end + end + end + + describe '#archive' do + context 'with a root module' do + context 'with an empty module path' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.2' } + it_behaves_like '#archive', 'all the files', 'README.md', 'go.mod', 'a.go', 'pkg/b.go' + end + end + + context 'with a root module and a submodule' do + context 'with an empty module path' do + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' } + it_behaves_like '#archive', 'files excluding the submodule', 'README.md', 'go.mod', 'a.go', 'pkg/b.go' + end + + context 'with the submodule\'s path' do + let_it_be(:mod) { create :go_module, project: project, path: 'mod' } + let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' } + it_behaves_like '#archive', 'the submodule\'s files', 'go.mod', 'a.go' + end + end + end +end diff --git a/spec/models/packages/maven/metadatum_spec.rb b/spec/models/packages/maven/metadatum_spec.rb new file mode 100644 index 00000000000..16f6929d710 --- /dev/null +++ b/spec/models/packages/maven/metadatum_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Maven::Metadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:package) } + + describe '#app_name' do + it { is_expected.to allow_value("my-app").for(:app_name) } + it { is_expected.not_to allow_value("my/app").for(:app_name) } + it { is_expected.not_to allow_value("my(app)").for(:app_name) } + end + + describe '#app_group' do + it { is_expected.to allow_value("my.domain.com").for(:app_group) } + it { is_expected.not_to allow_value("my/domain/com").for(:app_group) } + it { is_expected.not_to allow_value("my(domain)").for(:app_group) } + end + + describe '#path' do + it { is_expected.to allow_value("my/domain/com/my-app").for(:path) } + it { is_expected.to allow_value("my/domain/com/my-app/1.0-SNAPSHOT").for(:path) } + it { is_expected.not_to allow_value("my(domain)com.my-app").for(:path) } + end + + describe '#maven_package_type' do + it 'will not allow a package with a different package_type' do + package = build('conan_package') + maven_metadatum = build('maven_metadatum', package: package) + + expect(maven_metadatum).not_to be_valid + expect(maven_metadatum.errors.to_a).to include('Package type must be Maven') + end + end + end +end diff --git a/spec/models/packages/nuget/dependency_link_metadatum_spec.rb b/spec/models/packages/nuget/dependency_link_metadatum_spec.rb new file mode 100644 index 00000000000..0c03c65028e --- /dev/null +++ b/spec/models/packages/nuget/dependency_link_metadatum_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Nuget::DependencyLinkMetadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:dependency_link) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:dependency_link) } + it { is_expected.to validate_presence_of(:target_framework) } + + describe '#ensure_nuget_package_type' do + it 'validates package of type nuget' do + package = build('conan_package') + dependency_link = build('packages_dependency_link', package: package) + nuget_metadatum = build('nuget_dependency_link_metadatum', dependency_link: dependency_link) + + expect(nuget_metadatum).not_to be_valid + expect(nuget_metadatum.errors.to_a).to contain_exactly('Package type must be NuGet') + end + + it 'validates package of type nuget with nil dependency_link' do + nuget_metadatum = build('nuget_dependency_link_metadatum', dependency_link: nil) + + expect(nuget_metadatum).not_to be_valid + expect(nuget_metadatum.errors.to_a).to contain_exactly("Dependency link can't be blank", 'Package type must be NuGet') + end + end + end +end diff --git a/spec/models/packages/nuget/metadatum_spec.rb b/spec/models/packages/nuget/metadatum_spec.rb new file mode 100644 index 00000000000..c1bc5429500 --- /dev/null +++ b/spec/models/packages/nuget/metadatum_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Nuget::Metadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package).inverse_of(:nuget_metadatum) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:package) } + + %i[license_url project_url icon_url].each do |url| + describe "##{url}" do + it { is_expected.to allow_value('http://sandbox.com').for(url) } + it { is_expected.to allow_value('https://sandbox.com').for(url) } + it { is_expected.not_to allow_value('123').for(url) } + it { is_expected.not_to allow_value('sandbox.com').for(url) } + end + + describe '#ensure_at_least_one_field_supplied' do + subject { build(:nuget_metadatum) } + + it 'rejects unfilled metadatum' do + subject.attributes = { license_url: nil, project_url: nil, icon_url: nil } + + expect(subject).not_to be_valid + expect(subject.errors).to contain_exactly('Nuget metadatum must have at least license_url, project_url or icon_url set') + end + end + + describe '#ensure_nuget_package_type' do + subject { build(:nuget_metadatum) } + + it 'rejects if not linked to a nuget package' do + subject.package = build(:npm_package) + + expect(subject).not_to be_valid + expect(subject.errors).to contain_exactly('Package type must be NuGet') + end + end + end + end +end diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb new file mode 100644 index 00000000000..7758ed4a500 --- /dev/null +++ b/spec/models/packages/package_file_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::PackageFile, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package) } + it { is_expected.to have_one(:conan_file_metadatum) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:package) } + end + + context 'with package filenames' do + let_it_be(:package_file1) { create(:package_file, :xml, file_name: 'FooBar') } + let_it_be(:package_file2) { create(:package_file, :xml, file_name: 'ThisIsATest') } + + describe '.with_file_name' do + let(:filename) { 'FooBar' } + + subject { described_class.with_file_name(filename) } + + it { is_expected.to match_array([package_file1]) } + end + + describe '.with_file_name_like' do + let(:filename) { 'foobar' } + + subject { described_class.with_file_name_like(filename) } + + it { is_expected.to match_array([package_file1]) } + end + end + + it_behaves_like 'UpdateProjectStatistics' do + subject { build(:package_file, :jar, size: 42) } + + before do + allow_any_instance_of(Packages::PackageFileUploader).to receive(:size).and_return(42) + end + end + + describe '.with_conan_package_reference' do + let_it_be(:non_matching_package_file) { create(:package_file, :nuget) } + let_it_be(:metadatum) { create(:conan_file_metadatum, :package_file) } + let_it_be(:reference) { metadatum.conan_package_reference} + + it 'returns matching packages' do + expect(described_class.with_conan_package_reference(reference)) + .to eq([metadatum.package_file]) + end + end + + describe '#update_file_metadata callback' do + let_it_be(:package_file) { build(:package_file, :nuget, file_store: nil, size: nil) } + + subject { package_file.save! } + + it 'updates metadata columns' do + expect(package_file) + .to receive(:update_file_metadata) + .and_call_original + + expect { subject } + .to change { package_file.file_store }.from(nil).to(::Packages::PackageFileUploader::Store::LOCAL) + .and change { package_file.size }.from(nil).to(3513) + end + end +end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb new file mode 100644 index 00000000000..4170bf595f0 --- /dev/null +++ b/spec/models/packages/package_spec.rb @@ -0,0 +1,485 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Package, type: :model do + include SortingHelper + + describe 'relationships' do + it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:package_files).dependent(:destroy) } + it { is_expected.to have_many(:dependency_links).inverse_of(:package) } + it { is_expected.to have_many(:tags).inverse_of(:package) } + it { is_expected.to have_one(:conan_metadatum).inverse_of(:package) } + it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) } + it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) } + end + + describe '.with_composer_target' do + let!(:package1) { create(:composer_package, :with_metadatum, sha: '123') } + let!(:package2) { create(:composer_package, :with_metadatum, sha: '123') } + let!(:package3) { create(:composer_package, :with_metadatum, sha: '234') } + + subject { described_class.with_composer_target('123').to_a } + + it 'selects packages with the specified sha' do + expect(subject).to include(package1) + expect(subject).to include(package2) + expect(subject).not_to include(package3) + end + end + + describe '.sort_by_attribute' do + let_it_be(:group) { create(:group, :public) } + let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') } + let!(:package1) { create(:npm_package, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") } + let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') } + let(:package3) { create(:maven_package, project: project, version: '1.1.1', name: 'zzz') } + + before do + travel_to(1.day.ago) do + package3 + end + end + + RSpec.shared_examples 'package sorting by attribute' do |order_by| + subject { described_class.where(id: packages.map(&:id)).sort_by_attribute("#{order_by}_#{sort}").to_a } + + context "sorting by #{order_by}" do + context 'ascending order' do + let(:sort) { 'asc' } + + it { is_expected.to eq packages } + end + + context 'descending order' do + let(:sort) { 'desc' } + + it { is_expected.to eq packages.reverse } + end + end + end + + it_behaves_like 'package sorting by attribute', 'name' do + let(:packages) { [package1, package2, package3] } + end + + it_behaves_like 'package sorting by attribute', 'created_at' do + let(:packages) { [package3, package1, package2] } + end + + it_behaves_like 'package sorting by attribute', 'version' do + let(:packages) { [package3, package2, package1] } + end + + it_behaves_like 'package sorting by attribute', 'type' do + let(:packages) { [package3, package1, package2] } + end + + it_behaves_like 'package sorting by attribute', 'project_path' do + let(:another_project) { create(:project, :public, namespace: group, name: 'project B') } + let!(:package4) { create(:npm_package, project: another_project, version: '3.1.0', name: "@#{project.root_namespace.path}/bar") } + + let(:packages) { [package1, package2, package3, package4] } + end + end + + describe 'validations' do + subject { create(:package) } + + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id, :version, :package_type) } + + describe '#name' do + it { is_expected.to allow_value("my/domain/com/my-app").for(:name) } + it { is_expected.to allow_value("my.app-11.07.2018").for(:name) } + it { is_expected.not_to allow_value("my(dom$$$ain)com.my-app").for(:name) } + + context 'conan package' do + subject { create(:conan_package) } + + let(:fifty_one_characters) {'f_b' * 17} + + it { is_expected.to allow_value('foo+bar').for(:name) } + it { is_expected.to allow_value('foo_bar').for(:name) } + it { is_expected.to allow_value('foo.bar').for(:name) } + it { is_expected.not_to allow_value(fifty_one_characters).for(:name) } + it { is_expected.not_to allow_value('+foobar').for(:name) } + it { is_expected.not_to allow_value('.foobar').for(:name) } + it { is_expected.not_to allow_value('%foo%bar').for(:name) } + end + end + + describe '#version' do + RSpec.shared_examples 'validating version to be SemVer compliant for' do |factory_name| + context "for #{factory_name}" do + subject { create(factory_name) } + + it { is_expected.to allow_value('1.2.3').for(:version) } + it { is_expected.to allow_value('1.2.3-beta').for(:version) } + it { is_expected.to allow_value('1.2.3-alpha.3').for(:version) } + it { is_expected.not_to allow_value('1').for(:version) } + it { is_expected.not_to allow_value('1.2').for(:version) } + it { is_expected.not_to allow_value('1./2.3').for(:version) } + it { is_expected.not_to allow_value('../../../../../1.2.3').for(:version) } + it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) } + end + end + + context 'conan package' do + subject { create(:conan_package) } + + let(:fifty_one_characters) {'1.2' * 17} + + it { is_expected.to allow_value('1.2').for(:version) } + it { is_expected.to allow_value('1.2.3-beta').for(:version) } + it { is_expected.to allow_value('1.2.3-pre1+build2').for(:version) } + it { is_expected.not_to allow_value('1').for(:version) } + it { is_expected.not_to allow_value(fifty_one_characters).for(:version) } + it { is_expected.not_to allow_value('1./2.3').for(:version) } + it { is_expected.not_to allow_value('.1.2.3').for(:version) } + it { is_expected.not_to allow_value('+1.2.3').for(:version) } + it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) } + end + + context 'maven package' do + subject { create(:maven_package) } + + it { is_expected.to allow_value('0').for(:version) } + it { is_expected.to allow_value('1').for(:version) } + it { is_expected.to allow_value('10').for(:version) } + it { is_expected.to allow_value('1.0').for(:version) } + it { is_expected.to allow_value('1.3.350.v20200505-1744').for(:version) } + it { is_expected.to allow_value('1.1-beta-2').for(:version) } + it { is_expected.to allow_value('1.2-SNAPSHOT').for(:version) } + it { is_expected.to allow_value('12.1.2-2-1').for(:version) } + it { is_expected.to allow_value('1.2.3..beta').for(:version) } + it { is_expected.to allow_value('1.2.3-beta').for(:version) } + it { is_expected.to allow_value('10.2.3-beta').for(:version) } + it { is_expected.to allow_value('2.0.0.v200706041905-7C78EK9E_EkMNfNOd2d8qq').for(:version) } + it { is_expected.to allow_value('1.2-alpha-1-20050205.060708-1').for(:version) } + it { is_expected.to allow_value('703220b4e2cea9592caeb9f3013f6b1e5335c293').for(:version) } + it { is_expected.to allow_value('RELEASE').for(:version) } + it { is_expected.not_to allow_value('..1.2.3').for(:version) } + it { is_expected.not_to allow_value(' 1.2.3').for(:version) } + it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) } + it { is_expected.not_to allow_value("\r\t 1.2.3").for(:version) } + it { is_expected.not_to allow_value('1.2.3-4/../../').for(:version) } + it { is_expected.not_to allow_value('1.2.3-4%2e%2e%').for(:version) } + it { is_expected.not_to allow_value('../../../../../1.2.3').for(:version) } + it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) } + end + + it_behaves_like 'validating version to be SemVer compliant for', :npm_package + it_behaves_like 'validating version to be SemVer compliant for', :nuget_package + end + + describe '#package_already_taken' do + context 'npm package' do + let!(:package) { create(:npm_package) } + + it 'will not allow a package of the same name' do + new_package = build(:npm_package, name: package.name) + + expect(new_package).not_to be_valid + end + end + + context 'maven package' do + let!(:package) { create(:maven_package) } + + it 'will allow a package of the same name' do + new_package = build(:maven_package, name: package.name) + + expect(new_package).to be_valid + end + end + end + + context "recipe uniqueness for conan packages" do + let!(:package) { create('conan_package') } + + it "will allow a conan package with same project, name, version and package_type" do + new_package = build('conan_package', project: package.project, name: package.name, version: package.version) + new_package.conan_metadatum.package_channel = 'beta' + expect(new_package).to be_valid + end + + it "will not allow a conan package with same recipe (name, version, metadatum.package_channel, metadatum.package_username, and package_type)" do + new_package = build('conan_package', project: package.project, name: package.name, version: package.version) + expect(new_package).not_to be_valid + expect(new_package.errors.to_a).to include("Package recipe already exists") + end + end + + Packages::Package.package_types.keys.without('conan').each do |pt| + context "project id, name, version and package type uniqueness for package type #{pt}" do + let(:package) { create("#{pt}_package") } + + it "will not allow a #{pt} package with same project, name, version and package_type" do + new_package = build("#{pt}_package", project: package.project, name: package.name, version: package.version) + expect(new_package).not_to be_valid + expect(new_package.errors.to_a).to include("Name has already been taken") + end + end + end + end + + describe '#destroy' do + let(:package) { create(:npm_package) } + let(:package_file) { package.package_files.first } + let(:project_statistics) { ProjectStatistics.for_project_ids(package.project.id).first } + + it 'affects project statistics' do + expect { package.destroy! } + .to change { project_statistics.reload.packages_size } + .from(package_file.size).to(0) + end + end + + describe '.by_name_and_file_name' do + let!(:package) { create(:npm_package) } + let!(:package_file) { package.package_files.first } + + subject { described_class } + + it 'finds a package with correct arguiments' do + expect(subject.by_name_and_file_name(package.name, package_file.file_name)).to eq(package) + end + + it 'will raise error if not found' do + expect { subject.by_name_and_file_name('foo', 'foo-5.5.5.tgz') }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context 'version scopes' do + let!(:package1) { create(:npm_package, version: '1.0.0') } + let!(:package2) { create(:npm_package, version: '1.0.1') } + let!(:package3) { create(:npm_package, version: '1.0.1') } + + describe '.last_of_each_version' do + subject { described_class.last_of_each_version } + + it 'includes only latest package per version' do + is_expected.to include(package1, package3) + is_expected.not_to include(package2) + end + end + + describe '.has_version' do + subject { described_class.has_version } + + before do + create(:maven_metadatum).package.update!(version: nil) + end + + it 'includes only packages with version attribute' do + is_expected.to match_array([package1, package2, package3]) + end + end + + describe '.with_version' do + subject { described_class.with_version('1.0.1') } + + it 'includes only packages with specified version' do + is_expected.to match_array([package2, package3]) + end + end + + describe '.without_version_like' do + let(:version_pattern) { '%.0.0%' } + + subject { described_class.without_version_like(version_pattern) } + + it 'includes packages without the version pattern' do + is_expected.to match_array([package2, package3]) + end + end + end + + context 'conan scopes' do + let!(:package) { create(:conan_package) } + + describe '.with_conan_channel' do + subject { described_class.with_conan_channel('stable') } + + it 'includes only packages with specified version' do + is_expected.to include(package) + end + end + + describe '.with_conan_username' do + subject do + described_class.with_conan_username( + Packages::Conan::Metadatum.package_username_from(full_path: package.project.full_path) + ) + end + + it 'includes only packages with specified version' do + is_expected.to match_array([package]) + end + end + end + + describe '.without_nuget_temporary_name' do + let!(:package1) { create(:nuget_package) } + let!(:package2) { create(:nuget_package, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) } + + subject { described_class.without_nuget_temporary_name } + + it 'does not include nuget temporary packages' do + expect(subject).to eq([package1]) + end + end + + describe '.processed' do + let!(:package1) { create(:nuget_package) } + let!(:package2) { create(:npm_package) } + let!(:package3) { create(:nuget_package) } + + subject { described_class.processed } + + it { is_expected.to match_array([package1, package2, package3]) } + + context 'with temporary packages' do + let!(:package1) { create(:nuget_package, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) } + + it { is_expected.to match_array([package2, package3]) } + end + end + + describe '.limit_recent' do + let!(:package1) { create(:nuget_package) } + let!(:package2) { create(:nuget_package) } + let!(:package3) { create(:nuget_package) } + + subject { described_class.limit_recent(2) } + + it { is_expected.to match_array([package3, package2]) } + end + + context 'with several packages' do + let_it_be(:package1) { create(:nuget_package, name: 'FooBar') } + let_it_be(:package2) { create(:nuget_package, name: 'foobar') } + let_it_be(:package3) { create(:npm_package) } + let_it_be(:package4) { create(:npm_package) } + + describe '.pluck_names' do + subject { described_class.pluck_names } + + it { is_expected.to match_array([package1, package2, package3, package4].map(&:name)) } + end + + describe '.pluck_versions' do + subject { described_class.pluck_versions } + + it { is_expected.to match_array([package1, package2, package3, package4].map(&:version)) } + end + + describe '.with_name_like' do + subject { described_class.with_name_like(name_term) } + + context 'with downcase name' do + let(:name_term) { 'foobar' } + + it { is_expected.to match_array([package1, package2]) } + end + + context 'with prefix wildcard' do + let(:name_term) { '%ar' } + + it { is_expected.to match_array([package1, package2]) } + end + + context 'with suffix wildcard' do + let(:name_term) { 'foo%' } + + it { is_expected.to match_array([package1, package2]) } + end + + context 'with surrounding wildcards' do + let(:name_term) { '%ooba%' } + + it { is_expected.to match_array([package1, package2]) } + end + end + + describe '.search_by_name' do + let(:query) { 'oba' } + + subject { described_class.search_by_name(query) } + + it { is_expected.to match_array([package1, package2]) } + end + end + + describe '.select_distinct_name' do + let_it_be(:nuget_package) { create(:nuget_package) } + let_it_be(:nuget_packages) { create_list(:nuget_package, 3, name: nuget_package.name, project: nuget_package.project) } + let_it_be(:maven_package) { create(:maven_package) } + let_it_be(:maven_packages) { create_list(:maven_package, 3, name: maven_package.name, project: maven_package.project) } + + subject { described_class.select_distinct_name } + + it 'returns only distinct names' do + packages = subject + + expect(packages.size).to eq(2) + expect(packages.pluck(:name)).to match_array([nuget_package.name, maven_package.name]) + end + end + + describe '#versions' do + let_it_be(:project) { create(:project) } + let_it_be(:package) { create(:maven_package, project: project) } + let_it_be(:package2) { create(:maven_package, project: project) } + let_it_be(:package3) { create(:maven_package, project: project, name: 'foo') } + + it 'returns other package versions of the same package name belonging to the project' do + expect(package.versions).to contain_exactly(package2) + end + + it 'does not return different packages' do + expect(package.versions).not_to include(package3) + end + end + + describe '#pipeline' do + let_it_be(:package) { create(:maven_package) } + + context 'package without pipeline' do + it 'returns nil if there is no pipeline' do + expect(package.pipeline).to be_nil + end + end + + context 'package with pipeline' do + let_it_be(:pipeline) { create(:ci_pipeline) } + + before do + package.create_build_info!(pipeline: pipeline) + end + + it 'returns the pipeline' do + expect(package.pipeline).to eq(pipeline) + end + end + end + + describe '#tag_names' do + let_it_be(:package) { create(:nuget_package) } + + subject { package.tag_names } + + it { is_expected.to eq([]) } + + context 'with tags' do + let(:tags) { %w(tag1 tag2 tag3) } + + before do + tags.each { |t| create(:packages_tag, name: t, package: package) } + end + + it { is_expected.to contain_exactly(*tags) } + end + end +end diff --git a/spec/models/packages/pypi/metadatum_spec.rb b/spec/models/packages/pypi/metadatum_spec.rb new file mode 100644 index 00000000000..2c9893ef8f3 --- /dev/null +++ b/spec/models/packages/pypi/metadatum_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Pypi::Metadatum, type: :model do + describe 'relationships' do + it { is_expected.to belong_to(:package) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:package) } + + describe '#pypi_package_type' do + it 'will not allow a package with a different package_type' do + package = build('package') + pypi_metadatum = build('pypi_metadatum', package: package) + + expect(pypi_metadatum).not_to be_valid + expect(pypi_metadatum.errors.to_a).to include('Package type must be PyPi') + end + end + end +end diff --git a/spec/models/packages/sem_ver_spec.rb b/spec/models/packages/sem_ver_spec.rb new file mode 100644 index 00000000000..419653dca19 --- /dev/null +++ b/spec/models/packages/sem_ver_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::SemVer, type: :model do + shared_examples '#parse with a valid semver' do |str, major, minor, patch, prerelease, build| + context "with #{str}" do + it "returns #{described_class.new(major, minor, patch, prerelease, build, prefixed: true)} with prefix" do + expected = described_class.new(major, minor, patch, prerelease, build, prefixed: true) + expect(described_class.parse('v' + str, prefixed: true)).to eq(expected) + end + + it "returns #{described_class.new(major, minor, patch, prerelease, build)} without prefix" do + expected = described_class.new(major, minor, patch, prerelease, build) + expect(described_class.parse(str)).to eq(expected) + end + end + end + + shared_examples '#parse with an invalid semver' do |str| + context "with #{str}" do + it 'returns nil with prefix' do + expect(described_class.parse('v' + str, prefixed: true)).to be_nil + end + + it 'returns nil without prefix' do + expect(described_class.parse(str)).to be_nil + end + end + end + + describe '#parse' do + it_behaves_like '#parse with a valid semver', '1.0.0', 1, 0, 0, nil, nil + it_behaves_like '#parse with a valid semver', '1.0.0-pre', 1, 0, 0, 'pre', nil + it_behaves_like '#parse with a valid semver', '1.0.0+build', 1, 0, 0, nil, 'build' + it_behaves_like '#parse with a valid semver', '1.0.0-pre+build', 1, 0, 0, 'pre', 'build' + it_behaves_like '#parse with an invalid semver', '01.0.0' + it_behaves_like '#parse with an invalid semver', '0.01.0' + it_behaves_like '#parse with an invalid semver', '0.0.01' + it_behaves_like '#parse with an invalid semver', '1.0.0asdf' + end +end diff --git a/spec/models/packages/tag_spec.rb b/spec/models/packages/tag_spec.rb new file mode 100644 index 00000000000..18ec99c3d51 --- /dev/null +++ b/spec/models/packages/tag_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Tag, type: :model do + let!(:project) { create(:project) } + let!(:package) { create(:npm_package, version: '1.0.2', project: project, updated_at: 3.days.ago) } + + describe 'relationships' do + it { is_expected.to belong_to(:package).inverse_of(:tags) } + end + + describe 'validations' do + subject { create(:packages_tag) } + + it { is_expected.to validate_presence_of(:package) } + it { is_expected.to validate_presence_of(:name) } + end + + describe '.for_packages' do + let(:package2) { create(:package, project: project, updated_at: 2.days.ago) } + let(:package3) { create(:package, project: project, updated_at: 1.day.ago) } + let!(:tag1) { create(:packages_tag, package: package) } + let!(:tag2) { create(:packages_tag, package: package2) } + let!(:tag3) { create(:packages_tag, package: package3) } + + subject { described_class.for_packages(project.packages) } + + it { is_expected.to match_array([tag1, tag2, tag3]) } + + context 'with too many tags' do + before do + stub_const('Packages::Tag::FOR_PACKAGES_TAGS_LIMIT', 2) + end + + it { is_expected.to match_array([tag2, tag3]) } + end + end + + describe '.with_name' do + let_it_be(:package) { create(:package) } + let_it_be(:tag1) { create(:packages_tag, package: package, name: 'tag1') } + let_it_be(:tag2) { create(:packages_tag, package: package, name: 'tag2') } + let_it_be(:tag3) { create(:packages_tag, package: package, name: 'tag3') } + let(:name) { 'tag1' } + + subject { described_class.with_name(name) } + + it { is_expected.to contain_exactly(tag1) } + + context 'with nil name' do + let(:name) { nil } + + it { is_expected.to eq([]) } + end + + context 'with multiple names' do + let(:name) { %w(tag1 tag3) } + + it { is_expected.to contain_exactly(tag1, tag3) } + end + end +end diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb index c05d4c82634..38bd9b39a56 100644 --- a/spec/models/pages/lookup_path_spec.rb +++ b/spec/models/pages/lookup_path_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Pages::LookupPath do +RSpec.describe Pages::LookupPath do let(:project) do instance_double(Project, id: 12345, diff --git a/spec/models/pages/virtual_domain_spec.rb b/spec/models/pages/virtual_domain_spec.rb index a5310738482..38f5f4d2538 100644 --- a/spec/models/pages/virtual_domain_spec.rb +++ b/spec/models/pages/virtual_domain_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Pages::VirtualDomain do +RSpec.describe Pages::VirtualDomain do describe '#certificate and #key pair' do let(:domain) { nil } let(:project) { instance_double(Project) } diff --git a/spec/models/pages_domain_acme_order_spec.rb b/spec/models/pages_domain_acme_order_spec.rb index 4ffb4fc7389..4a104203e39 100644 --- a/spec/models/pages_domain_acme_order_spec.rb +++ b/spec/models/pages_domain_acme_order_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PagesDomainAcmeOrder do +RSpec.describe PagesDomainAcmeOrder do using RSpec::Parameterized::TableSyntax describe '.expired' do diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index fc7694530d0..d283389e29e 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PagesDomain do +RSpec.describe PagesDomain do using RSpec::Parameterized::TableSyntax subject(:pages_domain) { described_class.new } diff --git a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb index ef7298c3d8c..61174a7d0c5 100644 --- a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb +++ b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PerformanceMonitoring::PrometheusDashboard do +RSpec.describe PerformanceMonitoring::PrometheusDashboard do let(:json_content) do { "dashboard" => "Dashboard Title", @@ -50,19 +50,19 @@ describe PerformanceMonitoring::PrometheusDashboard do context 'dashboard content is missing' do let(:json_content) { nil } - it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"] + it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"] end context 'dashboard content is NOT a hash' do let(:json_content) { YAML.safe_load("'test'") } - it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"] + it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"] end context 'content is an array' do let(:json_content) { [{ "dashboard" => "Dashboard Title" }] } - it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"] + it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"] end context 'dashboard definition is missing panels_groups and dashboard keys' do @@ -72,7 +72,7 @@ describe PerformanceMonitoring::PrometheusDashboard do } end - it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"] + it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"] end context 'group definition is missing panels and group keys' do @@ -88,7 +88,7 @@ describe PerformanceMonitoring::PrometheusDashboard do } end - it_behaves_like 'validation failed', panels: ["can't be blank"], group: ["can't be blank"] + it_behaves_like 'validation failed', panels: ["should be an array of panels objects"], group: ["can't be blank"] end context 'panel definition is missing metrics and title keys' do @@ -110,7 +110,7 @@ describe PerformanceMonitoring::PrometheusDashboard do } end - it_behaves_like 'validation failed', metrics: ["can't be blank"], title: ["can't be blank"] + it_behaves_like 'validation failed', metrics: ["should be an array of metrics objects"], title: ["can't be blank"] end context 'metrics definition is missing unit, query and query_range keys' do @@ -180,7 +180,7 @@ describe PerformanceMonitoring::PrometheusDashboard do describe '.find_for' do let(:project) { build_stubbed(:project) } let(:user) { build_stubbed(:user) } - let(:environment) { build_stubbed(:environment) } + let(:environment) { build_stubbed(:environment, project: project) } let(:path) { ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH } context 'dashboard has been found' do diff --git a/spec/models/performance_monitoring/prometheus_metric_spec.rb b/spec/models/performance_monitoring/prometheus_metric_spec.rb index 83f687aa90e..b5b9cd58aa8 100644 --- a/spec/models/performance_monitoring/prometheus_metric_spec.rb +++ b/spec/models/performance_monitoring/prometheus_metric_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PerformanceMonitoring::PrometheusMetric do +RSpec.describe PerformanceMonitoring::PrometheusMetric do let(:json_content) do { "id" => "metric_of_ages", diff --git a/spec/models/performance_monitoring/prometheus_panel_group_spec.rb b/spec/models/performance_monitoring/prometheus_panel_group_spec.rb index ecf7e13a9a3..9e92cb27954 100644 --- a/spec/models/performance_monitoring/prometheus_panel_group_spec.rb +++ b/spec/models/performance_monitoring/prometheus_panel_group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PerformanceMonitoring::PrometheusPanelGroup do +RSpec.describe PerformanceMonitoring::PrometheusPanelGroup do let(:json_content) do { "group" => "Group Title", diff --git a/spec/models/performance_monitoring/prometheus_panel_spec.rb b/spec/models/performance_monitoring/prometheus_panel_spec.rb index 127b9e8183a..c5c6b1fdafd 100644 --- a/spec/models/performance_monitoring/prometheus_panel_spec.rb +++ b/spec/models/performance_monitoring/prometheus_panel_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PerformanceMonitoring::PrometheusPanel do +RSpec.describe PerformanceMonitoring::PrometheusPanel do let(:json_content) do { "max_value" => 1, diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb index a3f5eb38511..a39a37b605f 100644 --- a/spec/models/personal_access_token_spec.rb +++ b/spec/models/personal_access_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PersonalAccessToken do +RSpec.describe PersonalAccessToken do subject { described_class } describe '.build' do @@ -165,6 +165,7 @@ describe PersonalAccessToken do let_it_be(:revoked_token) { create(:personal_access_token, revoked: true) } let_it_be(:valid_token_and_notified) { create(:personal_access_token, expires_at: 2.days.from_now, expire_notification_delivered: true) } let_it_be(:valid_token) { create(:personal_access_token, expires_at: 2.days.from_now) } + let_it_be(:long_expiry_token) { create(:personal_access_token, expires_at: '999999-12-31'.to_date) } context 'in one day' do it "doesn't have any tokens" do @@ -187,10 +188,24 @@ describe PersonalAccessToken do expect(described_class.without_impersonation).to contain_exactly(personal_access_token) end end + + describe 'revoke scopes' do + let_it_be(:revoked_token) { create(:personal_access_token, :revoked) } + let_it_be(:non_revoked_token) { create(:personal_access_token, revoked: false) } + let_it_be(:non_revoked_token2) { create(:personal_access_token, revoked: nil) } + + describe '.revoked' do + it { expect(described_class.revoked).to contain_exactly(revoked_token) } + end + + describe '.not_revoked' do + it { expect(described_class.not_revoked).to contain_exactly(non_revoked_token, non_revoked_token2) } + end + end end describe '.simple_sorts' do - it 'includes overriden keys' do + it 'includes overridden keys' do expect(described_class.simple_sorts.keys).to include(*%w(expires_at_asc expires_at_desc)) end end diff --git a/spec/models/personal_snippet_spec.rb b/spec/models/personal_snippet_spec.rb index fb96d6e8bc3..10d70fed1ee 100644 --- a/spec/models/personal_snippet_spec.rb +++ b/spec/models/personal_snippet_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PersonalSnippet do +RSpec.describe PersonalSnippet do describe '#embeddable?' do [ { snippet: :public, embeddable: true }, diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb index 1366f088623..831fd0dcbc3 100644 --- a/spec/models/plan_limits_spec.rb +++ b/spec/models/plan_limits_spec.rb @@ -2,57 +2,217 @@ require 'spec_helper' -describe PlanLimits do - let(:plan_limits) { create(:plan_limits) } - let(:model) { ProjectHook } - let(:count) { model.count } +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 - create(:project_hook) + create_list(:project_hook, project_hooks_count, project: project) end - context 'without plan limits configured' do - describe '#exceeded?' do - it 'does not exceed any relation offset' do - expect(plan_limits.exceeded?(:project_hooks, model)).to be false - expect(plan_limits.exceeded?(:project_hooks, count)).to be false + describe '#exceeded?' do + let(:alternate_limit) { double('an alternate limit value') } + + subject(:exceeded_limit) { plan_limits.exceeded?(:project_hooks, limit_subject, alternate_limit: alternate_limit) } + + before do + allow(plan_limits).to receive(:limit_for).with(:project_hooks, alternate_limit: alternate_limit).and_return(limit) + end + + shared_examples_for 'comparing limits' do + context 'when limit for given name results to a disabled value' do + let(:limit) { nil } + + it { is_expected.to eq(false) } + end + + context 'when limit for given name results to a non-disabled value' do + context 'and given count is smaller than limit' do + let(:limit) { project_hooks_count + 1 } + + it { is_expected.to eq(false) } + end + + context 'and given count is equal to the limit' do + let(:limit) { project_hooks_count } + + it { is_expected.to eq(true) } + end + + context 'and given count is greater than the limit' do + let(:limit) { project_hooks_count - 1 } + + it { is_expected.to eq(true) } + end + end + end + + context 'when given limit subject is an integer' do + let(:limit_subject) { project.hooks.count } + + it_behaves_like 'comparing limits' + end + + context 'when given limit subject is an ActiveRecord::Relation' do + let(:limit_subject) { project.hooks } + + it_behaves_like 'comparing limits' + end + + context 'when given limit subject is something else' do + let(:limit_subject) { ProjectHook } + let(:limit) { 100 } + + it 'raises an error' do + expect { exceeded_limit }.to raise_error(ArgumentError) end end end - context 'with plan limits configured' do - before do - plan_limits.update!(project_hooks: 2) + describe '#limit_for' do + let(:alternate_limit) { nil } + + subject(:limit) { plan_limits.limit_for(:project_hooks, alternate_limit: alternate_limit) } + + context 'when given limit name does not exist' do + it 'raises an error' do + expect { plan_limits.limit_for(:project_foo) }.to raise_error(described_class::LimitUndefinedError) + end end - describe '#exceeded?' do - it 'does not exceed the relation offset' do - expect(plan_limits.exceeded?(:project_hooks, model)).to be false - expect(plan_limits.exceeded?(:project_hooks, count)).to be false + context 'when given limit name is disabled' do + before do + plan_limits.update!(project_hooks: 0) + end + + it { is_expected.to eq(nil) } + + context 'and alternate_limit is a non-zero integer' do + let(:alternate_limit) { 1 } + + it { is_expected.to eq(1) } + end + + context 'and alternate_limit is zero' do + let(:alternate_limit) { 0 } + + it { is_expected.to eq(nil) } + end + + context 'and alternate_limit is a proc that returns non-zero integer' do + let(:alternate_limit) { -> { 1 } } + + it { is_expected.to eq(1) } + end + + context 'and alternate_limit is a proc that returns zero' do + let(:alternate_limit) { -> { 0 } } + + it { is_expected.to eq(nil) } + end + + context 'and alternate_limit is a proc that returns nil' do + let(:alternate_limit) { -> { nil } } + + it { is_expected.to eq(nil) } end end - context 'with boundary values' do + context 'when given limit name is enabled' do + let(:plan_limit_value) { 2 } + before do - create(:project_hook) + plan_limits.update!(project_hooks: plan_limit_value) end - describe '#exceeded?' do - it 'does exceed the relation offset' do - expect(plan_limits.exceeded?(:project_hooks, model)).to be true - expect(plan_limits.exceeded?(:project_hooks, count)).to be true - end + context 'and alternate_limit is a non-zero integer that is bigger than the plan limit' do + let(:alternate_limit) { plan_limit_value + 1 } + + it { is_expected.to eq(plan_limit_value) } + end + + context 'and alternate_limit is a non-zero integer that is smaller than the plan limit' do + let(:alternate_limit) { plan_limit_value - 1 } + + it { is_expected.to eq(alternate_limit) } + end + + context 'and alternate_limit is zero' do + let(:alternate_limit) { 0 } + + it { is_expected.to eq(plan_limit_value) } + end + + context 'and alternate_limit is a proc that returns non-zero integer that is bigger than the plan limit' do + let(:alternate_limit) { -> { plan_limit_value + 1 } } + + it { is_expected.to eq(plan_limit_value) } + end + + context 'and alternate_limit is a proc that returns non-zero integer that is smaller than the plan limit' do + let(:alternate_limit) { -> { plan_limit_value - 1 } } + + it { is_expected.to eq(alternate_limit.call) } + end + + context 'and alternate_limit is a proc that returns zero' do + let(:alternate_limit) { -> { 0 } } + + it { is_expected.to eq(plan_limit_value) } + end + + context 'and alternate_limit is a proc that returns nil' do + let(:alternate_limit) { -> { nil } } + + it { is_expected.to eq(plan_limit_value) } end end end context 'validates default values' do + # TODO: For now, these columns have default values set to 0. + # Each artifact type listed here have their own matching issues to determine + # the actual limit value. In each of those issues, the default value should also be updated to + # a non-zero value. Also update existing values of zero to whatever the default value will be. + # For a list of the issues, see: https://gitlab.com/gitlab-org/gitlab/-/issues/211378#note_355619970 + let(:disabled_max_artifact_size_columns) do + %w[ + ci_max_artifact_size_archive + ci_max_artifact_size_metadata + ci_max_artifact_size_trace + ci_max_artifact_size_junit + ci_max_artifact_size_sast + ci_max_artifact_size_dependency_scanning + ci_max_artifact_size_container_scanning + ci_max_artifact_size_dast + ci_max_artifact_size_codequality + ci_max_artifact_size_license_management + ci_max_artifact_size_license_scanning + ci_max_artifact_size_performance + ci_max_artifact_size_browser_performance + ci_max_artifact_size_load_performance + ci_max_artifact_size_metrics + ci_max_artifact_size_metrics_referee + ci_max_artifact_size_network_referee + ci_max_artifact_size_dotenv + ci_max_artifact_size_cobertura + ci_max_artifact_size_terraform + ci_max_artifact_size_accessibility + ci_max_artifact_size_cluster_applications + ci_max_artifact_size_secret_detection + ci_max_artifact_size_requirements + ci_max_artifact_size_coverage_fuzzing + ] + end + let(:columns_with_zero) do %w[ ci_active_pipelines ci_pipeline_size ci_active_jobs - ] + storage_size_limit + ] + disabled_max_artifact_size_columns end it "has positive values for enabled limits" do diff --git a/spec/models/plan_spec.rb b/spec/models/plan_spec.rb index 3f3b8046232..490c6b1bbf7 100644 --- a/spec/models/plan_spec.rb +++ b/spec/models/plan_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Plan do +RSpec.describe Plan do describe '#default?' do subject { plan.default? } @@ -14,4 +14,16 @@ describe Plan do end end end + + context 'when updating plan limits' do + let(:plan) { described_class.default } + + it { expect(plan).to be_persisted } + + it { expect(plan.actual_limits).not_to be_persisted } + + it 'successfully updates the limits' do + expect(plan.actual_limits.update!(ci_instance_level_variables: 100)).to be_truthy + end + end end diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb index ae00f9df89e..92b3e41cd18 100644 --- a/spec/models/pool_repository_spec.rb +++ b/spec/models/pool_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PoolRepository do +RSpec.describe PoolRepository do describe 'associations' do it { is_expected.to belong_to(:shard) } it { is_expected.to belong_to(:source_project) } diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb index d435fccc09a..02a4d783b84 100644 --- a/spec/models/postgresql/replication_slot_spec.rb +++ b/spec/models/postgresql/replication_slot_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Postgresql::ReplicationSlot do +RSpec.describe Postgresql::ReplicationSlot do describe '.in_use?' do it 'returns true when replication slots are present' do expect(described_class).to receive(:exists?).and_return(true) diff --git a/spec/models/product_analytics_event_spec.rb b/spec/models/product_analytics_event_spec.rb new file mode 100644 index 00000000000..6058df9fa13 --- /dev/null +++ b/spec/models/product_analytics_event_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe ProductAnalyticsEvent, type: :model do + it { is_expected.to belong_to(:project) } + it { expect(described_class).to respond_to(:order_by_time) } + + describe 'validations' do + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:event_id) } + it { is_expected.to validate_presence_of(:v_collector) } + it { is_expected.to validate_presence_of(:v_etl) } + end + + describe '.timerange' do + let_it_be(:event_1) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 1.day) } + let_it_be(:event_2) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 5.days) } + let_it_be(:event_3) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 15.days) } + + it { expect(described_class.timerange(3.days)).to match_array([event_1]) } + it { expect(described_class.timerange(7.days)).to match_array([event_1, event_2]) } + it { expect(described_class.timerange(30.days)).to match_array([event_1, event_2, event_3]) } + end +end diff --git a/spec/models/programming_language_spec.rb b/spec/models/programming_language_spec.rb index b327d360461..f2201eabd1c 100644 --- a/spec/models/programming_language_spec.rb +++ b/spec/models/programming_language_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProgrammingLanguage do +RSpec.describe ProgrammingLanguage do it { is_expected.to respond_to(:name) } it { is_expected.to respond_to(:color) } diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb index 6f06fe4e55a..c517fc8be55 100644 --- a/spec/models/project_authorization_spec.rb +++ b/spec/models/project_authorization_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectAuthorization do +RSpec.describe ProjectAuthorization do let(:user) { create(:user) } let(:project1) { create(:project) } let(:project2) { create(:project) } diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 5af25ac1437..8313879114f 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectAutoDevops do +RSpec.describe ProjectAutoDevops do let_it_be(:project) { build(:project) } it_behaves_like 'having unique enum values' diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb index ecca371ce4e..698465e854a 100644 --- a/spec/models/project_ci_cd_setting_spec.rb +++ b/spec/models/project_ci_cd_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectCiCdSetting do +RSpec.describe ProjectCiCdSetting do describe 'validations' do it 'validates default_git_depth is between 0 and 1000 or nil' do expect(subject).to validate_numericality_of(:default_git_depth) diff --git a/spec/models/project_custom_attribute_spec.rb b/spec/models/project_custom_attribute_spec.rb index 80638676b49..25ee1e60819 100644 --- a/spec/models/project_custom_attribute_spec.rb +++ b/spec/models/project_custom_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectCustomAttribute do +RSpec.describe ProjectCustomAttribute do describe 'assocations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_daily_statistic_spec.rb b/spec/models/project_daily_statistic_spec.rb index 86210af15d8..8dbabdb3829 100644 --- a/spec/models/project_daily_statistic_spec.rb +++ b/spec/models/project_daily_statistic_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe ProjectDailyStatistic do +RSpec.describe ProjectDailyStatistic do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_export_job_spec.rb b/spec/models/project_export_job_spec.rb index dc39d0e401d..5a2b1443f8b 100644 --- a/spec/models/project_export_job_spec.rb +++ b/spec/models/project_export_job_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectExportJob, type: :model do +RSpec.describe ProjectExportJob, type: :model do let(:project) { create(:project) } let!(:job1) { create(:project_export_job, project: project, status: 0) } let!(:job2) { create(:project_export_job, project: project, status: 2) } diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb index e33ea75bc5d..c927c8fd1f9 100644 --- a/spec/models/project_feature_spec.rb +++ b/spec/models/project_feature_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectFeature do +RSpec.describe ProjectFeature do using RSpec::Parameterized::TableSyntax let(:project) { create(:project) } diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb index 8ef29e8a876..c925d87170c 100644 --- a/spec/models/project_group_link_spec.rb +++ b/spec/models/project_group_link_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectGroupLink do +RSpec.describe ProjectGroupLink do describe "Associations" do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/project_import_data_spec.rb b/spec/models/project_import_data_spec.rb index fe47811f074..50a2ee42084 100644 --- a/spec/models/project_import_data_spec.rb +++ b/spec/models/project_import_data_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectImportData do +RSpec.describe ProjectImportData do describe '#merge_data' do it 'writes the Hash to the attribute if it is nil' do row = described_class.new diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb index f3b83c036b5..6a0402d43a8 100644 --- a/spec/models/project_import_state_spec.rb +++ b/spec/models/project_import_state_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectImportState, type: :model do +RSpec.describe ProjectImportState, type: :model do let_it_be(:correlation_id) { 'cid' } let_it_be(:import_state, refind: true) { create(:import_state, correlation_id_value: correlation_id) } diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb index 330aab9f856..f451c2905e6 100644 --- a/spec/models/project_label_spec.rb +++ b/spec/models/project_label_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectLabel do +RSpec.describe ProjectLabel do describe 'relationships' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_metrics_setting_spec.rb b/spec/models/project_metrics_setting_spec.rb index adfbbbc3a45..6639f9cb208 100644 --- a/spec/models/project_metrics_setting_spec.rb +++ b/spec/models/project_metrics_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectMetricsSetting do +RSpec.describe ProjectMetricsSetting do describe 'Associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_repository_spec.rb b/spec/models/project_repository_spec.rb index c966447fedc..6852ca0097d 100644 --- a/spec/models/project_repository_spec.rb +++ b/spec/models/project_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectRepository do +RSpec.describe ProjectRepository do describe 'associations' do it { is_expected.to belong_to(:shard) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/project_services/alerts_service_spec.rb b/spec/models/project_services/alerts_service_spec.rb index 4e63ece26d8..db25885c76a 100644 --- a/spec/models/project_services/alerts_service_spec.rb +++ b/spec/models/project_services/alerts_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AlertsService do +RSpec.describe AlertsService do let_it_be(:project) { create(:project) } let(:service_params) { { project: project, active: active } } let(:active) { true } diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index 8b6f2888c0a..7a6fe4b1537 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AsanaService do +RSpec.describe AsanaService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb index 2c86c0ec7be..207add6f090 100644 --- a/spec/models/project_services/assembla_service_spec.rb +++ b/spec/models/project_services/assembla_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe AssemblaService do +RSpec.describe AssemblaService do include StubRequests describe "Associations" do diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index c1efa3a4348..4d2474cc56a 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BambooService, :use_clean_rails_memory_store_caching do +RSpec.describe BambooService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers include StubRequests diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index ab939e0d2f8..560c7c3ee83 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BugzillaService do +RSpec.describe BugzillaService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -32,49 +32,4 @@ describe BugzillaService do it { is_expected.not_to validate_presence_of(:new_issue_url) } end end - - context 'overriding properties' do - let(:url) { 'http://bugzilla.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # 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) { access_params.merge(title: title, description: description) } - let(:service) do - create(:bugzilla_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:bugzilla_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:bugzilla_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:bugzilla_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Bugzilla') - expect(service.description).to eq('Bugzilla issue tracker') - end - end - end end diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 8b6fa36eaa5..ff717a59e7b 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe BuildkiteService, :use_clean_rails_memory_store_caching do +RSpec.describe BuildkiteService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers include StubRequests diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb index 0d3dd89e93b..ea3990b339b 100644 --- a/spec/models/project_services/campfire_service_spec.rb +++ b/spec/models/project_services/campfire_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CampfireService do +RSpec.describe CampfireService do include StubRequests describe 'Associations' do diff --git a/spec/models/project_services/chat_message/alert_message_spec.rb b/spec/models/project_services/chat_message/alert_message_spec.rb index a1dd332c005..927c5dffe77 100644 --- a/spec/models/project_services/chat_message/alert_message_spec.rb +++ b/spec/models/project_services/chat_message/alert_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::AlertMessage do +RSpec.describe ChatMessage::AlertMessage do subject { described_class.new(args) } let_it_be(:start_time) { Time.current } diff --git a/spec/models/project_services/chat_message/base_message_spec.rb b/spec/models/project_services/chat_message/base_message_spec.rb index 8f80cf0b074..a7ddf230758 100644 --- a/spec/models/project_services/chat_message/base_message_spec.rb +++ b/spec/models/project_services/chat_message/base_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::BaseMessage do +RSpec.describe ChatMessage::BaseMessage do let(:base_message) { described_class.new(args) } let(:args) { { project_url: 'https://gitlab-domain.com' } } diff --git a/spec/models/project_services/chat_message/deployment_message_spec.rb b/spec/models/project_services/chat_message/deployment_message_spec.rb index 42c1689db3d..9c361f90ae0 100644 --- a/spec/models/project_services/chat_message/deployment_message_spec.rb +++ b/spec/models/project_services/chat_message/deployment_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::DeploymentMessage do +RSpec.describe ChatMessage::DeploymentMessage do describe '#pretext' do it 'returns a message with the data returned by the deployment data builder' do environment = create(:environment, name: "myenvironment") diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb index c4d10be8331..051f4780ba4 100644 --- a/spec/models/project_services/chat_message/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::IssueMessage do +RSpec.describe ChatMessage::IssueMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb index 6063ef4ecb3..45be5212508 100644 --- a/spec/models/project_services/chat_message/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::MergeMessage do +RSpec.describe ChatMessage::MergeMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb index 5e7987dc0f6..6a741365d55 100644 --- a/spec/models/project_services/chat_message/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::NoteMessage do +RSpec.describe ChatMessage::NoteMessage do subject { described_class.new(args) } let(:color) { '#345' } diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb index a7171577063..4eb2f57315b 100644 --- a/spec/models/project_services/chat_message/pipeline_message_spec.rb +++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe ChatMessage::PipelineMessage do +RSpec.describe ChatMessage::PipelineMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index 9d990508ab2..e3ba4c2aefe 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::PushMessage do +RSpec.describe ChatMessage::PushMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index 1346a43335e..04c9e5934be 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatMessage::WikiPageMessage do +RSpec.describe ChatMessage::WikiPageMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb index 1caec5c6eb7..77a1377c138 100644 --- a/spec/models/project_services/chat_notification_service_spec.rb +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ChatNotificationService do +RSpec.describe ChatNotificationService do describe 'Associations' do before do allow(subject).to receive(:activated?).and_return(true) diff --git a/spec/models/project_services/confluence_service_spec.rb b/spec/models/project_services/confluence_service_spec.rb new file mode 100644 index 00000000000..5d153b17070 --- /dev/null +++ b/spec/models/project_services/confluence_service_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ConfluenceService 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 + let(:active) { true } + + it { is_expected.not_to allow_value('https://example.com').for(:confluence_url) } + it { is_expected.not_to allow_value('example.com').for(:confluence_url) } + it { is_expected.not_to allow_value('foo').for(:confluence_url) } + it { is_expected.not_to allow_value('ftp://example.atlassian.net/wiki').for(:confluence_url) } + it { is_expected.not_to allow_value('https://example.atlassian.net').for(:confluence_url) } + it { is_expected.not_to allow_value('https://.atlassian.net/wiki').for(:confluence_url) } + it { is_expected.not_to allow_value('https://example.atlassian.net/wikifoo').for(:confluence_url) } + it { is_expected.not_to allow_value('').for(:confluence_url) } + it { is_expected.not_to allow_value(nil).for(:confluence_url) } + it { is_expected.not_to allow_value('😊').for(:confluence_url) } + it { is_expected.to allow_value('https://example.atlassian.net/wiki').for(:confluence_url) } + it { is_expected.to allow_value('http://example.atlassian.net/wiki').for(:confluence_url) } + it { is_expected.to allow_value('https://example.atlassian.net/wiki/').for(:confluence_url) } + it { is_expected.to allow_value('http://example.atlassian.net/wiki/').for(:confluence_url) } + it { is_expected.to allow_value('https://example.atlassian.net/wiki/foo').for(:confluence_url) } + + it { is_expected.to validate_presence_of(:confluence_url) } + end + + context 'when service is inactive' do + let(:active) { false } + + it { is_expected.not_to validate_presence_of(:confluence_url) } + it { is_expected.to allow_value('foo').for(:confluence_url) } + end + end + + describe '#detailed_description' do + it 'can correctly return a link to the project wiki when active' do + project = create(:project) + subject.project = project + subject.active = true + + expect(subject.detailed_description).to include(Gitlab::Routing.url_helpers.project_wikis_url(project)) + end + + context 'when the project wiki is not enabled' do + it 'returns nil when both active or inactive', :aggregate_failures do + project = create(:project, :wiki_disabled) + subject.project = project + + [true, false].each do |active| + subject.active = active + + expect(subject.detailed_description).to be_nil + end + end + end + end + + describe 'Caching has_confluence on project_settings' do + let(:project) { create(:project) } + + subject { project.project_setting.has_confluence? } + + it 'sets the property to true when service is active' do + create(:confluence_service, project: project, active: true) + + is_expected.to be(true) + end + + it 'sets the property to false when service is not active' do + create(:confluence_service, project: project, active: false) + + is_expected.to be(false) + end + + it 'creates a project_setting record if one was not already created' do + expect { create(:confluence_service) }.to change { ProjectSetting.count }.by(1) + end + end +end diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index e749ea6eacc..881ae60a680 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CustomIssueTrackerService do +RSpec.describe CustomIssueTrackerService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -31,66 +31,5 @@ describe CustomIssueTrackerService do it { is_expected.not_to validate_presence_of(:issues_url) } it { is_expected.not_to validate_presence_of(:new_issue_url) } end - - context 'title' do - let(:issue_tracker) { described_class.new(properties: {}) } - - it 'sets a default title' do - issue_tracker.title = nil - - expect(issue_tracker.title).to eq('Custom Issue Tracker') - end - - it 'sets the custom title' do - issue_tracker.title = 'test title' - - expect(issue_tracker.title).to eq('test title') - end - end - end - - context 'overriding properties' do - let(:url) { 'http://custom.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # 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) { access_params.merge(title: title, description: description) } - let(:service) do - create(:custom_issue_tracker_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:custom_issue_tracker_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:custom_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:custom_issue_tracker_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Custom Issue Tracker') - expect(service.description).to eq('Custom issue tracker') - end - end end end diff --git a/spec/models/project_services/data_fields_spec.rb b/spec/models/project_services/data_fields_spec.rb index 6b388a7222b..9a3042f9f8d 100644 --- a/spec/models/project_services/data_fields_spec.rb +++ b/spec/models/project_services/data_fields_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DataFields do +RSpec.describe DataFields do let(:url) { 'http://url.com' } let(:username) { 'username_one' } let(:properties) do diff --git a/spec/models/project_services/discord_service_spec.rb b/spec/models/project_services/discord_service_spec.rb index b5a54676dd7..d4bd08ddeb6 100644 --- a/spec/models/project_services/discord_service_spec.rb +++ b/spec/models/project_services/discord_service_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe DiscordService do +RSpec.describe DiscordService do it_behaves_like "chat service", "Discord notifications" do let(:client) { Discordrb::Webhooks::Client } let(:client_arguments) { { url: webhook_url } } diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index 1ee9c5f90c6..9aaf4f7a644 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe DroneCiService, :use_clean_rails_memory_store_caching do +RSpec.describe DroneCiService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers describe 'associations' do diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb index 44db95afc57..6954a72f9c1 100644 --- a/spec/models/project_services/emails_on_push_service_spec.rb +++ b/spec/models/project_services/emails_on_push_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe EmailsOnPushService do +RSpec.describe EmailsOnPushService do describe 'Validations' do context 'when service is active' do before do diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb index f8d88a944a5..c6891401a0f 100644 --- a/spec/models/project_services/external_wiki_service_spec.rb +++ b/spec/models/project_services/external_wiki_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ExternalWikiService do +RSpec.describe ExternalWikiService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index c1ebe69ee66..94a49fb3080 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe FlowdockService do +RSpec.describe FlowdockService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index 7f1c6224b7d..a6b7cb05836 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GitlabIssueTrackerService do +RSpec.describe GitlabIssueTrackerService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -51,49 +51,4 @@ describe GitlabIssueTrackerService do end end end - - context 'overriding properties' do - let(:url) { 'http://gitlab.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # 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) { access_params.merge(title: title, description: description) } - let(:service) do - create(:gitlab_issue_tracker_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:gitlab_issue_tracker_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:gitlab_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:gitlab_issue_tracker_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('GitLab') - expect(service.description).to eq('GitLab issue tracker') - end - end - end end diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb index 0505ac9b49c..042e32439d1 100644 --- a/spec/models/project_services/hangouts_chat_service_spec.rb +++ b/spec/models/project_services/hangouts_chat_service_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe HangoutsChatService do +RSpec.describe HangoutsChatService do it_behaves_like "chat service", "Hangouts Chat" do let(:client) { HangoutsChat::Sender } let(:client_arguments) { webhook_url } diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index c25edf81352..667e6cc85ab 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe HipchatService do +RSpec.describe HipchatService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index 88a93eef214..07963947de8 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'socket' require 'json' -describe IrkerService do +RSpec.describe IrkerService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/issue_tracker_data_spec.rb b/spec/models/project_services/issue_tracker_data_spec.rb index db617cf0abb..3ddb7d9250f 100644 --- a/spec/models/project_services/issue_tracker_data_spec.rb +++ b/spec/models/project_services/issue_tracker_data_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IssueTrackerData do +RSpec.describe IssueTrackerData do let(:service) { create(:custom_issue_tracker_service, active: false, properties: {}) } describe 'Associations' do diff --git a/spec/models/project_services/issue_tracker_service_spec.rb b/spec/models/project_services/issue_tracker_service_spec.rb index f1cdee5c4a3..5b12c7330b8 100644 --- a/spec/models/project_services/issue_tracker_service_spec.rb +++ b/spec/models/project_services/issue_tracker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IssueTrackerService do +RSpec.describe IssueTrackerService do describe 'Validations' do let(:project) { create :project } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 20e85f0fd4b..cfc2c920cd2 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -2,11 +2,9 @@ require 'spec_helper' -describe JiraService do +RSpec.describe JiraService do include AssetsHelpers - let(:title) { 'custom title' } - let(:description) { 'custom description' } let(:url) { 'http://jira.example.com' } let(:api_url) { 'http://api-jira.example.com' } let(:username) { 'jira-username' } @@ -93,7 +91,6 @@ describe JiraService do let(:params) do { project: create(:project), - title: 'custom title', description: 'custom description', url: url, api_url: api_url, username: username, password: password, jira_issue_transition_id: transition_id @@ -106,19 +103,6 @@ describe JiraService do expect(subject.properties).to be_nil end - it 'sets title correctly' do - service = subject - - expect(service.title).to eq('custom title') - end - - it 'sets service data correctly' do - service = subject - - expect(service.title).to eq('custom title') - expect(service.description).to eq('custom description') - end - it 'stores data in data_fields correcty' do service = subject @@ -209,7 +193,6 @@ describe JiraService do end it 'does not reset password if url "changed" to the same url as before' do - service.title = 'aaaaaa' service.url = 'http://jira.example.com' service.save @@ -318,46 +301,32 @@ describe JiraService 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.merge(title: title, description: description) } + let(:properties) { data_params } let!(:service) do create(:jira_service, :without_properties_callback, properties: properties.merge(additional: 'something')) end - it_behaves_like 'issue tracker fields' 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: {}, title: title, description: description)) + create(:jira_service, data_params.merge(properties: {})) end - it_behaves_like 'issue tracker fields' it_behaves_like 'handles jira fields' end context 'when data are stored in both properties and separated fields' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let(:service) do create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |service| create(:jira_tracker_data, data_params.merge(service: service)) end end - it_behaves_like 'issue tracker fields' it_behaves_like 'handles jira fields' end - - context 'when no title & description are set' do - let(:service) do - create(:jira_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Jira') - expect(service.description).to eq(s_('JiraService|Jira issue tracker')) - end - end end describe '#close_issue' do @@ -704,59 +673,6 @@ describe JiraService do end end - describe 'description and title' do - let(:title) { 'Jira One' } - let(:description) { 'Jira One issue tracker' } - let(:properties) do - { - url: 'http://jira.example.com/web', - username: 'mic', - password: 'password', - title: title, - description: description - } - end - - context 'when it is not set' do - it 'default values are returned' do - service = create(:jira_service) - - expect(service.title).to eq('Jira') - expect(service.description).to eq(s_('JiraService|Jira issue tracker')) - end - end - - context 'when it is set in properties' do - it 'values from properties are returned' do - service = create(:jira_service, :without_properties_callback, properties: properties) - - expect(service.title).to eq(title) - expect(service.description).to eq(description) - end - end - - context 'when it is in title & description fields' do - it 'values from title and description fields are returned' do - service = create(:jira_service, title: title, description: description) - - expect(service.title).to eq(title) - expect(service.description).to eq(description) - end - end - - context 'when it is in both properites & title & description fields' do - it 'values from title and description fields are returned' do - title2 = 'Jira 2' - description2 = 'Jira description 2' - - service = create(:jira_service, title: title2, description: description2, properties: properties) - - expect(service.title).to eq(title2) - expect(service.description).to eq(description2) - end - end - end - describe 'project and issue urls' do context 'when gitlab.yml was initialized' do it 'is prepopulated with the settings' do @@ -808,7 +724,7 @@ describe JiraService do describe '#new_issue_url' do it 'handles trailing slashes' do - expect(service.new_issue_url).to eq('http://jira.test.com/path/secure/CreateIssue.jspa') + expect(service.new_issue_url).to eq('http://jira.test.com/path/secure/CreateIssue!default.jspa') end end end diff --git a/spec/models/project_services/jira_tracker_data_spec.rb b/spec/models/project_services/jira_tracker_data_spec.rb index 12f6b99e8a7..9e38bced46c 100644 --- a/spec/models/project_services/jira_tracker_data_spec.rb +++ b/spec/models/project_services/jira_tracker_data_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -describe JiraTrackerData do - let(:service) { create(:jira_service, active: false) } +RSpec.describe JiraTrackerData do + let(:service) { build(:jira_service) } describe 'Associations' do it { is_expected.to belong_to(:service) } diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/project_services/mattermost_service_spec.rb index 5b974985706..af1944ea77d 100644 --- a/spec/models/project_services/mattermost_service_spec.rb +++ b/spec/models/project_services/mattermost_service_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe MattermostService do +RSpec.describe MattermostService do it_behaves_like "slack or mattermost notifications", "Mattermost" end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 836181929e3..4fff3bc56cc 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MattermostSlashCommandsService do +RSpec.describe MattermostSlashCommandsService do it_behaves_like "chat slash commands service" context 'Mattermost API' do diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb index 425599c73d4..610feb52827 100644 --- a/spec/models/project_services/microsoft_teams_service_spec.rb +++ b/spec/models/project_services/microsoft_teams_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MicrosoftTeamsService do +RSpec.describe MicrosoftTeamsService do let(:chat_service) { described_class.new } let(:webhook_url) { 'https://example.gitlab.com/' } diff --git a/spec/models/project_services/open_project_service_spec.rb b/spec/models/project_services/open_project_service_spec.rb index 8e373a31e62..1abaab0ceff 100644 --- a/spec/models/project_services/open_project_service_spec.rb +++ b/spec/models/project_services/open_project_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe OpenProjectService do +RSpec.describe OpenProjectService do describe 'Validations' do context 'when service is active' do before do diff --git a/spec/models/project_services/open_project_tracker_data_spec.rb b/spec/models/project_services/open_project_tracker_data_spec.rb index 0d387bbf69b..e6a3963ba87 100644 --- a/spec/models/project_services/open_project_tracker_data_spec.rb +++ b/spec/models/project_services/open_project_tracker_data_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe OpenProjectTrackerData do +RSpec.describe OpenProjectTrackerData do describe 'Associations' do it { is_expected.to belong_to(:service) } end diff --git a/spec/models/project_services/packagist_service_spec.rb b/spec/models/project_services/packagist_service_spec.rb index 53f18a1bdd9..f710385b6e2 100644 --- a/spec/models/project_services/packagist_service_spec.rb +++ b/spec/models/project_services/packagist_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PackagistService do +RSpec.describe PackagistService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/pipelines_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb index de1edf2099a..9a8386c619e 100644 --- a/spec/models/project_services/pipelines_email_service_spec.rb +++ b/spec/models/project_services/pipelines_email_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PipelinesEmailService, :mailer do +RSpec.describe PipelinesEmailService, :mailer do let(:pipeline) do create(:ci_pipeline, :failed, project: project, diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb index dde46c82df6..8de85cc7fa5 100644 --- a/spec/models/project_services/pivotaltracker_service_spec.rb +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PivotaltrackerService do +RSpec.describe PivotaltrackerService do include StubRequests describe 'Associations' do diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index db3cbe23ad3..16837e2b93a 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PrometheusService, :use_clean_rails_memory_store_caching do +RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching do include PrometheusHelpers include ReactiveCachingHelpers @@ -23,7 +23,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do # result = { success: false, result: error } expect(result[:success]).to be_falsy - expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::Error) + expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::UnexpectedResponseError) expect(redirect_req_stub).to have_been_requested expect(redirected_req_stub).not_to have_been_requested @@ -262,8 +262,6 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do service.google_iap_audience_client_id = "IAP_CLIENT_ID.apps.googleusercontent.com" stub_request(:post, "https://oauth2.googleapis.com/token").to_return(status: 200, body: '{"id_token": "FOO"}', headers: { 'Content-Type': 'application/json; charset=UTF-8' }) - - stub_feature_flags(prometheus_service_iap_auth: true) end it 'includes the authorization header' do @@ -474,11 +472,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do title: 'API URL', placeholder: s_('PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/'), required: true - } - ] - end - let(:feature_flagged_fields) do - [ + }, { type: 'text', name: 'google_iap_audience_client_id', @@ -498,13 +492,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do end it 'returns fields' do - stub_feature_flags(prometheus_service_iap_auth: false) expect(service.fields).to eq(expected_fields) end - - it 'returns fields with feature flag on' do - stub_feature_flags(prometheus_service_iap_auth: true) - expect(service.fields).to eq(expected_fields + feature_flagged_fields) - end end end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index 380f02739bc..b7d3b8987b8 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PushoverService do +RSpec.describe PushoverService do include StubRequests describe 'Associations' do diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 6220d7b1fac..b9be3940d34 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe RedmineService do +RSpec.describe RedmineService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -50,49 +50,4 @@ describe RedmineService do expect(described_class.reference_pattern.match('#123')[:issue]).to eq('123') end end - - context 'overriding properties' do - let(:url) { 'http://redmine.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # 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) { access_params.merge(title: title, description: description) } - let(:service) do - create(:redmine_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:redmine_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:redmine_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:redmine_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Redmine') - expect(service.description).to eq('Redmine issue tracker') - end - end - end end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 93036ac7ec4..0b35b9e7b30 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe SlackService do +RSpec.describe SlackService do it_behaves_like "slack or mattermost notifications", 'Slack' end diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb index 8c57907d064..95c87ef01bc 100644 --- a/spec/models/project_services/slack_slash_commands_service_spec.rb +++ b/spec/models/project_services/slack_slash_commands_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SlackSlashCommandsService do +RSpec.describe SlackSlashCommandsService do it_behaves_like "chat slash commands service" describe '#trigger' do diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 0dd77b68588..a3fda33664a 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe TeamcityService, :use_clean_rails_memory_store_caching do +RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers include StubRequests diff --git a/spec/models/project_services/unify_circuit_service_spec.rb b/spec/models/project_services/unify_circuit_service_spec.rb index 51079ea5395..73702aa8471 100644 --- a/spec/models/project_services/unify_circuit_service_spec.rb +++ b/spec/models/project_services/unify_circuit_service_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe UnifyCircuitService do +RSpec.describe UnifyCircuitService do it_behaves_like "chat service", "Unify Circuit" do let(:client_arguments) { webhook_url } let(:content_key) { :subject } diff --git a/spec/models/project_services/webex_teams_service_spec.rb b/spec/models/project_services/webex_teams_service_spec.rb index 38977ef3b7d..bd73d0c93b8 100644 --- a/spec/models/project_services/webex_teams_service_spec.rb +++ b/spec/models/project_services/webex_teams_service_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe WebexTeamsService do +RSpec.describe WebexTeamsService do it_behaves_like "chat service", "Webex Teams" do let(:client_arguments) { webhook_url } let(:content_key) { :markdown } diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb index b8fff635e99..4339b44e1de 100644 --- a/spec/models/project_services/youtrack_service_spec.rb +++ b/spec/models/project_services/youtrack_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe YoutrackService do +RSpec.describe YoutrackService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -42,49 +42,4 @@ describe YoutrackService do expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123') end end - - context 'overriding properties' do - let(:url) { 'http://youtrack.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # 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) { access_params.merge(title: title, description: description) } - let(:service) do - create(:youtrack_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:youtrack_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:youtrack_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:youtrack_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('YouTrack') - expect(service.description).to eq(s_('IssueTracker|YouTrack issue tracker')) - end - end - end end diff --git a/spec/models/project_setting_spec.rb b/spec/models/project_setting_spec.rb index 5cfb932eb2a..5572304d666 100644 --- a/spec/models/project_setting_spec.rb +++ b/spec/models/project_setting_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe ProjectSetting, type: :model do +RSpec.describe ProjectSetting, type: :model do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index c17a24dc7cf..464b9b1da84 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectSnippet do +RSpec.describe ProjectSnippet do describe "Associations" do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9ec306d297e..8fdda241719 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Project do +RSpec.describe Project do include ProjectForksHelper include GitHelpers include ExternalAuthorizationServiceHelpers @@ -63,6 +63,7 @@ describe Project do it { is_expected.to have_one(:bugzilla_service) } it { is_expected.to have_one(:gitlab_issue_tracker_service) } it { is_expected.to have_one(:external_wiki_service) } + it { is_expected.to have_one(:confluence_service) } it { is_expected.to have_one(:project_feature) } it { is_expected.to have_one(:project_repository) } it { is_expected.to have_one(:container_expiration_policy) } @@ -119,6 +120,8 @@ describe Project do it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:project) } it { is_expected.to have_many(:repository_storage_moves) } it { is_expected.to have_many(:reviews).inverse_of(:project) } + it { is_expected.to have_many(:packages).class_name('Packages::Package') } + it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') } it_behaves_like 'model with repository' do let_it_be(:container) { create(:project, :repository, path: 'somewhere') } @@ -1378,6 +1381,62 @@ describe Project do end end + describe '.service_desk_enabled' do + it 'returns the correct project' do + project_with_service_desk_enabled = create(:project) + project_with_service_desk_disabled = create(:project, :service_desk_disabled) + + expect(described_class.service_desk_enabled).to include(project_with_service_desk_enabled) + expect(described_class.service_desk_enabled).not_to include(project_with_service_desk_disabled) + end + end + + describe '#service_desk_enabled?' do + let_it_be(:namespace) { create(:namespace) } + + subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) } + + before do + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true) + end + + it 'is enabled' do + expect(project.service_desk_enabled?).to be_truthy + expect(project.service_desk_enabled).to be_truthy + end + end + + describe '#service_desk_address' do + let_it_be(:project) { create(:project, service_desk_enabled: true) } + + before do + allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true) + allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) + allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com") + end + + it 'uses project full path as service desk address key' do + expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com") + end + end + + describe '.find_by_service_desk_project_key' do + it 'returns the correct project' do + project1 = create(:project) + project2 = create(:project) + create(:service_desk_setting, project: project1, project_key: 'key1') + create(:service_desk_setting, project: project2, project_key: 'key2') + + expect(Project.find_by_service_desk_project_key('key1')).to eq(project1) + expect(Project.find_by_service_desk_project_key('key2')).to eq(project2) + end + + it 'returns nil if there is no project with the key' do + expect(Project.find_by_service_desk_project_key('some_key')).to be_nil + end + end + context 'repository storage by default' do let(:project) { build(:project) } @@ -1651,6 +1710,14 @@ describe Project do let(:project_name) { 'group.example.com' } it { is_expected.to eq("http://group.example.com") } + + context 'mixed case path' do + before do + project.update!(path: 'Group.example.com') + end + + it { is_expected.to eq("http://group.example.com") } + end end context 'project page' do @@ -1658,6 +1725,14 @@ describe Project do let(:project_name) { 'Project' } it { is_expected.to eq("http://group.example.com/project") } + + context 'mixed case path' do + before do + project.update!(path: 'Project') + end + + it { is_expected.to eq("http://group.example.com/Project") } + end end end @@ -2897,28 +2972,73 @@ describe Project do subject { project.deployment_variables(environment: environment, kubernetes_namespace: namespace) } - before do - expect(project).to receive(:deployment_platform).with(environment: environment) - .and_return(deployment_platform) - end + context 'when the deployment platform is stubbed' do + before do + expect(project).to receive(:deployment_platform).with(environment: environment) + .and_return(deployment_platform) + end + + context 'when project has a deployment platform' do + let(:platform_variables) { %w(platform variables) } + let(:deployment_platform) { double } + + before do + expect(deployment_platform).to receive(:predefined_variables) + .with(project: project, environment_name: environment, kubernetes_namespace: namespace) + .and_return(platform_variables) + end + + it { is_expected.to eq platform_variables } + end - context 'when project has no deployment platform' do - let(:deployment_platform) { nil } + context 'when project has no deployment platform' do + let(:deployment_platform) { nil } - it { is_expected.to eq [] } + it { is_expected.to eq [] } + end end - context 'when project has a deployment platform' do - let(:platform_variables) { %w(platform variables) } - let(:deployment_platform) { double } + context 'when project has a deployment platforms' do + let(:project) { create(:project) } + + let!(:default_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: '*', + platform_kubernetes: default_cluster_kubernetes) + end - before do - expect(deployment_platform).to receive(:predefined_variables) - .with(project: project, environment_name: environment, kubernetes_namespace: namespace) - .and_return(platform_variables) + let!(:review_env_cluster) do + create(:cluster, + :not_managed, + platform_type: :kubernetes, + projects: [project], + environment_scope: 'review/*', + platform_kubernetes: review_env_cluster_kubernetes) end - it { is_expected.to eq platform_variables } + let(:default_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'default-AAA') } + let(:review_env_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'review-AAA') } + + context 'when environment name is review/name' do + let!(:environment) { create(:environment, project: project, name: 'review/name') } + + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'review/name')) + .to include(key: 'KUBE_TOKEN', value: 'review-AAA', public: false, masked: true) + end + end + + context 'when environment name is other' do + let!(:environment) { create(:environment, project: project, name: 'staging/name') } + + it 'returns variables from this service' do + expect(project.deployment_variables(environment: 'staging/name')) + .to include(key: 'KUBE_TOKEN', value: 'default-AAA', public: false, masked: true) + end + end end end @@ -3999,7 +4119,7 @@ describe Project do it 'returns the number of forks' do project = build(:project) - expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1) + expect_any_instance_of(::Projects::BatchForksCountService).to receive(:refresh_cache_and_retrieve_data).and_return({ project => 1 }) expect(project.forks_count).to eq(1) end @@ -4655,6 +4775,7 @@ describe Project do expect(project).to receive(:refresh_markdown_cache!) expect(InternalId).to receive(:flush_records!).with(project: project) expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id) + expect(project).to receive(:write_repository_config) project.after_import end @@ -4743,6 +4864,36 @@ describe Project do end end + describe "#default_branch" do + context "with an empty repository" do + let_it_be(:project) { create(:project_empty_repo) } + + context "Gitlab::CurrentSettings.default_branch_name is unavailable" do + before do + expect(Gitlab::CurrentSettings) + .to receive(:default_branch_name) + .and_return(nil) + end + + it "returns that value" do + expect(project.default_branch).to be_nil + end + end + + context "Gitlab::CurrentSettings.default_branch_name is available" do + before do + expect(Gitlab::CurrentSettings) + .to receive(:default_branch_name) + .and_return('example_branch') + end + + it "returns that value" do + expect(project.default_branch).to eq("example_branch") + end + end + end + end + describe '#to_ability_name' do it 'returns project' do project = build(:project_empty_repo) @@ -5886,6 +6037,30 @@ describe Project do end end + describe '#prometheus_service_active?' do + let(:project) { create(:project) } + + subject { project.prometheus_service_active? } + + before do + create(:prometheus_service, project: project, manual_configuration: manual_configuration) + end + + context 'when project has an activated prometheus service' do + let(:manual_configuration) { true } + + it { is_expected.to be_truthy } + end + + context 'when project has an inactive prometheus service' do + let(:manual_configuration) { false } + + it 'the service is marked as inactive' do + expect(subject).to be_falsey + end + end + end + describe '#self_monitoring?' do let_it_be(:project) { create(:project) } @@ -6025,6 +6200,39 @@ describe Project do end end + describe '#packages_enabled' do + subject { create(:project).packages_enabled } + + it { is_expected.to be true } + end + + describe '#package_already_taken?' do + let(:namespace) { create(:namespace) } + let(:project) { create(:project, :public, namespace: namespace) } + let!(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") } + + context 'no package exists with the same name' do + it 'returns false' do + result = project.package_already_taken?("@#{namespace.path}/bar") + expect(result).to be false + end + + it 'returns false if it is the project that the package belongs to' do + result = project.package_already_taken?("@#{namespace.path}/foo") + expect(result).to be false + end + end + + context 'a package already exists with the same name' do + let(:alt_project) { create(:project, :public, namespace: namespace) } + + it 'returns true' do + result = alt_project.package_already_taken?("@#{namespace.path}/foo") + expect(result).to be true + end + end + end + describe '#design_management_enabled?' do let(:project) { build(:project) } diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index 4bc6130387a..3659e6b973e 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectStatistics do +RSpec.describe ProjectStatistics do let(:project) { create :project } let(:statistics) { project.statistics } @@ -32,7 +32,8 @@ describe ProjectStatistics do repository_size: 2.exabytes, wiki_size: 1.exabytes, lfs_objects_size: 2.exabytes, - build_artifacts_size: 3.exabytes - 1 + build_artifacts_size: 2.exabytes - 1, + snippets_size: 1.exabyte ) statistics.reload @@ -41,8 +42,9 @@ describe ProjectStatistics do expect(statistics.repository_size).to eq(2.exabytes) expect(statistics.wiki_size).to eq(1.exabytes) expect(statistics.lfs_objects_size).to eq(2.exabytes) - expect(statistics.build_artifacts_size).to eq(3.exabytes - 1) + expect(statistics.build_artifacts_size).to eq(2.exabytes - 1) expect(statistics.storage_size).to eq(8.exabytes - 1) + expect(statistics.snippets_size).to eq(1.exabyte) end end @@ -52,23 +54,47 @@ describe ProjectStatistics do statistics.wiki_size = 6 statistics.lfs_objects_size = 3 statistics.build_artifacts_size = 4 + statistics.snippets_size = 5 expect(statistics.total_repository_size).to eq 5 end end describe '#wiki_size' do - it "is initialized with not null value" do + it 'is initialized with not null value' do + expect(statistics.attributes['wiki_size']).to be_zero + expect(statistics.wiki_size).to be_zero + end + + it 'coerces any nil value to 0' do + statistics.update!(wiki_size: nil) + + expect(statistics.attributes['wiki_size']).to be_nil expect(statistics.wiki_size).to eq 0 end end + describe '#snippets_size' do + it 'is initialized with not null value' do + expect(statistics.attributes['snippets_size']).to be_zero + expect(statistics.snippets_size).to be_zero + end + + it 'coerces any nil value to 0' do + statistics.update!(snippets_size: nil) + + expect(statistics.attributes['snippets_size']).to be_nil + expect(statistics.snippets_size).to eq 0 + end + end + describe '#refresh!' do before do allow(statistics).to receive(:update_commit_count) allow(statistics).to receive(:update_repository_size) allow(statistics).to receive(:update_wiki_size) allow(statistics).to receive(:update_lfs_objects_size) + allow(statistics).to receive(:update_snippets_size) allow(statistics).to receive(:update_storage_size) end @@ -82,6 +108,7 @@ describe ProjectStatistics do expect(statistics).to have_received(:update_repository_size) expect(statistics).to have_received(:update_wiki_size) expect(statistics).to have_received(:update_lfs_objects_size) + expect(statistics).to have_received(:update_snippets_size) end end @@ -95,6 +122,7 @@ describe ProjectStatistics do expect(statistics).not_to have_received(:update_commit_count) expect(statistics).not_to have_received(:update_repository_size) expect(statistics).not_to have_received(:update_wiki_size) + expect(statistics).not_to have_received(:update_snippets_size) end end @@ -108,9 +136,11 @@ describe ProjectStatistics do expect(statistics).to have_received(:update_commit_count) expect(statistics).to have_received(:update_repository_size) expect(statistics).to have_received(:update_wiki_size) + expect(statistics).to have_received(:update_snippets_size) expect(statistics.repository_size).to eq(0) expect(statistics.commit_count).to eq(0) expect(statistics.wiki_size).to eq(0) + expect(statistics.snippets_size).to eq(0) end end @@ -130,9 +160,11 @@ describe ProjectStatistics do expect(statistics).to have_received(:update_commit_count) expect(statistics).to have_received(:update_repository_size) expect(statistics).to have_received(:update_wiki_size) + expect(statistics).to have_received(:update_snippets_size) expect(statistics.repository_size).to eq(0) expect(statistics.commit_count).to eq(0) expect(statistics.wiki_size).to eq(0) + expect(statistics.snippets_size).to eq(0) end end @@ -202,6 +234,33 @@ describe ProjectStatistics do end end + describe '#update_snippets_size' do + before do + create_list(:project_snippet, 2, project: project) + SnippetStatistics.update_all(repository_size: 10) + end + + it 'stores the size of snippets' do + # Snippet not associated with the project + snippet = create(:project_snippet) + snippet.statistics.update!(repository_size: 40) + + statistics.update_snippets_size + + expect(statistics.update_snippets_size).to eq 20 + end + + context 'when not all snippets has statistics' do + it 'stores the size of snippets with statistics' do + SnippetStatistics.last.delete + + statistics.update_snippets_size + + expect(statistics.update_snippets_size).to eq 10 + end + end + end + describe '#update_lfs_objects_size' do let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) } let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) } @@ -222,12 +281,13 @@ describe ProjectStatistics do statistics.update!( repository_size: 2, wiki_size: 4, - lfs_objects_size: 3 + lfs_objects_size: 3, + snippets_size: 2 ) statistics.reload - expect(statistics.storage_size).to eq 9 + expect(statistics.storage_size).to eq 11 end it 'works during wiki_size backfill' do @@ -241,6 +301,21 @@ describe ProjectStatistics do expect(statistics.storage_size).to eq 5 end + + context 'when nullable columns are nil' do + it 'does not raise any error' do + expect do + statistics.update!( + repository_size: 2, + wiki_size: nil, + lfs_objects_size: 3, + snippets_size: nil + ) + end.not_to raise_error + + expect(statistics.storage_size).to eq 5 + end + end end describe '.increment_statistic' do diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 24652a1d706..34ec856459c 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe ProjectTeam do +RSpec.describe ProjectTeam do let(:maintainer) { create(:user) } let(:reporter) { create(:user) } let(:guest) { create(:user) } diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index aff2b248642..d9c5fed542e 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProjectWiki do +RSpec.describe ProjectWiki do it_behaves_like 'wiki model' do let(:wiki_container) { create(:project, :wiki_repo, namespace: user.namespace) } let(:wiki_container_without_repo) { create(:project, namespace: user.namespace) } diff --git a/spec/models/prometheus_alert_event_spec.rb b/spec/models/prometheus_alert_event_spec.rb index 85e57cb08c3..913ca7db0be 100644 --- a/spec/models/prometheus_alert_event_spec.rb +++ b/spec/models/prometheus_alert_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PrometheusAlertEvent do +RSpec.describe PrometheusAlertEvent do subject { build(:prometheus_alert_event) } let(:alert) { subject.prometheus_alert } diff --git a/spec/models/prometheus_alert_spec.rb b/spec/models/prometheus_alert_spec.rb index 1409cf65fee..7169a34d96f 100644 --- a/spec/models/prometheus_alert_spec.rb +++ b/spec/models/prometheus_alert_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PrometheusAlert do +RSpec.describe PrometheusAlert do let_it_be(:project) { build(:project) } let(:metric) { build(:prometheus_metric) } @@ -33,6 +33,10 @@ describe PrometheusAlert do describe 'associations' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:environment) } + it { is_expected.to belong_to(:prometheus_metric) } + it { is_expected.to have_many(:prometheus_alert_events) } + it { is_expected.to have_many(:related_issues) } + it { is_expected.to have_many(:alert_management_alerts) } end describe 'project validations' do diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb index 93abef063cb..f284102b4a9 100644 --- a/spec/models/prometheus_metric_spec.rb +++ b/spec/models/prometheus_metric_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PrometheusMetric do +RSpec.describe PrometheusMetric do subject { build(:prometheus_metric) } it_behaves_like 'having unique enum values' @@ -11,6 +11,7 @@ describe PrometheusMetric do it { is_expected.to validate_presence_of(:title) } it { is_expected.to validate_presence_of(:query) } it { is_expected.to validate_presence_of(:group) } + it { is_expected.to validate_uniqueness_of(:identifier).scoped_to(:project_id).allow_nil } describe 'common metrics' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/protectable_dropdown_spec.rb b/spec/models/protectable_dropdown_spec.rb index aca3df9fdde..c51197234ca 100644 --- a/spec/models/protectable_dropdown_spec.rb +++ b/spec/models/protectable_dropdown_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProtectableDropdown do +RSpec.describe ProtectableDropdown do let(:project) { create(:project, :repository) } let(:subject) { described_class.new(project, :branches) } diff --git a/spec/models/protected_branch/merge_access_level_spec.rb b/spec/models/protected_branch/merge_access_level_spec.rb index 39dd586b157..b6c2d527d1b 100644 --- a/spec/models/protected_branch/merge_access_level_spec.rb +++ b/spec/models/protected_branch/merge_access_level_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe ProtectedBranch::MergeAccessLevel do +RSpec.describe ProtectedBranch::MergeAccessLevel do it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) } end diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb index 628c8d29ecd..77fe9814c86 100644 --- a/spec/models/protected_branch/push_access_level_spec.rb +++ b/spec/models/protected_branch/push_access_level_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -describe ProtectedBranch::PushAccessLevel do +RSpec.describe ProtectedBranch::PushAccessLevel do it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) } end diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 30fce1cd5c4..a89f8778780 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProtectedBranch do +RSpec.describe ProtectedBranch do subject { build_stubbed(:protected_branch) } describe 'Associations' do diff --git a/spec/models/protected_tag_spec.rb b/spec/models/protected_tag_spec.rb index 79120d17d39..7bc62b1d0e7 100644 --- a/spec/models/protected_tag_spec.rb +++ b/spec/models/protected_tag_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ProtectedTag do +RSpec.describe ProtectedTag do describe 'Associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/push_event_payload_spec.rb b/spec/models/push_event_payload_spec.rb index 6b59ee5ee57..32415ef4719 100644 --- a/spec/models/push_event_payload_spec.rb +++ b/spec/models/push_event_payload_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PushEventPayload do +RSpec.describe PushEventPayload do it_behaves_like 'having unique enum values' describe 'saving payloads' do diff --git a/spec/models/push_event_spec.rb b/spec/models/push_event_spec.rb index 5c1802669c1..61e31e7c4e3 100644 --- a/spec/models/push_event_spec.rb +++ b/spec/models/push_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe PushEvent do +RSpec.describe PushEvent do let(:payload) { PushEventPayload.new } let(:event) do diff --git a/spec/models/readme_blob_spec.rb b/spec/models/readme_blob_spec.rb index 34182fa413f..95622d55254 100644 --- a/spec/models/readme_blob_spec.rb +++ b/spec/models/readme_blob_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ReadmeBlob do +RSpec.describe ReadmeBlob do include FakeBlobHelpers describe 'policy' do diff --git a/spec/models/redirect_route_spec.rb b/spec/models/redirect_route_spec.rb index b9b2873f8f2..c6e35923b89 100644 --- a/spec/models/redirect_route_spec.rb +++ b/spec/models/redirect_route_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe RedirectRoute do +RSpec.describe RedirectRoute do let(:group) { create(:group) } let!(:redirect_route) { group.redirect_routes.create(path: 'gitlabb') } diff --git a/spec/models/releases/evidence_spec.rb b/spec/models/releases/evidence_spec.rb index 927e2e9bbe6..ca5d4b67b59 100644 --- a/spec/models/releases/evidence_spec.rb +++ b/spec/models/releases/evidence_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Releases::Evidence do +RSpec.describe Releases::Evidence do let_it_be(:project) { create(:project) } let(:release) { create(:release, project: project) } diff --git a/spec/models/releases/link_spec.rb b/spec/models/releases/link_spec.rb index 7533d1e6e5c..4dc1e53d59e 100644 --- a/spec/models/releases/link_spec.rb +++ b/spec/models/releases/link_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Releases::Link do +RSpec.describe Releases::Link do let(:release) { create(:release, project: project) } let(:project) { create(:project) } diff --git a/spec/models/releases/source_spec.rb b/spec/models/releases/source_spec.rb index d7af6fd90a6..d10b2140550 100644 --- a/spec/models/releases/source_spec.rb +++ b/spec/models/releases/source_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Releases::Source do +RSpec.describe Releases::Source do let_it_be(:project) { create(:project, :repository, name: 'finance-cal') } let(:tag_name) { 'v1.0' } diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index 6d163a16e63..ebc9760ab14 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe RemoteMirror, :mailer do +RSpec.describe RemoteMirror, :mailer do include GitHelpers describe 'URL validation' do diff --git a/spec/models/repository_language_spec.rb b/spec/models/repository_language_spec.rb index 13a4cd1e7cf..8cf5e17086d 100644 --- a/spec/models/repository_language_spec.rb +++ b/spec/models/repository_language_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe RepositoryLanguage do +RSpec.describe RepositoryLanguage do let(:repository_language) { build(:repository_language) } describe 'associations' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c698b40a4c0..964cc5a13ca 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Repository do +RSpec.describe Repository do include RepoHelpers include GitHelpers @@ -252,6 +252,21 @@ describe Repository do end end end + + context 'with filename with pathspec characters' do + let(:filename) { ':wq' } + let(:newrev) { project.repository.commit('master').sha } + + before do + create_file_in_repo(project, 'master', 'master', filename, 'Test file') + end + + subject { repository.last_commit_for_path('master', filename, literal_pathspec: true).id } + + it 'returns a commit SHA' do + expect(subject).to eq(newrev) + end + end end describe '#last_commit_id_for_path' do @@ -276,6 +291,21 @@ describe Repository do end end end + + context 'with filename with pathspec characters' do + let(:filename) { ':wq' } + let(:newrev) { project.repository.commit('master').sha } + + before do + create_file_in_repo(project, 'master', 'master', filename, 'Test file') + end + + subject { repository.last_commit_id_for_path('master', filename, literal_pathspec: true) } + + it 'returns a commit SHA' do + expect(subject).to eq(newrev) + end + end end describe '#commits' do @@ -2865,6 +2895,29 @@ describe Repository do end end + describe '#project' do + it 'returns the project for a project snippet' do + snippet = create(:project_snippet) + + expect(snippet.repository.project).to be(snippet.project) + end + + it 'returns nil for a personal snippet' do + snippet = create(:personal_snippet) + + expect(snippet.repository.project).to be_nil + end + + it 'returns the container if it is a project' do + expect(repository.project).to be(project) + end + + it 'returns nil if the container is not a project' do + expect(repository).to receive(:container).and_return(Group.new) + expect(repository.project).to be_nil + end + end + describe '#submodule_links' do it 'returns an instance of Gitlab::SubmoduleLinks' do expect(repository.submodule_links).to be_a(Gitlab::SubmoduleLinks) diff --git a/spec/models/resource_milestone_event_spec.rb b/spec/models/resource_milestone_event_spec.rb index 66686ec77d0..76ffb358d80 100644 --- a/spec/models/resource_milestone_event_spec.rb +++ b/spec/models/resource_milestone_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ResourceMilestoneEvent, type: :model do +RSpec.describe ResourceMilestoneEvent, type: :model do it_behaves_like 'a resource event' it_behaves_like 'a resource event for issues' it_behaves_like 'a resource event for merge requests' diff --git a/spec/models/resource_state_event_spec.rb b/spec/models/resource_state_event_spec.rb index 986a13cbd0d..1381b45cf9e 100644 --- a/spec/models/resource_state_event_spec.rb +++ b/spec/models/resource_state_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ResourceStateEvent, type: :model do +RSpec.describe ResourceStateEvent, type: :model do subject { build(:resource_state_event, issue: issue) } let(:issue) { create(:issue) } diff --git a/spec/models/review_spec.rb b/spec/models/review_spec.rb index 9dd8b90feee..2683dc93a4b 100644 --- a/spec/models/review_spec.rb +++ b/spec/models/review_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Review do +RSpec.describe Review do describe 'associations' do it { is_expected.to belong_to(:author).class_name('User').with_foreign_key(:author_id).inverse_of(:reviews) } it { is_expected.to belong_to(:merge_request).inverse_of(:reviews).touch(false) } diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index 20289afbeb5..0f1637016d6 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Route do +RSpec.describe Route do let(:group) { create(:group, path: 'git_lab', name: 'git_lab') } let(:route) { group.route } diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb index 087bc957373..aeafb49f8b5 100644 --- a/spec/models/sent_notification_spec.rb +++ b/spec/models/sent_notification_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SentNotification do +RSpec.describe SentNotification do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } diff --git a/spec/models/sentry_issue_spec.rb b/spec/models/sentry_issue_spec.rb index b4c1cf57761..33654bf5e1a 100644 --- a/spec/models/sentry_issue_spec.rb +++ b/spec/models/sentry_issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SentryIssue do +RSpec.describe SentryIssue do describe 'associations' do it { is_expected.to belong_to(:issue) } end diff --git a/spec/models/serverless/domain_cluster_spec.rb b/spec/models/serverless/domain_cluster_spec.rb index f5e1eb304a1..fdae0483c19 100644 --- a/spec/models/serverless/domain_cluster_spec.rb +++ b/spec/models/serverless/domain_cluster_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ::Serverless::DomainCluster do +RSpec.describe ::Serverless::DomainCluster do subject { create(:serverless_domain_cluster) } describe 'validations' do diff --git a/spec/models/serverless/domain_spec.rb b/spec/models/serverless/domain_spec.rb index ba54e05b4e3..f997b28b149 100644 --- a/spec/models/serverless/domain_spec.rb +++ b/spec/models/serverless/domain_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ::Serverless::Domain do +RSpec.describe ::Serverless::Domain do let(:function_name) { 'test-function' } let(:pages_domain_name) { 'serverless.gitlab.io' } let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: pages_domain_name) } diff --git a/spec/models/serverless/function_spec.rb b/spec/models/serverless/function_spec.rb index 810d4409a34..632f5eba5c3 100644 --- a/spec/models/serverless/function_spec.rb +++ b/spec/models/serverless/function_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ::Serverless::Function do +RSpec.describe ::Serverless::Function do let(:project) { create(:project) } let(:func) { described_class.new(project, 'test', 'test-ns') } diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb new file mode 100644 index 00000000000..ca57a5d4087 --- /dev/null +++ b/spec/models/service_desk_setting_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ServiceDeskSetting do + describe 'validations' do + subject(:service_desk_setting) { create(:service_desk_setting) } + + it { is_expected.to validate_presence_of(:project_id) } + 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('Big val').for(:project_key) } + + describe '.valid_issue_template' do + let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/issue_templates/service_desk.md' => 'template' }) } + + it 'is not valid if template does not exist' do + settings = build(:service_desk_setting, project: project, issue_template_key: 'invalid key') + + expect(settings).not_to be_valid + expect(settings.errors[:issue_template_key].first).to eq('is empty or does not exist') + end + + it 'is valid if template exists' do + settings = build(:service_desk_setting, project: project, issue_template_key: 'service_desk') + + expect(settings).to be_valid + end + end + end + + describe 'associations' do + it { is_expected.to belong_to(:project) } + end +end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 8698a6cf3d3..75bbb074526 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Service do +RSpec.describe Service do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -304,8 +304,6 @@ describe Service do end describe 'build issue tracker from an integration' do - let(:title) { 'custom title' } - let(:description) { 'custom description' } let(:url) { 'http://jira.example.com' } let(:api_url) { 'http://api-jira.example.com' } let(:username) { 'jira-username' } @@ -322,8 +320,6 @@ describe Service do service = described_class.build_from_integration(project.id, integration) expect(service).to be_active - expect(service.title).to eq(title) - expect(service.description).to eq(description) expect(service.url).to eq(url) expect(service.api_url).to eq(api_url) expect(service.username).to eq(username) @@ -335,7 +331,7 @@ describe Service 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.merge(title: title, description: description) } + let(:properties) { data_params } let!(:integration) do create(:jira_service, :without_properties_callback, template: true, properties: properties.merge(additional: 'something')) end @@ -345,14 +341,14 @@ describe Service do context 'when data are stored in separated fields' do let(:integration) do - create(:jira_service, :template, data_params.merge(properties: {}, title: title, description: description)) + create(:jira_service, :template, data_params.merge(properties: {})) end it_behaves_like 'service creation from an integration' end context 'when data are stored in both properties and separated fields' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let(:integration) do create(:jira_service, :without_properties_callback, active: true, template: true, properties: properties).tap do |service| create(:jira_tracker_data, data_params.merge(service: service)) @@ -390,6 +386,33 @@ describe Service do end end + describe 'instance' do + describe '.instance_for' do + let_it_be(:jira_service) { create(:jira_service, :instance) } + let_it_be(:slack_service) { create(:slack_service, :instance) } + + subject { described_class.instance_for(type) } + + context 'Hipchat serivce' do + let(:type) { 'HipchatService' } + + it { is_expected.to eq(nil) } + end + + context 'Jira serivce' do + let(:type) { 'JiraService' } + + it { is_expected.to eq(jira_service) } + end + + context 'Slack serivce' do + let(:type) { 'SlackService' } + + it { is_expected.to eq(slack_service) } + end + end + end + describe "{property}_changed?" do let(:service) do BambooService.create( @@ -514,7 +537,6 @@ describe Service do let(:service) do GitlabIssueTrackerService.create( project: create(:project), - title: 'random title', project_url: 'http://gitlab.example.com' ) end @@ -523,10 +545,6 @@ describe Service do expect { service }.not_to raise_error end - it 'sets title correctly' do - expect(service.title).to eq('random title') - end - it 'sets data correctly' do expect(service.data_fields.project_url).to eq('http://gitlab.example.com') end diff --git a/spec/models/shard_spec.rb b/spec/models/shard_spec.rb index 4da86858b54..a9d11f4290c 100644 --- a/spec/models/shard_spec.rb +++ b/spec/models/shard_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Shard do +RSpec.describe Shard do describe '.populate!' do it 'creates shards based on the config file' do expect(described_class.all).to be_empty diff --git a/spec/models/snippet_blob_spec.rb b/spec/models/snippet_blob_spec.rb index 88441e39d45..19b985f66ee 100644 --- a/spec/models/snippet_blob_spec.rb +++ b/spec/models/snippet_blob_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SnippetBlob do +RSpec.describe SnippetBlob do let(:snippet) { create(:snippet) } subject { described_class.new(snippet) } diff --git a/spec/models/snippet_input_action_collection_spec.rb b/spec/models/snippet_input_action_collection_spec.rb index ef18ab5a810..3ec206bd031 100644 --- a/spec/models/snippet_input_action_collection_spec.rb +++ b/spec/models/snippet_input_action_collection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SnippetInputActionCollection do +RSpec.describe SnippetInputActionCollection do let(:action_name) { 'create' } let(:action) { { action: action_name, file_path: 'foo', content: 'bar', previous_path: 'foobar' } } let(:data) { [action, action] } diff --git a/spec/models/snippet_input_action_spec.rb b/spec/models/snippet_input_action_spec.rb index 5e379a48171..ca61b80df4c 100644 --- a/spec/models/snippet_input_action_spec.rb +++ b/spec/models/snippet_input_action_spec.rb @@ -2,18 +2,18 @@ require 'spec_helper' -describe SnippetInputAction do +RSpec.describe SnippetInputAction do describe 'validations' do using RSpec::Parameterized::TableSyntax where(:action, :file_path, :content, :previous_path, :allowed_actions, :is_valid, :invalid_field) do :create | 'foobar' | 'foobar' | 'foobar' | nil | true | nil - :move | 'foobar' | 'foobar' | 'foobar' | nil | true | nil + :move | 'foobar' | 'foobar' | 'foo1' | nil | true | nil :delete | 'foobar' | 'foobar' | 'foobar' | nil | true | nil :update | 'foobar' | 'foobar' | 'foobar' | nil | true | nil :foo | 'foobar' | 'foobar' | 'foobar' | nil | false | :action 'create' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil - 'move' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil + 'move' | 'foobar' | 'foobar' | 'foo1' | nil | true | nil 'delete' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil 'update' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil 'foo' | 'foobar' | 'foobar' | 'foobar' | nil | false | :action @@ -21,10 +21,16 @@ describe SnippetInputAction do '' | 'foobar' | 'foobar' | 'foobar' | nil | false | :action :move | 'foobar' | 'foobar' | nil | nil | false | :previous_path :move | 'foobar' | 'foobar' | '' | nil | false | :previous_path + :move | 'foobar' | 'foobar' | 'foobar' | nil | false | :file_path + :move | nil | 'foobar' | 'foobar' | nil | false | :file_path + :move | '' | 'foobar' | 'foobar' | nil | false | :file_path + :move | nil | 'foobar' | 'foo1' | nil | false | :file_path + :move | 'foobar' | nil | 'foo1' | nil | true | nil + :move | 'foobar' | '' | 'foo1' | nil | true | nil :create | 'foobar' | nil | 'foobar' | nil | false | :content :create | 'foobar' | '' | 'foobar' | nil | false | :content - :create | nil | 'foobar' | 'foobar' | nil | false | :file_path - :create | '' | 'foobar' | 'foobar' | nil | false | :file_path + :create | nil | 'foobar' | 'foobar' | nil | true | nil + :create | '' | 'foobar' | 'foobar' | nil | true | nil :update | 'foobar' | nil | 'foobar' | nil | false | :content :update | 'foobar' | '' | 'foobar' | nil | false | :content :update | 'other' | 'foobar' | 'foobar' | nil | false | :file_path diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb index b86a6f82f07..8c25d713c0a 100644 --- a/spec/models/snippet_repository_spec.rb +++ b/spec/models/snippet_repository_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SnippetRepository do +RSpec.describe SnippetRepository do let_it_be(:user) { create(:user) } let(:snippet) { create(:personal_snippet, :repository, author: user) } let(:snippet_repository) { snippet.snippet_repository } diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 4d6586c1df4..3f9c6981de1 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Snippet do +RSpec.describe Snippet do describe 'modules' do subject { described_class } @@ -20,6 +20,7 @@ describe Snippet do it { is_expected.to have_many(:award_emoji).dependent(:destroy) } it { is_expected.to have_many(:user_mentions).class_name("SnippetUserMention") } it { is_expected.to have_one(:snippet_repository) } + it { is_expected.to have_one(:statistics).class_name('SnippetStatistics').dependent(:destroy) } end describe 'validation' do @@ -91,6 +92,17 @@ describe Snippet do end end + describe 'callbacks' do + it 'creates snippet statistics when the snippet is created' do + snippet = build(:snippet) + expect(snippet.statistics).to be_nil + + snippet.save + + expect(snippet.statistics).to be_persisted + end + end + describe '#to_reference' do context 'when snippet belongs to a project' do let(:project) { build(:project, name: 'sample-project') } @@ -750,4 +762,29 @@ describe Snippet do end end end + + describe '#list_files' do + let_it_be(:snippet) { create(:snippet, :repository) } + let(:ref) { 'test-ref' } + + subject { snippet.list_files(ref) } + + context 'when snippet has a repository' do + it 'lists files from the repository with the ref' do + expect(snippet.repository).to receive(:ls_files).with(ref) + + subject + end + end + + context 'when snippet does not have a repository' do + before do + allow(snippet.repository).to receive(:empty?).and_return(true) + end + + it 'returns an empty array' do + expect(subject).to eq [] + end + end + end end diff --git a/spec/models/snippet_statistics_spec.rb b/spec/models/snippet_statistics_spec.rb new file mode 100644 index 00000000000..ad25bd7b3be --- /dev/null +++ b/spec/models/snippet_statistics_spec.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe SnippetStatistics do + let_it_be(:snippet_without_repo) { create(:snippet) } + let_it_be(:snippet_with_repo) { create(:snippet, :repository) } + + let(:statistics) { snippet_with_repo.statistics } + + it { is_expected.to belong_to(:snippet) } + it { is_expected.to validate_presence_of(:snippet) } + + describe '#update_commit_count' do + subject { statistics.update_commit_count } + + it 'updates the count of commits' do + commit_count = snippet_with_repo.repository.commit_count + + subject + + expect(statistics.commit_count).to eq commit_count + end + + context 'when the snippet does not have a repository' do + let(:statistics) { snippet_without_repo.statistics } + + it 'returns 0' do + expect(subject).to eq 0 + expect(statistics.commit_count).to eq 0 + end + end + end + + describe '#update_file_count' do + subject { statistics.update_file_count } + + it 'updates the count of files' do + file_count = snippet_with_repo.repository.ls_files(nil).count + + subject + + expect(statistics.file_count).to eq file_count + end + + context 'when the snippet does not have a repository' do + let(:statistics) { snippet_without_repo.statistics } + + it 'returns 0' do + expect(subject).to eq 0 + expect(statistics.file_count).to eq 0 + end + end + end + + describe '#update_repository_size' do + subject { statistics.update_repository_size } + + it 'updates the repository_size' do + repository_size = snippet_with_repo.repository.size.megabytes.to_i + + subject + + expect(statistics.repository_size).to eq repository_size + end + + context 'when the snippet does not have a repository' do + let(:statistics) { snippet_without_repo.statistics } + + it 'returns 0' do + expect(subject).to eq 0 + expect(statistics.repository_size).to eq 0 + end + end + end + + describe '#refresh!' do + subject { statistics.refresh! } + + it 'retrieves and saves statistic data from repository' do + expect(statistics).to receive(:update_commit_count) + expect(statistics).to receive(:update_file_count) + expect(statistics).to receive(:update_repository_size) + expect(statistics).to receive(:save!) + + subject + end + end + + context 'with a PersonalSnippet' do + let!(:snippet) { create(:personal_snippet, :repository) } + + shared_examples 'personal snippet statistics updates' do + it 'schedules a namespace statistics worker' do + expect(Namespaces::ScheduleAggregationWorker) + .to receive(:perform_async).once + + statistics.save! + end + + it 'does not try to update project stats' do + expect(statistics).not_to receive(:schedule_update_project_statistic) + + statistics.save! + end + end + + context 'when creating' do + let(:statistics) { build(:snippet_statistics, snippet_id: snippet.id, with_data: true) } + + before do + snippet.statistics.delete + end + + it_behaves_like 'personal snippet statistics updates' + end + + context 'when updating' do + let(:statistics) { snippet.statistics } + + before do + snippet.statistics.repository_size = 123 + end + + it_behaves_like 'personal snippet statistics updates' + end + end + + context 'with a ProjectSnippet' do + let!(:snippet) { create(:project_snippet) } + + it_behaves_like 'UpdateProjectStatistics' do + subject { build(:snippet_statistics, snippet: snippet, id: snippet.id, with_data: true) } + + before do + # The shared examples requires the snippet statistics not to be present + snippet.statistics.delete + snippet.reload + end + end + + it 'does not call personal snippet callbacks' do + expect(snippet.statistics).not_to receive(:update_author_root_storage_statistics) + expect(snippet.statistics).to receive(:schedule_update_project_statistic) + + snippet.statistics.update!(repository_size: 123) + end + end +end diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb index 8d0f247b5d6..97a0dc27f17 100644 --- a/spec/models/spam_log_spec.rb +++ b/spec/models/spam_log_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SpamLog do +RSpec.describe SpamLog do let_it_be(:admin) { create(:admin) } describe 'associations' do diff --git a/spec/models/ssh_host_key_spec.rb b/spec/models/ssh_host_key_spec.rb index a17cd8ba345..4d729d5585f 100644 --- a/spec/models/ssh_host_key_spec.rb +++ b/spec/models/ssh_host_key_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SshHostKey do +RSpec.describe SshHostKey do using RSpec::Parameterized::TableSyntax include ReactiveCachingHelpers diff --git a/spec/models/state_note_spec.rb b/spec/models/state_note_spec.rb index d3409315e41..bd07af7ceca 100644 --- a/spec/models/state_note_spec.rb +++ b/spec/models/state_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe StateNote do +RSpec.describe StateNote do describe '.from_event' do let_it_be(:author) { create(:user) } let_it_be(:project) { create(:project, :repository) } @@ -10,18 +10,62 @@ describe StateNote do ResourceStateEvent.states.each do |state, _value| context "with event state #{state}" do - let_it_be(:event) { create(:resource_state_event, issue: noteable, state: state, created_at: '2020-02-05') } + let(:event) { create(:resource_state_event, issue: noteable, state: state, created_at: '2020-02-05') } subject { described_class.from_event(event, resource: noteable, resource_parent: project) } - it_behaves_like 'a system note', exclude_project: true do - let(:action) { state.to_s } + it_behaves_like 'a synthetic note', state == 'reopened' ? 'opened' : state + + it 'contains the expected values' do + expect(subject.author).to eq(author) + expect(subject.created_at).to eq(event.created_at) + expect(subject.note).to eq(state) + end + end + end + + context 'with a mentionable source' do + subject { described_class.from_event(event, resource: noteable, resource_parent: project) } + + context 'with a commit' do + let(:commit) { create(:commit, project: project) } + let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', source_commit: commit.id) } + + it 'contains the expected values' do + expect(subject.author).to eq(author) + expect(subject.created_at).to eq(subject.created_at) + expect(subject.note).to eq("closed via commit #{commit.id}") + end + end + + context 'with a merge request' do + let(:merge_request) { create(:merge_request, source_project: project) } + let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', source_merge_request: merge_request) } + + it 'contains the expected values' do + expect(subject.author).to eq(author) + expect(subject.created_at).to eq(event.created_at) + expect(subject.note).to eq("closed via merge request !#{merge_request.iid}") + end + end + + context 'when closed by error tracking' do + let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', close_after_error_tracking_resolve: true) } + + it 'contains the expected values' do + expect(subject.author).to eq(author) + expect(subject.created_at).to eq(event.created_at) + expect(subject.note).to eq('resolved the corresponding error and closed the issue.') end + end + + context 'when closed by promotheus alert' do + let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', close_auto_resolve_prometheus_alert: true) } it 'contains the expected values' do expect(subject.author).to eq(author) expect(subject.created_at).to eq(event.created_at) - expect(subject.note_html).to eq("<p dir=\"auto\">#{state}</p>") + expect(subject.note).to eq('automatically closed this issue because the alert resolved.') end end end diff --git a/spec/models/subscription_spec.rb b/spec/models/subscription_spec.rb index 41bd48810b2..be85e6e10f4 100644 --- a/spec/models/subscription_spec.rb +++ b/spec/models/subscription_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Subscription do +RSpec.describe Subscription do describe 'relationships' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:subscribable) } diff --git a/spec/models/suggestion_spec.rb b/spec/models/suggestion_spec.rb index 2ac3ae0a5ad..6c30bc39c1d 100644 --- a/spec/models/suggestion_spec.rb +++ b/spec/models/suggestion_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Suggestion do +RSpec.describe Suggestion do let(:suggestion) { create(:suggestion) } describe 'associations' do @@ -38,26 +38,106 @@ describe Suggestion do end describe '#appliable?' do - context 'when patch is already applied' do - let(:suggestion) { create(:suggestion, :applied) } + let(:suggestion) { build(:suggestion) } - it 'returns false' do - expect(suggestion).not_to be_appliable + subject(:appliable) { suggestion.appliable? } + + before do + allow(suggestion).to receive(:inapplicable_reason).and_return(inapplicable_reason) + end + + context 'when inapplicable_reason is nil' do + let(:inapplicable_reason) { nil } + + it { is_expected.to be_truthy } + end + + context 'when inapplicable_reason is not nil' do + let(:inapplicable_reason) { :applied } + + it { is_expected.to be_falsey } + end + end + + describe '#inapplicable_reason' do + let(:merge_request) { create(:merge_request) } + + let!(:note) do + create( + :diff_note_on_merge_request, + project: merge_request.project, + noteable: merge_request + ) + end + + let(:suggestion) { build(:suggestion, note: note) } + + subject(:inapplicable_reason) { suggestion.inapplicable_reason } + + context 'when suggestion is already applied' do + let(:suggestion) { build(:suggestion, :applied, note: note) } + + it { is_expected.to eq(:applied) } + end + + context 'when merge request was merged' do + before do + merge_request.mark_as_merged! + end + + it { is_expected.to eq(:merge_request_merged) } + end + + context 'when merge request is closed' do + before do + merge_request.close! end + + it { is_expected.to eq(:merge_request_closed) } end - context 'when merge request is not opened' do - let(:merge_request) { create(:merge_request, :merged) } - let(:note) do - create(:diff_note_on_merge_request, project: merge_request.project, - noteable: merge_request) + context 'when source branch is deleted' do + before do + merge_request.project.repository.rm_branch(merge_request.author, merge_request.source_branch) end - let(:suggestion) { create(:suggestion, note: note) } + it { is_expected.to eq(:source_branch_deleted) } + end - it 'returns false' do - expect(suggestion).not_to be_appliable + context 'when content is outdated' do + before do + allow(suggestion).to receive(:outdated?).and_return(true) + end + + it { is_expected.to eq(:outdated) } + end + + context 'when note is outdated' do + before do + allow(note).to receive(:active?).and_return(false) end + + it { is_expected.to eq(:outdated) } + end + + context 'when applicable' do + it { is_expected.to be_nil } + end + end + + describe '#single_line?' do + subject(:single_line) { suggestion.single_line? } + + context 'when suggestion is for a single line' do + let(:suggestion) { build(:suggestion, lines_above: 0, lines_below: 0) } + + it { is_expected.to eq(true) } + end + + context 'when suggestion is for multiple lines' do + let(:suggestion) { build(:suggestion, lines_above: 2, lines_below: 0) } + + it { is_expected.to eq(false) } end end end diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb index 801f139355b..9a6b57afb97 100644 --- a/spec/models/system_note_metadata_spec.rb +++ b/spec/models/system_note_metadata_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SystemNoteMetadata do +RSpec.describe SystemNoteMetadata do describe 'associations' do it { is_expected.to belong_to(:note) } it { is_expected.to belong_to(:description_version) } diff --git a/spec/models/term_agreement_spec.rb b/spec/models/term_agreement_spec.rb index 42a48048b67..98c7a2daadd 100644 --- a/spec/models/term_agreement_spec.rb +++ b/spec/models/term_agreement_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe TermAgreement do +RSpec.describe TermAgreement do describe 'validations' do it { is_expected.to validate_presence_of(:term) } it { is_expected.to validate_presence_of(:user) } diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb index 3cd15e23ee2..00e67ad70db 100644 --- a/spec/models/terraform/state_spec.rb +++ b/spec/models/terraform/state_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Terraform::State do +RSpec.describe Terraform::State do subject { create(:terraform_state, :with_file) } let(:terraform_state_file) { fixture_file('terraform/terraform.tfstate') } diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index bda89fc01f3..44e81455a67 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Todo do +RSpec.describe Todo do let(:issue) { create(:issue) } describe 'relationships' do diff --git a/spec/models/tree_spec.rb b/spec/models/tree_spec.rb index 7dde8459f9a..1522d836f76 100644 --- a/spec/models/tree_spec.rb +++ b/spec/models/tree_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Tree do +RSpec.describe Tree do let(:repository) { create(:project, :repository).repository } let(:sha) { repository.root_ref } diff --git a/spec/models/trending_project_spec.rb b/spec/models/trending_project_spec.rb index 39f5d686eb4..802f8befbcd 100644 --- a/spec/models/trending_project_spec.rb +++ b/spec/models/trending_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe TrendingProject do +RSpec.describe TrendingProject do let(:user) { create(:user) } let(:public_project1) { create(:project, :public, :repository) } let(:public_project2) { create(:project, :public, :repository) } diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index 8a64948d570..18388b4cd83 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Upload do +RSpec.describe Upload do describe 'associations' do it { is_expected.to belong_to(:model) } end diff --git a/spec/models/uploads/fog_spec.rb b/spec/models/uploads/fog_spec.rb index 72a169280af..899e6f2064c 100644 --- a/spec/models/uploads/fog_spec.rb +++ b/spec/models/uploads/fog_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Uploads::Fog do +RSpec.describe Uploads::Fog do let(:data_store) { described_class.new } before do diff --git a/spec/models/uploads/local_spec.rb b/spec/models/uploads/local_spec.rb index 374c3019edc..d354b252b39 100644 --- a/spec/models/uploads/local_spec.rb +++ b/spec/models/uploads/local_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Uploads::Local do +RSpec.describe Uploads::Local do let(:data_store) { described_class.new } before do diff --git a/spec/models/user_agent_detail_spec.rb b/spec/models/user_agent_detail_spec.rb index 5c28511b446..e3f3d9c342b 100644 --- a/spec/models/user_agent_detail_spec.rb +++ b/spec/models/user_agent_detail_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserAgentDetail do +RSpec.describe UserAgentDetail do describe '.submittable?' do it 'is submittable when not already submitted' do detail = build(:user_agent_detail) diff --git a/spec/models/user_callout_spec.rb b/spec/models/user_callout_spec.rb index a084b1ac662..cdf70dd5190 100644 --- a/spec/models/user_callout_spec.rb +++ b/spec/models/user_callout_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserCallout do +RSpec.describe UserCallout do let!(:callout) { create(:user_callout) } it_behaves_like 'having unique enum values' diff --git a/spec/models/user_canonical_email_spec.rb b/spec/models/user_canonical_email_spec.rb index 54a4e968033..8e26f68c09b 100644 --- a/spec/models/user_canonical_email_spec.rb +++ b/spec/models/user_canonical_email_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserCanonicalEmail do +RSpec.describe UserCanonicalEmail do it { is_expected.to belong_to(:user) } describe 'validations' do diff --git a/spec/models/user_custom_attribute_spec.rb b/spec/models/user_custom_attribute_spec.rb index d0981b2d771..1a51ad662b0 100644 --- a/spec/models/user_custom_attribute_spec.rb +++ b/spec/models/user_custom_attribute_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserCustomAttribute do +RSpec.describe UserCustomAttribute do describe 'assocations' do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/user_detail_spec.rb b/spec/models/user_detail_spec.rb index 2b2bfff7be2..041af5b9c31 100644 --- a/spec/models/user_detail_spec.rb +++ b/spec/models/user_detail_spec.rb @@ -2,13 +2,42 @@ require 'spec_helper' -describe UserDetail do +RSpec.describe UserDetail do it { is_expected.to belong_to(:user) } describe 'validations' do - describe 'job_title' do + describe '#job_title' do it { is_expected.not_to validate_presence_of(:job_title) } it { is_expected.to validate_length_of(:job_title).is_at_most(200) } end + + describe '#bio' do + it { is_expected.to validate_length_of(:bio).is_at_most(255) } + end + end + + describe '#bio_html' do + let(:user) { create(:user, bio: 'some **bio**') } + + subject { user.user_detail.bio_html } + + it 'falls back to #bio when the html representation is missing' do + user.user_detail.update!(bio_html: nil) + + expect(subject).to eq(user.user_detail.bio) + end + + it 'stores rendered html' do + expect(subject).to include('some <strong>bio</strong>') + end + + it 'does not try to set the value when the column is not there' do + without_bio_html_column = UserDetail.column_names - ['bio_html'] + + expect(described_class).to receive(:column_names).at_least(:once).and_return(without_bio_html_column) + expect(user.user_detail).not_to receive(:bio_html=) + + subject + end end end diff --git a/spec/models/user_highest_role_spec.rb b/spec/models/user_highest_role_spec.rb index b3c795f6623..3ae672cf7f7 100644 --- a/spec/models/user_highest_role_spec.rb +++ b/spec/models/user_highest_role_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserHighestRole do +RSpec.describe UserHighestRole do describe 'associations' do it { is_expected.to belong_to(:user).required } end diff --git a/spec/models/user_interacted_project_spec.rb b/spec/models/user_interacted_project_spec.rb index 83c66bf1969..2fec8be76e8 100644 --- a/spec/models/user_interacted_project_spec.rb +++ b/spec/models/user_interacted_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserInteractedProject do +RSpec.describe UserInteractedProject do describe '.track' do subject { described_class.track(event) } diff --git a/spec/models/user_mentions/commit_user_mention_spec.rb b/spec/models/user_mentions/commit_user_mention_spec.rb index ebad3902d6b..91d28241650 100644 --- a/spec/models/user_mentions/commit_user_mention_spec.rb +++ b/spec/models/user_mentions/commit_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CommitUserMention do +RSpec.describe CommitUserMention do describe 'associations' do it { is_expected.to belong_to(:note) } end diff --git a/spec/models/user_mentions/issue_user_mention_spec.rb b/spec/models/user_mentions/issue_user_mention_spec.rb index ac29f3084b4..6faf598ee36 100644 --- a/spec/models/user_mentions/issue_user_mention_spec.rb +++ b/spec/models/user_mentions/issue_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe IssueUserMention do +RSpec.describe IssueUserMention do describe 'associations' do it { is_expected.to belong_to(:issue) } it { is_expected.to belong_to(:note) } diff --git a/spec/models/user_mentions/merge_request_user_mention_spec.rb b/spec/models/user_mentions/merge_request_user_mention_spec.rb index c5c7cebfaa5..10fcb126965 100644 --- a/spec/models/user_mentions/merge_request_user_mention_spec.rb +++ b/spec/models/user_mentions/merge_request_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe MergeRequestUserMention do +RSpec.describe MergeRequestUserMention do describe 'associations' do it { is_expected.to belong_to(:merge_request) } it { is_expected.to belong_to(:note) } diff --git a/spec/models/user_mentions/snippet_user_mention_spec.rb b/spec/models/user_mentions/snippet_user_mention_spec.rb index 0e34a2dd5a1..0762e731a53 100644 --- a/spec/models/user_mentions/snippet_user_mention_spec.rb +++ b/spec/models/user_mentions/snippet_user_mention_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe SnippetUserMention do +RSpec.describe SnippetUserMention do describe 'associations' do it { is_expected.to belong_to(:snippet) } it { is_expected.to belong_to(:note) } diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb index cf32d4eeca7..27ddaea763d 100644 --- a/spec/models/user_preference_spec.rb +++ b/spec/models/user_preference_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserPreference do +RSpec.describe UserPreference do let(:user_preference) { create(:user_preference) } describe 'notes filters global keys' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index dd4b174a38f..fa2e4b63648 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe User do +RSpec.describe User do include ProjectForksHelper include TermsHelper include ExclusiveLeaseHelpers @@ -58,6 +58,10 @@ describe User do it { is_expected.to delegate_method(:job_title).to(:user_detail).allow_nil } it { is_expected.to delegate_method(:job_title=).to(:user_detail).with_arguments(:args).allow_nil } + + it { is_expected.to delegate_method(:bio).to(:user_detail).allow_nil } + it { is_expected.to delegate_method(:bio=).to(:user_detail).with_arguments(:args).allow_nil } + it { is_expected.to delegate_method(:bio_html).to(:user_detail).allow_nil } end describe 'associations' do @@ -91,64 +95,28 @@ describe User do it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:user) } it { is_expected.to have_many(:reviews).inverse_of(:author) } - describe "#bio" do - it 'syncs bio with `user_details.bio` on create' do - user = create(:user, bio: 'my bio') - - expect(user.bio).to eq(user.user_detail.bio) + describe "#user_detail" do + it 'does not persist `user_detail` by default' do + expect(create(:user).user_detail).not_to be_persisted end - context 'when `migrate_bio_to_user_details` feature flag is off' do - before do - stub_feature_flags(migrate_bio_to_user_details: false) - end - - it 'does not sync bio with `user_details.bio`' do - user = create(:user, bio: 'my bio') + it 'creates `user_detail` when `bio` is given' do + user = create(:user, bio: 'my bio') - expect(user.bio).to eq('my bio') - expect(user.user_detail.bio).to eq('') - end + expect(user.user_detail).to be_persisted + expect(user.user_detail.bio).to eq('my bio') end - it 'syncs bio with `user_details.bio` on update' do - user = create(:user) - - user.update!(bio: 'my bio') + it 'delegates `bio` to `user_detail`' do + user = create(:user, bio: 'my bio') expect(user.bio).to eq(user.user_detail.bio) end - context 'when `user_details` association already exists' do - let(:user) { create(:user) } - - before do - create(:user_detail, user: user) - end - - it 'syncs bio with `user_details.bio`' do - user.update!(bio: 'my bio') - - expect(user.bio).to eq(user.user_detail.bio) - end - - it 'falls back to "" when nil is given' do - user.update!(bio: nil) - - expect(user.bio).to eq(nil) - expect(user.user_detail.bio).to eq('') - end - - # very unlikely scenario - it 'truncates long bio when syncing to user_details' do - invalid_bio = 'a' * 256 - truncated_bio = 'a' * 255 - - user.bio = invalid_bio - user.save(validate: false) + it 'creates `user_detail` when `bio` is first updated' do + user = create(:user) - expect(user.user_detail.bio).to eq(truncated_bio) - end + expect { user.update(bio: 'my bio') }.to change { user.user_detail.persisted? }.from(false).to(true) end end @@ -214,7 +182,7 @@ describe User do describe 'validations' do describe 'password' do - let!(:user) { create(:user) } + let!(:user) { build_stubbed(:user) } before do allow(Devise).to receive(:password_length).and_return(8..128) @@ -337,8 +305,6 @@ describe User do it { is_expected.not_to allow_value(-1).for(:projects_limit) } it { is_expected.not_to allow_value(Gitlab::Database::MAX_INT_VALUE + 1).for(:projects_limit) } - it { is_expected.to validate_length_of(:bio).is_at_most(255) } - it_behaves_like 'an object with email-formated attributes', :email do subject { build(:user) } end @@ -3745,6 +3711,12 @@ describe User do expect(user.namespace).not_to be_nil end + + it 'creates the namespace setting' do + user.save! + + expect(user.namespace.namespace_settings).to be_persisted + end end context 'for an existing user' do @@ -4634,7 +4606,8 @@ describe User do [ { state: 'blocked' }, { user_type: :ghost }, - { user_type: :alert_bot } + { user_type: :alert_bot }, + { user_type: :support_bot } ] end @@ -4688,6 +4661,7 @@ describe User do where(:user_type, :expected_result) do 'human' | true 'alert_bot' | false + 'support_bot' | false end with_them do @@ -4756,19 +4730,44 @@ describe User do end end - describe '#migration_bot' do - it 'creates the user if it does not exist' do - expect do - described_class.migration_bot - end.to change { User.where(user_type: :migration_bot).count }.by(1) + context 'bot users' do + shared_examples 'bot users' do |bot_type| + it 'creates the user if it does not exist' do + expect do + described_class.public_send(bot_type) + end.to change { User.where(user_type: bot_type).count }.by(1) + end + + it 'creates a route for the namespace of the created user' do + bot_user = described_class.public_send(bot_type) + + expect(bot_user.namespace.route).to be_present + end + + it 'does not create a new user if it already exists' do + described_class.public_send(bot_type) + + expect do + described_class.public_send(bot_type) + end.not_to change { User.count } + end end - it 'does not create a new user if it already exists' do - described_class.migration_bot + shared_examples 'bot user avatars' do |bot_type, avatar_filename| + it 'sets the custom avatar for the created bot' do + bot_user = described_class.public_send(bot_type) - expect do - described_class.migration_bot - end.not_to change { User.count } + expect(bot_user.avatar.url).to be_present + expect(bot_user.avatar.filename).to eq(avatar_filename) + end end + + it_behaves_like 'bot users', :alert_bot + it_behaves_like 'bot users', :support_bot + it_behaves_like 'bot users', :migration_bot + it_behaves_like 'bot users', :ghost + + it_behaves_like 'bot user avatars', :alert_bot, 'alert-bot.png' + it_behaves_like 'bot user avatars', :support_bot, 'support-bot.png' end end diff --git a/spec/models/user_status_spec.rb b/spec/models/user_status_spec.rb index fcc01cdae3d..2c0664bd165 100644 --- a/spec/models/user_status_spec.rb +++ b/spec/models/user_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UserStatus do +RSpec.describe UserStatus do it { is_expected.to validate_presence_of(:user) } it { is_expected.to allow_value('smirk').for(:emoji) } diff --git a/spec/models/users_statistics_spec.rb b/spec/models/users_statistics_spec.rb index 4437a5469c6..b4b7ddb7c63 100644 --- a/spec/models/users_statistics_spec.rb +++ b/spec/models/users_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe UsersStatistics do +RSpec.describe UsersStatistics do let(:users_statistics) { build(:users_statistics) } describe 'scopes' do diff --git a/spec/models/web_ide_terminal_spec.rb b/spec/models/web_ide_terminal_spec.rb index 4103a26c75a..149fce33f43 100644 --- a/spec/models/web_ide_terminal_spec.rb +++ b/spec/models/web_ide_terminal_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WebIdeTerminal do +RSpec.describe WebIdeTerminal do let(:build) { create(:ci_build) } subject { described_class.new(build) } diff --git a/spec/models/wiki_page/meta_spec.rb b/spec/models/wiki_page/meta_spec.rb index 0255dd802cf..aaac72cbc68 100644 --- a/spec/models/wiki_page/meta_spec.rb +++ b/spec/models/wiki_page/meta_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WikiPage::Meta do +RSpec.describe WikiPage::Meta do let_it_be(:project) { create(:project, :wiki_repo) } let_it_be(:other_project) { create(:project) } diff --git a/spec/models/wiki_page/slug_spec.rb b/spec/models/wiki_page/slug_spec.rb index 324dea6b320..cf256c67277 100644 --- a/spec/models/wiki_page/slug_spec.rb +++ b/spec/models/wiki_page/slug_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe WikiPage::Slug do +RSpec.describe WikiPage::Slug do let_it_be(:meta) { create(:wiki_page_meta) } describe 'Associations' do diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 8f2da8ff9a1..a2ca6441f28 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe WikiPage do +RSpec.describe WikiPage do let_it_be(:user) { create(:user) } let(:container) { create(:project, :wiki_repo) } let(:wiki) { Wiki.for_container(container, user) } @@ -864,6 +864,24 @@ describe WikiPage do end end + describe '#diffs' do + subject { existing_page } + + it 'returns a diff instance' do + diffs = subject.diffs(foo: 'bar') + + expect(diffs).to be_a(Gitlab::Diff::FileCollection::WikiPage) + expect(diffs.diffable).to be_a(Commit) + expect(diffs.diffable.id).to eq(subject.version.id) + expect(diffs.project).to be(subject.wiki) + expect(diffs.diff_options).to include( + expanded: true, + paths: [subject.path], + foo: 'bar' + ) + end + end + private def get_slugs(page_or_dir) diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb index 3dad957a1ce..00a0f92e848 100644 --- a/spec/models/zoom_meeting_spec.rb +++ b/spec/models/zoom_meeting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe ZoomMeeting do +RSpec.describe ZoomMeeting do let(:project) { build(:project) } describe 'Factory' do |