diff options
Diffstat (limited to 'spec/models')
21 files changed, 482 insertions, 421 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index bc81c34f7ab..89d18abee27 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -30,12 +30,6 @@ describe Ci::Build do it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) } it { is_expected.to include_module(Ci::PipelineDelegator) } - it { is_expected.to be_a(ArtifactMigratable) } - - it_behaves_like 'UpdateProjectStatistics' do - subject { FactoryBot.build(:ci_build, pipeline: pipeline, artifacts_size: 23) } - end - describe 'associations' do it 'has a bidirectional relationship with projects' do expect(described_class.reflect_on_association(:project).has_inverse?).to eq(:builds) @@ -116,24 +110,6 @@ describe Ci::Build do end end - context 'when job has a legacy archive' do - let!(:job) { create(:ci_build, :legacy_artifacts) } - - it 'returns the job' do - is_expected.to include(job) - end - - context 'when ci_enable_legacy_artifacts feature flag is disabled' do - before do - stub_feature_flags(ci_enable_legacy_artifacts: false) - end - - it 'does not return the job' do - is_expected.not_to include(job) - end - end - end - context 'when job has a job artifact archive' do let!(:job) { create(:ci_build, :artifacts) } @@ -464,51 +440,11 @@ describe Ci::Build do end end end - - context 'when legacy artifacts are used' do - let(:build) { create(:ci_build, :legacy_artifacts) } - - subject { build.artifacts? } - - context 'is expired' do - let(:build) { create(:ci_build, :legacy_artifacts, :expired) } - - it { is_expected.to be_falsy } - end - - context 'artifacts archive does not exist' do - let(:build) { create(:ci_build) } - - it { is_expected.to be_falsy } - end - - context 'artifacts archive exists' do - let(:build) { create(:ci_build, :legacy_artifacts) } - - it { is_expected.to be_truthy } - - context 'when ci_enable_legacy_artifacts feature flag is disabled' do - before do - stub_feature_flags(ci_enable_legacy_artifacts: false) - end - - it { is_expected.to be_falsy } - end - end - end end describe '#browsable_artifacts?' do subject { build.browsable_artifacts? } - context 'artifacts metadata does not exist' do - before do - build.update(legacy_artifacts_metadata: nil) - end - - it { is_expected.to be_falsy } - end - context 'artifacts metadata does exists' do let(:build) { create(:ci_build, :artifacts) } @@ -764,12 +700,6 @@ describe Ci::Build do it { is_expected.to be_truthy } end - - context 'when build does not have job artifacts' do - let(:build) { create(:ci_build, :legacy_artifacts) } - - it { is_expected.to be_falsy } - end end describe '#has_old_trace?' do @@ -1096,11 +1026,11 @@ describe Ci::Build do describe 'erasable build' do shared_examples 'erasable' do it 'removes artifact file' do - expect(build.artifacts_file.exists?).to be_falsy + expect(build.artifacts_file.present?).to be_falsy end it 'removes artifact metadata file' do - expect(build.artifacts_metadata.exists?).to be_falsy + expect(build.artifacts_metadata.present?).to be_falsy end it 'removes all job_artifacts' do @@ -1192,7 +1122,7 @@ describe Ci::Build do let!(:build) { create(:ci_build, :success, :artifacts) } before do - build.remove_artifacts_metadata! + build.erase_erasable_artifacts! end describe '#erase' do @@ -1203,76 +1133,6 @@ describe Ci::Build do end end end - - context 'old artifacts' do - context 'build is erasable' do - context 'new artifacts' do - let!(:build) { create(:ci_build, :trace_artifact, :success, :legacy_artifacts) } - - describe '#erase' do - before do - build.erase(erased_by: erased_by) - end - - context 'erased by user' do - let!(:erased_by) { create(:user, username: 'eraser') } - - include_examples 'erasable' - - it 'records user who erased a build' do - expect(build.erased_by).to eq erased_by - end - end - - context 'erased by system' do - let(:erased_by) { nil } - - include_examples 'erasable' - - it 'does not set user who erased a build' do - expect(build.erased_by).to be_nil - end - end - end - - describe '#erasable?' do - subject { build.erasable? } - it { is_expected.to be_truthy } - end - - describe '#erased?' do - let!(:build) { create(:ci_build, :trace_artifact, :success, :legacy_artifacts) } - subject { build.erased? } - - context 'job has not been erased' do - it { is_expected.to be_falsey } - end - - context 'job has been erased' do - before do - build.erase - end - - it { is_expected.to be_truthy } - end - end - - context 'metadata and build trace are not available' do - let!(:build) { create(:ci_build, :success, :legacy_artifacts) } - - before do - build.remove_artifacts_metadata! - end - - describe '#erase' do - it 'does not raise error' do - expect { build.erase }.not_to raise_error - end - end - end - end - end - end end describe '#erase_erasable_artifacts!' do @@ -3490,6 +3350,18 @@ describe Ci::Build do end end + describe '#report_artifacts' do + subject { build.report_artifacts } + + context 'when the build has reports' do + let!(:report) { create(:ci_job_artifact, :codequality, job: build) } + + it 'returns the artifacts with reports' do + expect(subject).to contain_exactly(report) + end + end + end + describe '#artifacts_metadata_entry' do set(:build) { create(:ci_build, project: project) } let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' } diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 5964f66c398..1ba66565e03 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -5,10 +5,6 @@ require 'spec_helper' describe Ci::JobArtifact do let(:artifact) { create(:ci_job_artifact, :archive) } - it_behaves_like 'UpdateProjectStatistics' do - subject { build(:ci_job_artifact, :archive, size: 106365) } - end - describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:job) } @@ -23,6 +19,25 @@ describe Ci::JobArtifact do it_behaves_like 'having unique enum values' + it_behaves_like 'UpdateProjectStatistics' do + subject { build(:ci_job_artifact, :archive, size: 106365) } + end + + describe '.with_reports' do + let!(:artifact) { create(:ci_job_artifact, :archive) } + + subject { described_class.with_reports } + + it { is_expected.to be_empty } + + context 'when there are reports' do + let!(:metrics_report) { create(:ci_job_artifact, :junit) } + let!(:codequality_report) { create(:ci_job_artifact, :codequality) } + + it { is_expected.to eq([metrics_report, codequality_report]) } + end + end + describe '.test_reports' do subject { described_class.test_reports } diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 42d4769a921..6382be73ea7 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -48,32 +48,116 @@ describe Ci::PipelineSchedule do end end + describe '.runnable_schedules' do + subject { described_class.runnable_schedules } + + let!(:pipeline_schedule) do + Timecop.freeze(1.day.ago) do + create(:ci_pipeline_schedule, :hourly) + end + end + + it 'returns the runnable schedule' do + is_expected.to eq([pipeline_schedule]) + end + + context 'when there are no runnable schedules' do + let!(:pipeline_schedule) { } + + it 'returns an empty array' do + is_expected.to be_empty + end + end + end + + describe '.preloaded' do + subject { described_class.preloaded } + + before do + create_list(:ci_pipeline_schedule, 3) + end + + it 'preloads the associations' do + subject + + query = ActiveRecord::QueryRecorder.new { subject.each(&:project) } + + expect(query.count).to eq(2) + end + end + describe '#set_next_run_at' do - let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) } + let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) } + let(:ideal_next_run_at) { pipeline_schedule.send(:ideal_next_run_at) } + + let(:expected_next_run_at) do + Gitlab::Ci::CronParser.new(Settings.cron_jobs['pipeline_schedule_worker']['cron'], Time.zone.name) + .next_time_from(ideal_next_run_at) + end + + let(:cron_worker_next_run_at) do + Gitlab::Ci::CronParser.new(Settings.cron_jobs['pipeline_schedule_worker']['cron'], Time.zone.name) + .next_time_from(Time.now) + end context 'when creates new pipeline schedule' do - let(:expected_next_run_at) do - Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone) - .next_time_from(Time.now) + it 'updates next_run_at automatically' do + expect(pipeline_schedule.next_run_at).to eq(expected_next_run_at) end + end - it 'updates next_run_at automatically' do - expect(described_class.last.next_run_at).to eq(expected_next_run_at) + context 'when PipelineScheduleWorker runs at a specific interval' do + before do + allow(Settings).to receive(:cron_jobs) do + { + 'pipeline_schedule_worker' => { + 'cron' => '0 1 2 3 *' + } + } + end + end + + it "updates next_run_at to the sidekiq worker's execution time" do + expect(pipeline_schedule.next_run_at.min).to eq(0) + expect(pipeline_schedule.next_run_at.hour).to eq(1) + expect(pipeline_schedule.next_run_at.day).to eq(2) + expect(pipeline_schedule.next_run_at.month).to eq(3) end end - context 'when updates cron of exsisted pipeline schedule' do - let(:new_cron) { '0 0 1 1 *' } + context 'when pipeline schedule runs every minute' do + let(:pipeline_schedule) { create(:ci_pipeline_schedule, :every_minute) } - let(:expected_next_run_at) do - Gitlab::Ci::CronParser.new(new_cron, pipeline_schedule.cron_timezone) - .next_time_from(Time.now) + it "updates next_run_at to the sidekiq worker's execution time" do + expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at) + end + end + + context 'when there are two different pipeline schedules in different time zones' do + let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'Eastern Time (US & Canada)') } + let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } + + it 'sets different next_run_at' do + expect(pipeline_schedule_1.next_run_at).not_to eq(pipeline_schedule_2.next_run_at) + end + end + + context 'when there are two different pipeline schedules in the same time zones' do + let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } + let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') } + + it 'sets the sames next_run_at' do + expect(pipeline_schedule_1.next_run_at).to eq(pipeline_schedule_2.next_run_at) end + end + + context 'when updates cron of exsisted pipeline schedule' do + let(:new_cron) { '0 0 1 1 *' } it 'updates next_run_at automatically' do pipeline_schedule.update!(cron: new_cron) - expect(described_class.last.next_run_at).to eq(expected_next_run_at) + expect(pipeline_schedule.next_run_at).to eq(expected_next_run_at) end end end @@ -83,10 +167,11 @@ describe Ci::PipelineSchedule do context 'when reschedules after 10 days from now' do let(:future_time) { 10.days.from_now } + let(:ideal_next_run_at) { pipeline_schedule.send(:ideal_next_run_at) } let(:expected_next_run_at) do - Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone) - .next_time_from(future_time) + Gitlab::Ci::CronParser.new(Settings.cron_jobs['pipeline_schedule_worker']['cron'], Time.zone.name) + .next_time_from(ideal_next_run_at) end it 'points to proper next_run_at' do @@ -99,38 +184,6 @@ describe Ci::PipelineSchedule do end end - describe '#real_next_run' do - subject do - described_class.last.real_next_run(worker_cron: worker_cron, - worker_time_zone: worker_time_zone) - end - - context 'when GitLab time_zone is UTC' do - before do - allow(Time).to receive(:zone) - .and_return(ActiveSupport::TimeZone[worker_time_zone]) - end - - let(:worker_time_zone) { 'UTC' } - - context 'when cron_timezone is Eastern Time (US & Canada)' do - before do - create(:ci_pipeline_schedule, :nightly, - cron_timezone: 'Eastern Time (US & Canada)') - end - - let(:worker_cron) { '0 1 2 3 *' } - - it 'returns the next time worker executes' do - expect(subject.min).to eq(0) - expect(subject.hour).to eq(1) - expect(subject.day).to eq(2) - expect(subject.month).to eq(3) - end - end - end - end - describe '#job_variables' do let!(:pipeline_schedule) { create(:ci_pipeline_schedule) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index a0319b3eb0a..a8701f0efa4 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1381,6 +1381,40 @@ describe Ci::Pipeline, :mailer do end end + describe 'auto merge' do + let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) } + + let(:pipeline) do + create(:ci_pipeline, :running, project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha) + end + + before do + merge_request.update_head_pipeline + end + + %w[succeed! drop! cancel! skip!].each do |action| + context "when the pipeline recieved #{action} event" do + it 'performs AutoMergeProcessWorker' do + expect(AutoMergeProcessWorker).to receive(:perform_async).with(merge_request.id) + + pipeline.public_send(action) + end + end + end + + context 'when auto merge is not enabled in the merge request' do + let(:merge_request) { create(:merge_request) } + + it 'performs AutoMergeProcessWorker' do + expect(AutoMergeProcessWorker).not_to receive(:perform_async) + + pipeline.succeed! + end + end + end + def create_build(name, *traits, queued_at: current, started_from: 0, **opts) create(:ci_build, *traits, name: name, diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index d5974f47190..b38cf96de7e 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -3,9 +3,6 @@ require 'rails_helper' describe Clusters::Applications::Knative do - include KubernetesHelpers - include ReactiveCachingHelpers - let(:knative) { create(:clusters_applications_knative) } include_examples 'cluster application core specs', :clusters_applications_knative @@ -146,77 +143,4 @@ describe Clusters::Applications::Knative do describe 'validations' do it { is_expected.to validate_presence_of(:hostname) } end - - describe '#service_pod_details' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:service) { cluster.platform_kubernetes } - let(:knative) { create(:clusters_applications_knative, cluster: cluster) } - - let(:namespace) do - create(:cluster_kubernetes_namespace, - cluster: cluster, - cluster_project: cluster.cluster_project, - project: cluster.cluster_project.project) - end - - before do - stub_kubeclient_discover(service.api_url) - stub_kubeclient_knative_services - stub_kubeclient_service_pods - stub_reactive_cache(knative, - { - services: kube_response(kube_knative_services_body), - pods: kube_response(kube_knative_pods_body(cluster.cluster_project.project.name, namespace.namespace)) - }) - synchronous_reactive_cache(knative) - end - - it 'is able k8s core for pod details' do - expect(knative.service_pod_details(namespace.namespace, cluster.cluster_project.project.name)).not_to be_nil - end - end - - describe '#services' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:service) { cluster.platform_kubernetes } - let(:knative) { create(:clusters_applications_knative, cluster: cluster) } - - let(:namespace) do - create(:cluster_kubernetes_namespace, - cluster: cluster, - cluster_project: cluster.cluster_project, - project: cluster.cluster_project.project) - end - - subject { knative.services } - - before do - stub_kubeclient_discover(service.api_url) - stub_kubeclient_knative_services - stub_kubeclient_service_pods - end - - it 'has an unintialized cache' do - is_expected.to be_nil - end - - context 'when using synchronous reactive cache' do - before do - stub_reactive_cache(knative, - { - services: kube_response(kube_knative_services_body), - pods: kube_response(kube_knative_pods_body(cluster.cluster_project.project.name, namespace.namespace)) - }) - synchronous_reactive_cache(knative) - end - - it 'has cached services' do - is_expected.not_to be_nil - end - - it 'matches our namespace' do - expect(knative.services_for(ns: namespace)).not_to be_nil - end - end - end end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 4739e62289a..f206bb41f45 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -38,6 +38,11 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it { is_expected.to respond_to :project } + it do + expect(subject.knative_services_finder(subject.project)) + .to be_instance_of(Clusters::KnativeServicesFinder) + end + describe '.enabled' do subject { described_class.enabled } diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index ee613b199ad..e17b98536fa 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -260,4 +260,16 @@ describe Noteable do end end end + + describe '.replyable_types' do + it 'exposes the replyable types' do + expect(described_class.replyable_types).to include('Issue', 'MergeRequest') + end + end + + describe '.resolvable_types' do + it 'exposes the replyable types' do + expect(described_class.resolvable_types).to include('MergeRequest') + end + end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index cc777cbf749..a5c7e9db2a1 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -93,6 +93,21 @@ describe Issue do end end + describe '#sort' do + let(:project) { create(:project) } + + context "by relative_position" do + let!(:issue) { create(:issue, project: project) } + let!(:issue2) { create(:issue, project: project, relative_position: 2) } + let!(:issue3) { create(:issue, project: project, relative_position: 1) } + + it "sorts asc with nulls at the end" do + issues = project.issues.sort_by_attribute('relative_position') + expect(issues).to eq([issue3, issue2, issue]) + end + end + end + describe '#card_attributes' do it 'includes the author name' do allow(subject).to receive(:author).and_return(double(name: 'Robert')) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c72b6e9033d..956c5675f38 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -173,6 +173,42 @@ describe MergeRequest do end end + context 'for branch' do + before do + stub_feature_flags(stricter_mr_branch_name: false) + end + + using RSpec::Parameterized::TableSyntax + + where(:branch_name, :valid) do + 'foo' | true + 'foo:bar' | false + '+foo:bar' | false + 'foo bar' | false + '-foo' | false + 'HEAD' | true + 'refs/heads/master' | true + end + + with_them do + it "validates source_branch" do + subject = build(:merge_request, source_branch: branch_name, target_branch: 'master') + + subject.valid? + + expect(subject.errors.added?(:source_branch)).to eq(!valid) + end + + it "validates target_branch" do + subject = build(:merge_request, source_branch: 'master', target_branch: branch_name) + + subject.valid? + + expect(subject.errors.added?(:target_branch)).to eq(!valid) + end + end + end + context 'for forks' do let(:project) { create(:project) } let(:fork1) { fork_project(project) } @@ -1038,14 +1074,28 @@ describe MergeRequest do end end - describe "#reset_merge_when_pipeline_succeeds" do + describe "#auto_merge_strategy" do + subject { merge_request.auto_merge_strategy } + + let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) } + + it { is_expected.to eq('merge_when_pipeline_succeeds') } + + context 'when auto merge is disabled' do + let(:merge_request) { create(:merge_request) } + + it { is_expected.to be_nil } + end + end + + describe "#reset_auto_merge" do let(:merge_if_green) do create :merge_request, merge_when_pipeline_succeeds: true, merge_user: create(:user), merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" } end it "sets the item to false" do - merge_if_green.reset_merge_when_pipeline_succeeds + merge_if_green.reset_auto_merge merge_if_green.reload expect(merge_if_green.merge_when_pipeline_succeeds).to be_falsey @@ -1962,57 +2012,6 @@ describe MergeRequest do end end - describe '#check_if_can_be_merged' do - let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) } - - shared_examples 'checking if can be merged' do - context 'when it is not broken and has no conflicts' do - before do - allow(subject).to receive(:broken?) { false } - allow(project.repository).to receive(:can_be_merged?).and_return(true) - end - - it 'is marked as mergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged') - end - end - - context 'when broken' do - before do - allow(subject).to receive(:broken?) { true } - allow(project.repository).to receive(:can_be_merged?).and_return(false) - end - - it 'becomes unmergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') - end - end - - context 'when it has conflicts' do - before do - allow(subject).to receive(:broken?) { false } - allow(project.repository).to receive(:can_be_merged?).and_return(false) - end - - it 'becomes unmergeable' do - expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') - end - end - end - - context 'when merge_status is unchecked' do - subject { create(:merge_request, source_project: project, merge_status: :unchecked) } - - it_behaves_like 'checking if can be merged' - end - - context 'when merge_status is unchecked' do - subject { create(:merge_request, source_project: project, merge_status: :cannot_be_merged_recheck) } - - it_behaves_like 'checking if can be merged' - end - end - describe '#mergeable?' do let(:project) { create(:project) } @@ -2026,7 +2025,7 @@ describe MergeRequest do it 'return true if #mergeable_state? is true and the MR #can_be_merged? is true' do allow(subject).to receive(:mergeable_state?) { true } - expect(subject).to receive(:check_if_can_be_merged) + expect(subject).to receive(:check_mergeability) expect(subject).to receive(:can_be_merged?) { true } expect(subject.mergeable?).to be_truthy @@ -2040,7 +2039,7 @@ describe MergeRequest do it 'checks if merge request can be merged' do allow(subject).to receive(:mergeable_ci_state?) { true } - expect(subject).to receive(:check_if_can_be_merged) + expect(subject).to receive(:check_mergeability) subject.mergeable? end @@ -3108,38 +3107,6 @@ describe MergeRequest do end end - describe '#mergeable_to_ref?' do - it 'returns true when merge request is mergeable' do - subject = create(:merge_request) - - expect(subject.mergeable_to_ref?).to be(true) - end - - it 'returns false when merge request is already merged' do - subject = create(:merge_request, :merged) - - expect(subject.mergeable_to_ref?).to be(false) - end - - it 'returns false when merge request is closed' do - subject = create(:merge_request, :closed) - - expect(subject.mergeable_to_ref?).to be(false) - end - - it 'returns false when merge request is work in progress' do - subject = create(:merge_request, title: 'WIP: The feature') - - expect(subject.mergeable_to_ref?).to be(false) - end - - it 'returns false when merge request has no commits' do - subject = create(:merge_request, source_branch: 'empty-branch', target_branch: 'master') - - expect(subject.mergeable_to_ref?).to be(false) - end - end - describe '#merge_participants' do it 'contains author' do expect(subject.merge_participants).to eq([subject.author]) diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index b82368318f2..0fa4e470eef 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -31,12 +31,28 @@ describe Milestone do end describe 'start_date' do - it 'adds an error when start_date is greated then due_date' do + it 'adds an error when start_date is greater then due_date' do milestone = build(:milestone, start_date: Date.tomorrow, due_date: Date.yesterday) expect(milestone).not_to be_valid expect(milestone.errors[:due_date]).to include("must be greater than start date") end + + it 'adds an error when start_date is greater than 9999-12-31' do + milestone = build(:milestone, start_date: Date.new(10000, 1, 1)) + + expect(milestone).not_to be_valid + expect(milestone.errors[:start_date]).to include("date must not be after 9999-12-31") + end + end + + describe 'due_date' do + it 'adds an error when due_date is greater than 9999-12-31' do + milestone = build(:milestone, due_date: Date.new(10000, 1, 1)) + + expect(milestone).not_to be_valid + expect(milestone.errors[:due_date]).to include("date must not be after 9999-12-31") + end end end @@ -381,21 +397,6 @@ describe Milestone do expect(milestone_ids).to be_empty end end - - context 'when there is a milestone with a date after 294276 AD', :postgresql do - before do - past_milestone_project_1.update!(due_date: Date.new(294277, 1, 1)) - end - - it 'returns the next upcoming open milestone ID for each project and group' do - expect(milestone_ids).to contain_exactly( - current_milestone_project_1.id, - current_milestone_project_2.id, - current_milestone_group_1.id, - current_milestone_group_2.id - ) - end - end end describe '#to_reference' do @@ -519,4 +520,20 @@ describe Milestone do end end end + + describe '.reference_pattern' do + subject { described_class.reference_pattern } + + it { is_expected.to match('gitlab-org/gitlab-ce%123') } + it { is_expected.to match('gitlab-org/gitlab-ce%"my-milestone"') } + end + + describe '.link_reference_pattern' do + subject { described_class.link_reference_pattern } + + it { is_expected.to match("#{Gitlab.config.gitlab.url}/gitlab-org/gitlab-ce/milestones/123") } + it { is_expected.to match("#{Gitlab.config.gitlab.url}/gitlab-org/gitlab-ce/-/milestones/123") } + it { is_expected.not_to match("#{Gitlab.config.gitlab.url}/gitlab-org/gitlab-ce/issues/123") } + it { is_expected.not_to match("gitlab-org/gitlab-ce/milestones/123") } + end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index bfde367c47f..d80183af33e 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -147,6 +147,7 @@ describe Namespace do namespace: namespace, statistics: build(:project_statistics, repository_size: 101, + wiki_size: 505, lfs_objects_size: 202, build_artifacts_size: 303, packages_size: 404)) @@ -157,6 +158,7 @@ describe Namespace do namespace: namespace, statistics: build(:project_statistics, repository_size: 10, + wiki_size: 50, lfs_objects_size: 20, build_artifacts_size: 30, packages_size: 40)) @@ -167,8 +169,9 @@ describe Namespace do project2 statistics = described_class.with_statistics.find(namespace.id) - expect(statistics.storage_size).to eq 1110 + expect(statistics.storage_size).to eq 1665 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 @@ -179,6 +182,7 @@ describe Namespace do expect(statistics.storage_size).to eq 0 expect(statistics.repository_size).to eq 0 + expect(statistics.wiki_size).to eq 0 expect(statistics.lfs_objects_size).to eq 0 expect(statistics.build_artifacts_size).to eq 0 expect(statistics.packages_size).to eq 0 diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb index 7742e33e901..2c86c0ec7be 100644 --- a/spec/models/project_services/assembla_service_spec.rb +++ b/spec/models/project_services/assembla_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe AssemblaService do + include StubRequests + describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -23,12 +25,12 @@ describe AssemblaService do ) @sample_data = Gitlab::DataBuilder::Push.build_sample(project, user) @api_url = 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret' - WebMock.stub_request(:post, @api_url) + stub_full_request(@api_url, method: :post) end it "calls Assembla API" do @assembla_service.execute(@sample_data) - expect(WebMock).to have_requested(:post, @api_url).with( + expect(WebMock).to have_requested(:post, stubbed_hostname(@api_url)).with( body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/ ).once end diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 08c510f09df..65d227a17f9 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe BambooService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers + include StubRequests let(:bamboo_url) { 'http://gitlab.com/bamboo' } @@ -257,7 +258,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do end def stub_bamboo_request(url, status, body) - WebMock.stub_request(:get, url).to_return( + stub_full_request(url).to_return( status: status, headers: { 'Content-Type' => 'application/json' }, body: body diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 091d4d8f695..ca196069055 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe BuildkiteService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers + include StubRequests let(:project) { create(:project) } @@ -110,10 +111,9 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do body ||= %q({"status":"success"}) buildkite_full_url = 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123' - WebMock.stub_request(:get, buildkite_full_url).to_return( - status: status, - headers: { 'Content-Type' => 'application/json' }, - body: body - ) + stub_full_request(buildkite_full_url) + .to_return(status: status, + headers: { 'Content-Type' => 'application/json' }, + body: body) end end diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb index bf4c52fc7ab..0d3dd89e93b 100644 --- a/spec/models/project_services/campfire_service_spec.rb +++ b/spec/models/project_services/campfire_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe CampfireService do + include StubRequests + describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -49,39 +51,37 @@ describe CampfireService do it "calls Campfire API to get a list of rooms and speak in a room" do # make sure a valid list of rooms is returned body = File.read(Rails.root + 'spec/fixtures/project_services/campfire/rooms.json') - WebMock.stub_request(:get, @rooms_url).with(basic_auth: @auth).to_return( + + stub_full_request(@rooms_url).with(basic_auth: @auth).to_return( body: body, status: 200, headers: @headers ) + # stub the speak request with the room id found in the previous request's response speak_url = 'https://project-name.campfirenow.com/room/123/speak.json' - WebMock.stub_request(:post, speak_url).with(basic_auth: @auth) + stub_full_request(speak_url, method: :post).with(basic_auth: @auth) @campfire_service.execute(@sample_data) - expect(WebMock).to have_requested(:get, @rooms_url).once - expect(WebMock).to have_requested(:post, speak_url).with( - body: /#{project.path}.*#{@sample_data[:before]}.*#{@sample_data[:after]}/ - ).once + expect(WebMock).to have_requested(:get, stubbed_hostname(@rooms_url)).once + expect(WebMock).to have_requested(:post, stubbed_hostname(speak_url)) + .with(body: /#{project.path}.*#{@sample_data[:before]}.*#{@sample_data[:after]}/).once end it "calls Campfire API to get a list of rooms but shouldn't speak in a room" do # return a list of rooms that do not contain a room named 'test-room' body = File.read(Rails.root + 'spec/fixtures/project_services/campfire/rooms2.json') - WebMock.stub_request(:get, @rooms_url).with(basic_auth: @auth).to_return( + stub_full_request(@rooms_url).with(basic_auth: @auth).to_return( body: body, status: 200, headers: @headers ) - # we want to make sure no request is sent to the /speak endpoint, here is a basic - # regexp that matches this endpoint - speak_url = 'https://verySecret:X@project-name.campfirenow.com/room/.*/speak.json' @campfire_service.execute(@sample_data) - expect(WebMock).to have_requested(:get, @rooms_url).once - expect(WebMock).not_to have_requested(:post, /#{speak_url}/) + expect(WebMock).to have_requested(:get, 'https://8.8.8.9/rooms.json').once + expect(WebMock).not_to have_requested(:post, '*/room/.*/speak.json') end end end diff --git a/spec/models/project_services/pipelines_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb index ca17e7453b8..b85565e0c25 100644 --- a/spec/models/project_services/pipelines_email_service_spec.rb +++ b/spec/models/project_services/pipelines_email_service_spec.rb @@ -4,7 +4,11 @@ require 'spec_helper' describe PipelinesEmailService, :mailer do let(:pipeline) do - create(:ci_pipeline, project: project, sha: project.commit('master').sha) + create(:ci_pipeline, :failed, + project: project, + sha: project.commit('master').sha, + ref: project.default_branch + ) end let(:project) { create(:project, :repository) } @@ -84,12 +88,7 @@ describe PipelinesEmailService, :mailer do subject.test(data) end - context 'when pipeline is failed' do - before do - data[:object_attributes][:status] = 'failed' - pipeline.update(status: 'failed') - end - + context 'when pipeline is failed and on default branch' do it_behaves_like 'sending email' end @@ -101,6 +100,25 @@ describe PipelinesEmailService, :mailer do it_behaves_like 'sending email' end + + context 'when pipeline is failed and on a non-default branch' do + before do + data[:object_attributes][:ref] = 'not-the-default-branch' + pipeline.update(ref: 'not-the-default-branch') + end + + context 'with notify_only_default branch on' do + before do + subject.notify_only_default_branch = true + end + + it_behaves_like 'sending email' + end + + context 'with notify_only_default_branch off' do + it_behaves_like 'sending email' + end + end end describe '#execute' do @@ -110,11 +128,6 @@ describe PipelinesEmailService, :mailer do context 'with recipients' do context 'with failed pipeline' do - before do - data[:object_attributes][:status] = 'failed' - pipeline.update(status: 'failed') - end - it_behaves_like 'sending email' end @@ -133,11 +146,6 @@ describe PipelinesEmailService, :mailer do end context 'with failed pipeline' do - before do - data[:object_attributes][:status] = 'failed' - pipeline.update(status: 'failed') - end - it_behaves_like 'sending email' end @@ -150,6 +158,40 @@ describe PipelinesEmailService, :mailer do it_behaves_like 'not sending email' end end + + context 'with notify_only_default_branch off' do + context 'with default branch' do + it_behaves_like 'sending email' + end + + context 'with non default branch' do + before do + data[:object_attributes][:ref] = 'not-the-default-branch' + pipeline.update(ref: 'not-the-default-branch') + end + + it_behaves_like 'sending email' + end + end + + context 'with notify_only_default_branch on' do + before do + subject.notify_only_default_branch = true + end + + context 'with default branch' do + it_behaves_like 'sending email' + end + + context 'with non default branch' do + before do + data[:object_attributes][:ref] = 'not-the-default-branch' + pipeline.update(ref: 'not-the-default-branch') + end + + it_behaves_like 'not sending email' + end + end end context 'with empty recipients list' do diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb index 773b8b7890f..dde46c82df6 100644 --- a/spec/models/project_services/pivotaltracker_service_spec.rb +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe PivotaltrackerService do + include StubRequests + describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -53,12 +55,12 @@ describe PivotaltrackerService do end before do - WebMock.stub_request(:post, url) + stub_full_request(url, method: :post) end it 'posts correct message' do service.execute(push_data) - expect(WebMock).to have_requested(:post, url).with( + expect(WebMock).to have_requested(:post, stubbed_hostname(url)).with( body: { 'source_commit' => { 'commit_id' => '21c12ea', @@ -85,14 +87,14 @@ describe PivotaltrackerService do service.execute(push_data(branch: 'master')) service.execute(push_data(branch: 'v10')) - expect(WebMock).to have_requested(:post, url).twice + expect(WebMock).to have_requested(:post, stubbed_hostname(url)).twice end it 'does not post message if branch is not in the list' do service.execute(push_data(branch: 'mas')) service.execute(push_data(branch: 'v11')) - expect(WebMock).not_to have_requested(:post, url) + expect(WebMock).not_to have_requested(:post, stubbed_hostname(url)) end end end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index d2a45f48705..380f02739bc 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe PushoverService do + include StubRequests + describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -57,13 +59,13 @@ describe PushoverService do sound: sound ) - WebMock.stub_request(:post, api_url) + stub_full_request(api_url, method: :post, ip_address: '8.8.8.8') end it 'calls Pushover API' do pushover.execute(sample_data) - expect(WebMock).to have_requested(:post, api_url).once + expect(WebMock).to have_requested(:post, 'https://8.8.8.8/1/messages.json').once end end end diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 96dccae733b..1c434b25205 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe TeamcityService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers + include StubRequests let(:teamcity_url) { 'http://gitlab.com/teamcity' } @@ -212,7 +213,7 @@ describe TeamcityService, :use_clean_rails_memory_store_caching do body ||= %Q({"build":{"status":"#{build_status}","id":"666"}}) - WebMock.stub_request(:get, teamcity_full_url).with(basic_auth: auth).to_return( + stub_full_request(teamcity_full_url).with(basic_auth: auth).to_return( status: status, headers: { 'Content-Type' => 'application/json' }, body: body diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 08662231fdf..aad08b9d4aa 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1147,7 +1147,7 @@ describe Project do allow(project).to receive(:avatar_in_git) { true } end - let(:avatar_path) { "/#{project.full_path}/avatar" } + let(:avatar_path) { "/#{project.full_path}/-/avatar" } it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" } end @@ -3170,6 +3170,23 @@ describe Project do end end + describe '.ids_with_milestone_available_for' do + let!(:user) { create(:user) } + + it 'returns project ids with milestones available for user' do + project_1 = create(:project, :public, :merge_requests_disabled, :issues_disabled) + project_2 = create(:project, :public, :merge_requests_disabled) + project_3 = create(:project, :public, :issues_disabled) + project_4 = create(:project, :public) + project_4.project_feature.update(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE ) + + project_ids = described_class.ids_with_milestone_available_for(user).pluck(:id) + + expect(project_ids).to include(project_2.id, project_3.id) + expect(project_ids).not_to include(project_1.id, project_4.id) + end + end + describe '.with_feature_available_for_user' do let(:user) { create(:user) } let(:feature) { MergeRequest } diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index 738398a06f9..358873f9a2f 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -11,21 +11,37 @@ describe ProjectStatistics do it { is_expected.to belong_to(:namespace) } end + describe 'scopes' do + describe '.for_project_ids' do + it 'returns only requested projects' do + stats = create_list(:project_statistics, 3) + project_ids = stats[0..1].map { |s| s.project_id } + expected_ids = stats[0..1].map { |s| s.id } + + requested_stats = described_class.for_project_ids(project_ids).pluck(:id) + + expect(requested_stats).to eq(expected_ids) + end + end + end + describe 'statistics columns' do it "support values up to 8 exabytes" do statistics.update!( commit_count: 8.exabytes - 1, repository_size: 2.exabytes, + wiki_size: 1.exabytes, lfs_objects_size: 2.exabytes, - build_artifacts_size: 4.exabytes - 1 + build_artifacts_size: 3.exabytes - 1 ) statistics.reload expect(statistics.commit_count).to eq(8.exabytes - 1) 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(4.exabytes - 1) + expect(statistics.build_artifacts_size).to eq(3.exabytes - 1) expect(statistics.storage_size).to eq(8.exabytes - 1) end end @@ -33,6 +49,7 @@ describe ProjectStatistics do describe '#total_repository_size' do it "sums repository and LFS object size" do statistics.repository_size = 2 + statistics.wiki_size = 6 statistics.lfs_objects_size = 3 statistics.build_artifacts_size = 4 @@ -40,10 +57,17 @@ describe ProjectStatistics do end end + describe '#wiki_size' do + it "is initialized with not null value" do + expect(statistics.wiki_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_storage_size) end @@ -56,6 +80,7 @@ describe ProjectStatistics do it "sums all counters" 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_lfs_objects_size) end end @@ -69,6 +94,45 @@ describe ProjectStatistics do expect(statistics).to have_received(:update_lfs_objects_size) 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) + end + end + + context 'without repositories' do + it 'does not crash' do + expect(project.repository.exists?).to be_falsey + expect(project.wiki.repository.exists?).to be_falsey + + statistics.refresh! + + 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.repository_size).to eq(0) + expect(statistics.commit_count).to eq(0) + expect(statistics.wiki_size).to eq(0) + end + end + + context 'with deleted repositories' do + let(:project) { create(:project, :repository, :wiki_repo) } + + before do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + FileUtils.rm_rf(project.repository.path) + FileUtils.rm_rf(project.wiki.repository.path) + end + end + + it 'does not crash' do + statistics.refresh! + + 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.repository_size).to eq(0) + expect(statistics.commit_count).to eq(0) + expect(statistics.wiki_size).to eq(0) end end end @@ -95,6 +159,17 @@ describe ProjectStatistics do end end + describe '#update_wiki_size' do + before do + allow(project.wiki.repository).to receive(:size).and_return(34) + statistics.update_wiki_size + end + + it "stores the size of the wiki" do + expect(statistics.wiki_size).to eq 34.megabytes + 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) } @@ -114,12 +189,13 @@ describe ProjectStatistics do it "sums all storage counters" do statistics.update!( repository_size: 2, + wiki_size: 4, lfs_objects_size: 3 ) statistics.reload - expect(statistics.storage_size).to eq 5 + expect(statistics.storage_size).to eq 9 end end |