1# frozen_string_literal: true 2 3require 'spec_helper' 4 5RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do 6 include ProjectForksHelper 7 include StubRequests 8 include Ci::SourcePipelineHelpers 9 10 let_it_be(:user) { create(:user, :public_email) } 11 let_it_be(:namespace) { create_default(:namespace).freeze } 12 let_it_be(:project) { create_default(:project, :repository).freeze } 13 14 it 'paginates 15 pipeleines per page' do 15 expect(described_class.default_per_page).to eq(15) 16 end 17 18 it_behaves_like 'having unique enum values' 19 20 it { is_expected.to belong_to(:project) } 21 it { is_expected.to belong_to(:user) } 22 it { is_expected.to belong_to(:auto_canceled_by) } 23 it { is_expected.to belong_to(:pipeline_schedule) } 24 it { is_expected.to belong_to(:merge_request) } 25 it { is_expected.to belong_to(:external_pull_request) } 26 27 it { is_expected.to have_many(:statuses) } 28 it { is_expected.to have_many(:trigger_requests) } 29 it { is_expected.to have_many(:variables) } 30 it { is_expected.to have_many(:builds) } 31 it { is_expected.to have_many(:statuses_order_id_desc) } 32 it { is_expected.to have_many(:bridges) } 33 it { is_expected.to have_many(:job_artifacts).through(:builds) } 34 it { is_expected.to have_many(:auto_canceled_pipelines) } 35 it { is_expected.to have_many(:auto_canceled_jobs) } 36 it { is_expected.to have_many(:sourced_pipelines) } 37 it { is_expected.to have_many(:triggered_pipelines) } 38 it { is_expected.to have_many(:pipeline_artifacts) } 39 40 it { is_expected.to have_one(:chat_data) } 41 it { is_expected.to have_one(:source_pipeline) } 42 it { is_expected.to have_one(:triggered_by_pipeline) } 43 it { is_expected.to have_one(:source_job) } 44 it { is_expected.to have_one(:pipeline_config) } 45 46 it { is_expected.to respond_to :git_author_name } 47 it { is_expected.to respond_to :git_author_email } 48 it { is_expected.to respond_to :git_author_full_text } 49 it { is_expected.to respond_to :short_sha } 50 it { is_expected.to delegate_method(:full_path).to(:project).with_prefix } 51 52 describe 'validations' do 53 it { is_expected.to validate_presence_of(:sha) } 54 it { is_expected.to validate_presence_of(:status) } 55 end 56 57 describe 'associations' do 58 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 59 60 it 'has a bidirectional relationship with projects' do 61 expect(described_class.reflect_on_association(:project).has_inverse?).to eq(:all_pipelines) 62 expect(Project.reflect_on_association(:all_pipelines).has_inverse?).to eq(:project) 63 expect(Project.reflect_on_association(:ci_pipelines).has_inverse?).to eq(:project) 64 end 65 66 describe '#latest_builds' do 67 it 'has a one to many relationship with its latest builds' do 68 _old_build = create(:ci_build, :retried, pipeline: pipeline) 69 latest_build = create(:ci_build, :expired, pipeline: pipeline) 70 71 expect(pipeline.latest_builds).to contain_exactly(latest_build) 72 end 73 end 74 75 describe '#downloadable_artifacts' do 76 let_it_be(:build) { create(:ci_build, pipeline: pipeline) } 77 let_it_be(:downloadable_artifact) { create(:ci_job_artifact, :codequality, job: build) } 78 let_it_be(:expired_artifact) { create(:ci_job_artifact, :junit, :expired, job: build) } 79 let_it_be(:undownloadable_artifact) { create(:ci_job_artifact, :trace, job: build) } 80 81 context 'when artifacts are locked' do 82 it 'returns downloadable artifacts including locked artifacts' do 83 expect(pipeline.downloadable_artifacts).to contain_exactly(downloadable_artifact, expired_artifact) 84 end 85 end 86 87 context 'when artifacts are unlocked' do 88 it 'returns only downloadable artifacts not expired' do 89 expired_artifact.job.pipeline.unlocked! 90 91 expect(pipeline.reload.downloadable_artifacts).to contain_exactly(downloadable_artifact) 92 end 93 end 94 end 95 end 96 97 describe '#set_status' do 98 let(:pipeline) { build(:ci_empty_pipeline, :created) } 99 100 where(:from_status, :to_status) do 101 from_status_names = described_class.state_machines[:status].states.map(&:name) 102 to_status_names = from_status_names - [:created] # we never want to transition into created 103 104 from_status_names.product(to_status_names) 105 end 106 107 with_them do 108 it do 109 pipeline.status = from_status.to_s 110 111 if from_status != to_status 112 expect(pipeline.set_status(to_status.to_s)) 113 .to eq(true) 114 else 115 expect(pipeline.set_status(to_status.to_s)) 116 .to eq(false), "loopback transitions are not allowed" 117 end 118 end 119 end 120 end 121 122 describe '.processables' do 123 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 124 125 before do 126 create(:ci_build, name: 'build', pipeline: pipeline) 127 create(:ci_bridge, name: 'bridge', pipeline: pipeline) 128 create(:commit_status, name: 'commit status', pipeline: pipeline) 129 create(:generic_commit_status, name: 'generic status', pipeline: pipeline) 130 end 131 132 it 'has an association with processable CI/CD entities' do 133 pipeline.processables.pluck('name').yield_self do |processables| 134 expect(processables).to match_array %w[build bridge] 135 end 136 end 137 138 it 'makes it possible to append a new processable' do 139 pipeline.processables << build(:ci_bridge) 140 141 pipeline.save! 142 143 expect(pipeline.processables.reload.count).to eq 3 144 end 145 end 146 147 describe '.for_iid' do 148 subject { described_class.for_iid(iid) } 149 150 let(:iid) { '1234' } 151 let!(:pipeline) { create(:ci_pipeline, iid: '1234') } 152 153 it 'returns the pipeline' do 154 is_expected.to contain_exactly(pipeline) 155 end 156 end 157 158 describe '.for_sha' do 159 subject { described_class.for_sha(sha) } 160 161 let(:sha) { 'abc' } 162 163 let_it_be(:pipeline) { create(:ci_pipeline, sha: 'abc') } 164 165 it 'returns the pipeline' do 166 is_expected.to contain_exactly(pipeline) 167 end 168 169 context 'when argument is array' do 170 let(:sha) { %w[abc def] } 171 let!(:pipeline_2) { create(:ci_pipeline, sha: 'def') } 172 173 it 'returns the pipelines' do 174 is_expected.to contain_exactly(pipeline, pipeline_2) 175 end 176 end 177 178 context 'when sha is empty' do 179 let(:sha) { nil } 180 181 it 'does not return anything' do 182 is_expected.to be_empty 183 end 184 end 185 end 186 187 describe '.where_not_sha' do 188 let_it_be(:pipeline) { create(:ci_pipeline, sha: 'abcx') } 189 let_it_be(:pipeline_2) { create(:ci_pipeline, sha: 'abc') } 190 191 let(:sha) { 'abc' } 192 193 subject { described_class.where_not_sha(sha) } 194 195 it 'returns the pipeline without the specified sha' do 196 is_expected.to contain_exactly(pipeline) 197 end 198 199 context 'when argument is array' do 200 let(:sha) { %w[abc abcx] } 201 202 it 'returns the pipelines without the specified shas' do 203 pipeline_3 = create(:ci_pipeline, sha: 'abcy') 204 is_expected.to contain_exactly(pipeline_3) 205 end 206 end 207 end 208 209 describe '.for_source_sha' do 210 subject { described_class.for_source_sha(source_sha) } 211 212 let(:source_sha) { 'abc' } 213 214 let_it_be(:pipeline) { create(:ci_pipeline, source_sha: 'abc') } 215 216 it 'returns the pipeline' do 217 is_expected.to contain_exactly(pipeline) 218 end 219 220 context 'when argument is array' do 221 let(:source_sha) { %w[abc def] } 222 let!(:pipeline_2) { create(:ci_pipeline, source_sha: 'def') } 223 224 it 'returns the pipelines' do 225 is_expected.to contain_exactly(pipeline, pipeline_2) 226 end 227 end 228 229 context 'when source_sha is empty' do 230 let(:source_sha) { nil } 231 232 it 'does not return anything' do 233 is_expected.to be_empty 234 end 235 end 236 end 237 238 describe '.for_sha_or_source_sha' do 239 subject { described_class.for_sha_or_source_sha(sha) } 240 241 let(:sha) { 'abc' } 242 243 context 'when sha is matched' do 244 let!(:pipeline) { create(:ci_pipeline, sha: sha) } 245 246 it 'returns the pipeline' do 247 is_expected.to contain_exactly(pipeline) 248 end 249 end 250 251 context 'when source sha is matched' do 252 let!(:pipeline) { create(:ci_pipeline, source_sha: sha) } 253 254 it 'returns the pipeline' do 255 is_expected.to contain_exactly(pipeline) 256 end 257 end 258 259 context 'when both sha and source sha are not matched' do 260 let!(:pipeline) { create(:ci_pipeline, sha: 'bcd', source_sha: 'bcd') } 261 262 it 'does not return anything' do 263 is_expected.to be_empty 264 end 265 end 266 end 267 268 describe '.for_branch' do 269 subject { described_class.for_branch(branch) } 270 271 let(:branch) { 'master' } 272 273 let_it_be(:pipeline) { create(:ci_pipeline, ref: 'master') } 274 275 it 'returns the pipeline' do 276 is_expected.to contain_exactly(pipeline) 277 end 278 279 context 'with tag pipeline' do 280 let(:branch) { 'v1.0' } 281 let!(:pipeline) { create(:ci_pipeline, ref: 'v1.0', tag: true) } 282 283 it 'returns nothing' do 284 is_expected.to be_empty 285 end 286 end 287 end 288 289 describe '.with_pipeline_source' do 290 subject { described_class.with_pipeline_source(source) } 291 292 let(:source) { 'web' } 293 294 let_it_be(:push_pipeline) { create(:ci_pipeline, source: :push) } 295 let_it_be(:web_pipeline) { create(:ci_pipeline, source: :web) } 296 let_it_be(:api_pipeline) { create(:ci_pipeline, source: :api) } 297 298 it 'contains pipelines created due to specified source' do 299 expect(subject).to contain_exactly(web_pipeline) 300 end 301 end 302 303 describe '.ci_sources' do 304 subject { described_class.ci_sources } 305 306 let(:push_pipeline) { build(:ci_pipeline, source: :push) } 307 let(:web_pipeline) { build(:ci_pipeline, source: :web) } 308 let(:api_pipeline) { build(:ci_pipeline, source: :api) } 309 let(:webide_pipeline) { build(:ci_pipeline, source: :webide) } 310 let(:child_pipeline) { build(:ci_pipeline, source: :parent_pipeline) } 311 let(:pipelines) { [push_pipeline, web_pipeline, api_pipeline, webide_pipeline, child_pipeline] } 312 313 it 'contains pipelines having CI only sources' do 314 pipelines.map(&:save!) 315 316 expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline) 317 end 318 319 it 'filters on expected sources' do 320 expect(::Enums::Ci::Pipeline.ci_sources.keys).to contain_exactly( 321 *%i[unknown push web trigger schedule api external pipeline chat 322 merge_request_event external_pull_request_event]) 323 end 324 end 325 326 describe '.ci_branch_sources' do 327 subject { described_class.ci_branch_sources } 328 329 let_it_be(:push_pipeline) { create(:ci_pipeline, source: :push) } 330 let_it_be(:web_pipeline) { create(:ci_pipeline, source: :web) } 331 let_it_be(:api_pipeline) { create(:ci_pipeline, source: :api) } 332 let_it_be(:webide_pipeline) { create(:ci_pipeline, source: :webide) } 333 let_it_be(:child_pipeline) { create(:ci_pipeline, source: :parent_pipeline) } 334 let_it_be(:merge_request_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline) } 335 336 it 'contains pipelines having CI only sources' do 337 expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline) 338 end 339 340 it 'filters on expected sources' do 341 expect(::Enums::Ci::Pipeline.ci_branch_sources.keys).to contain_exactly( 342 *%i[unknown push web trigger schedule api external pipeline chat 343 external_pull_request_event]) 344 end 345 end 346 347 describe '.outside_pipeline_family' do 348 subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) } 349 350 let(:upstream_pipeline) { create(:ci_pipeline, project: project) } 351 let(:child_pipeline) { create(:ci_pipeline, project: project) } 352 353 let!(:other_pipeline) { create(:ci_pipeline, project: project) } 354 355 before do 356 create(:ci_sources_pipeline, 357 source_job: create(:ci_build, pipeline: upstream_pipeline), 358 source_project: project, 359 pipeline: child_pipeline, 360 project: project) 361 end 362 363 it 'only returns pipelines outside pipeline family' do 364 expect(outside_pipeline_family).to contain_exactly(other_pipeline) 365 end 366 end 367 368 describe '.before_pipeline' do 369 subject(:before_pipeline) { described_class.before_pipeline(child_pipeline) } 370 371 let!(:older_other_pipeline) { create(:ci_pipeline, project: project) } 372 373 let!(:upstream_pipeline) { create(:ci_pipeline, project: project) } 374 let!(:child_pipeline) { create(:ci_pipeline, child_of: upstream_pipeline) } 375 376 let!(:other_pipeline) { create(:ci_pipeline, project: project) } 377 378 before do 379 create(:ci_sources_pipeline, 380 source_job: create(:ci_build, pipeline: upstream_pipeline), 381 source_project: project, 382 pipeline: child_pipeline, 383 project: project) 384 end 385 386 it 'only returns older pipelines outside pipeline family' do 387 expect(before_pipeline).to contain_exactly(older_other_pipeline) 388 end 389 end 390 391 describe '#merge_request?' do 392 let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) } 393 let(:merge_request) { create(:merge_request) } 394 395 it 'returns true' do 396 expect(pipeline).to be_merge_request 397 end 398 399 context 'when merge request is nil' do 400 let(:merge_request) { nil } 401 402 it 'returns false' do 403 expect(pipeline).not_to be_merge_request 404 end 405 end 406 end 407 408 describe '#detached_merge_request_pipeline?' do 409 subject { pipeline.detached_merge_request_pipeline? } 410 411 let!(:pipeline) do 412 create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) 413 end 414 415 let(:merge_request) { create(:merge_request) } 416 let(:target_sha) { nil } 417 418 it { is_expected.to be_truthy } 419 420 context 'when target sha exists' do 421 let(:target_sha) { merge_request.target_branch_sha } 422 423 it { is_expected.to be_falsy } 424 end 425 end 426 427 describe '#merged_result_pipeline?' do 428 subject { pipeline.merged_result_pipeline? } 429 430 let!(:pipeline) do 431 create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) 432 end 433 434 let(:merge_request) { create(:merge_request) } 435 let(:target_sha) { merge_request.target_branch_sha } 436 437 it { is_expected.to be_truthy } 438 439 context 'when target sha is empty' do 440 let(:target_sha) { nil } 441 442 it { is_expected.to be_falsy } 443 end 444 end 445 446 describe '#merge_request_ref?' do 447 subject { pipeline.merge_request_ref? } 448 449 let(:pipeline) { build(:ci_empty_pipeline, :created) } 450 451 it 'calls MergeRequest#merge_request_ref?' do 452 expect(MergeRequest).to receive(:merge_request_ref?).with(pipeline.ref) 453 454 subject 455 end 456 end 457 458 describe '#merge_request_event_type' do 459 subject { pipeline.merge_request_event_type } 460 461 let(:pipeline) { merge_request.all_pipelines.last } 462 463 context 'when pipeline is merge request pipeline' do 464 let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } 465 466 it { is_expected.to eq(:merged_result) } 467 end 468 469 context 'when pipeline is detached merge request pipeline' do 470 let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) } 471 472 it { is_expected.to eq(:detached) } 473 end 474 end 475 476 describe '#legacy_detached_merge_request_pipeline?' do 477 subject { pipeline.legacy_detached_merge_request_pipeline? } 478 479 let_it_be(:merge_request) { create(:merge_request) } 480 481 let(:ref) { 'feature' } 482 let(:target_sha) { nil } 483 484 let(:pipeline) do 485 build(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, ref: ref, target_sha: target_sha) 486 end 487 488 it { is_expected.to be_truthy } 489 490 context 'when pipeline ref is a merge request ref' do 491 let(:ref) { 'refs/merge-requests/1/head' } 492 493 it { is_expected.to be_falsy } 494 end 495 496 context 'when target sha is set' do 497 let(:target_sha) { 'target-sha' } 498 499 it { is_expected.to be_falsy } 500 end 501 end 502 503 describe '#matches_sha_or_source_sha?' do 504 subject { pipeline.matches_sha_or_source_sha?(sample_sha) } 505 506 let(:sample_sha) { Digest::SHA1.hexdigest(SecureRandom.hex) } 507 508 context 'when sha matches' do 509 let(:pipeline) { build(:ci_pipeline, sha: sample_sha) } 510 511 it { is_expected.to be_truthy } 512 end 513 514 context 'when source_sha matches' do 515 let(:pipeline) { build(:ci_pipeline, source_sha: sample_sha) } 516 517 it { is_expected.to be_truthy } 518 end 519 520 context 'when both sha and source_sha do not matche' do 521 let(:pipeline) { build(:ci_pipeline, sha: 'test', source_sha: 'test') } 522 523 it { is_expected.to be_falsy } 524 end 525 end 526 527 describe '#source_ref' do 528 subject { pipeline.source_ref } 529 530 let(:pipeline) { create(:ci_pipeline, ref: 'feature') } 531 532 it 'returns source ref' do 533 is_expected.to eq('feature') 534 end 535 536 context 'when the pipeline is a detached merge request pipeline' do 537 let(:merge_request) { create(:merge_request) } 538 539 let(:pipeline) do 540 create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, ref: merge_request.ref_path) 541 end 542 543 it 'returns source ref' do 544 is_expected.to eq(merge_request.source_branch) 545 end 546 end 547 end 548 549 describe '#source_ref_slug' do 550 subject { pipeline.source_ref_slug } 551 552 let(:pipeline) { create(:ci_pipeline, ref: 'feature') } 553 554 it 'slugifies with the source ref' do 555 expect(Gitlab::Utils).to receive(:slugify).with('feature') 556 557 subject 558 end 559 560 context 'when the pipeline is a detached merge request pipeline' do 561 let(:merge_request) { create(:merge_request) } 562 563 let(:pipeline) do 564 create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, ref: merge_request.ref_path) 565 end 566 567 it 'slugifies with the source ref of the merge request' do 568 expect(Gitlab::Utils).to receive(:slugify).with(merge_request.source_branch) 569 570 subject 571 end 572 end 573 end 574 575 describe '.with_reports' do 576 context 'when pipeline has a test report' do 577 subject { described_class.with_reports(Ci::JobArtifact.test_reports) } 578 579 let!(:pipeline_with_report) { create(:ci_pipeline, :with_test_reports) } 580 581 it 'selects the pipeline' do 582 is_expected.to eq([pipeline_with_report]) 583 end 584 end 585 586 context 'when pipeline has a coverage report' do 587 subject { described_class.with_reports(Ci::JobArtifact.coverage_reports) } 588 589 let!(:pipeline_with_report) { create(:ci_pipeline, :with_coverage_reports) } 590 591 it 'selects the pipeline' do 592 is_expected.to eq([pipeline_with_report]) 593 end 594 end 595 596 context 'when pipeline has an accessibility report' do 597 subject { described_class.with_reports(Ci::JobArtifact.accessibility_reports) } 598 599 let(:pipeline_with_report) { create(:ci_pipeline, :with_accessibility_reports) } 600 601 it 'selects the pipeline' do 602 is_expected.to eq([pipeline_with_report]) 603 end 604 end 605 606 context 'when pipeline has a codequality report' do 607 subject { described_class.with_reports(Ci::JobArtifact.codequality_reports) } 608 609 let(:pipeline_with_report) { create(:ci_pipeline, :with_codequality_reports) } 610 611 it 'selects the pipeline' do 612 is_expected.to eq([pipeline_with_report]) 613 end 614 end 615 616 context 'when pipeline has a terraform report' do 617 it 'selects the pipeline' do 618 pipeline_with_report = create(:ci_pipeline, :with_terraform_reports) 619 620 expect(described_class.with_reports(Ci::JobArtifact.terraform_reports)).to eq( 621 [pipeline_with_report] 622 ) 623 end 624 end 625 626 context 'when pipeline does not have metrics reports' do 627 subject { described_class.with_reports(Ci::JobArtifact.test_reports) } 628 629 let!(:pipeline_without_report) { create(:ci_empty_pipeline) } 630 631 it 'does not select the pipeline' do 632 is_expected.to be_empty 633 end 634 end 635 end 636 637 describe '.merge_request_event' do 638 subject { described_class.merge_request_event } 639 640 context 'when there is a merge request pipeline' do 641 let!(:pipeline) { create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) } 642 let(:merge_request) { create(:merge_request) } 643 644 it 'returns merge request pipeline first' do 645 expect(subject).to eq([pipeline]) 646 end 647 end 648 649 context 'when there are no merge request pipelines' do 650 let!(:pipeline) { create(:ci_pipeline, source: :push) } 651 652 it 'returns empty array' do 653 expect(subject).to be_empty 654 end 655 end 656 end 657 658 describe 'modules' do 659 it_behaves_like 'AtomicInternalId', validate_presence: false do 660 let(:internal_id_attribute) { :iid } 661 let(:instance) { build(:ci_pipeline) } 662 let(:scope) { :project } 663 let(:scope_attrs) { { project: instance.project } } 664 let(:usage) { :ci_pipelines } 665 end 666 end 667 668 describe '#source' do 669 context 'when creating new pipeline' do 670 let(:pipeline) do 671 build(:ci_empty_pipeline, :created, project: project, source: nil) 672 end 673 674 it "prevents from creating an object" do 675 expect(pipeline).not_to be_valid 676 end 677 end 678 679 context 'when updating existing pipeline' do 680 let(:pipeline) { create(:ci_empty_pipeline, :created) } 681 682 before do 683 pipeline.update_attribute(:source, nil) 684 end 685 686 it 'object is valid' do 687 expect(pipeline).to be_valid 688 end 689 end 690 end 691 692 describe '#block' do 693 let(:pipeline) { create(:ci_empty_pipeline, :created) } 694 695 it 'changes pipeline status to manual' do 696 expect(pipeline.block).to be true 697 expect(pipeline.reload).to be_manual 698 expect(pipeline.reload).to be_blocked 699 end 700 end 701 702 describe '#delay' do 703 subject { pipeline.delay } 704 705 let(:pipeline) { build(:ci_pipeline, :created) } 706 707 it 'changes pipeline status to schedule' do 708 subject 709 710 expect(pipeline).to be_scheduled 711 end 712 end 713 714 describe '#valid_commit_sha' do 715 let(:pipeline) { build_stubbed(:ci_empty_pipeline, :created, project: project) } 716 717 context 'commit.sha can not start with 00000000' do 718 before do 719 pipeline.sha = '0' * 40 720 pipeline.valid_commit_sha 721 end 722 723 it('commit errors should not be empty') { expect(pipeline.errors).not_to be_empty } 724 end 725 end 726 727 describe '#short_sha' do 728 subject { pipeline.short_sha } 729 730 let(:pipeline) { build_stubbed(:ci_empty_pipeline, :created) } 731 732 it 'has 8 items' do 733 expect(subject.size).to eq(8) 734 end 735 it { expect(pipeline.sha).to start_with(subject) } 736 end 737 738 describe '#retried' do 739 subject { pipeline.retried } 740 741 let(:pipeline) { create(:ci_empty_pipeline, :created, project: project) } 742 let!(:build1) { create(:ci_build, pipeline: pipeline, name: 'deploy', retried: true) } 743 744 before do 745 create(:ci_build, pipeline: pipeline, name: 'deploy') 746 end 747 748 it 'returns old builds' do 749 is_expected.to contain_exactly(build1) 750 end 751 end 752 753 describe '#coverage' do 754 let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline) } 755 756 context 'with multiple pipelines' do 757 before_all do 758 create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline) 759 create(:ci_build, name: "rubocop", coverage: 35, pipeline: pipeline) 760 end 761 762 it "calculates average when there are two builds with coverage" do 763 expect(pipeline.coverage).to be_within(0.001).of(32.5) 764 end 765 766 it "calculates average when there are two builds with coverage and one with nil" do 767 create(:ci_build, pipeline: pipeline) 768 769 expect(pipeline.coverage).to be_within(0.001).of(32.5) 770 end 771 772 it "calculates average when there are two builds with coverage and one is retried" do 773 create(:ci_build, name: "rubocop", coverage: 30, pipeline: pipeline, retried: true) 774 775 expect(pipeline.coverage).to be_within(0.001).of(32.5) 776 end 777 end 778 779 context 'when there is one build without coverage' do 780 it "calculates average to nil" do 781 create(:ci_build, pipeline: pipeline) 782 783 expect(pipeline.coverage).to be_nil 784 end 785 end 786 end 787 788 describe '#update_builds_coverage' do 789 let_it_be(:pipeline) { create(:ci_empty_pipeline) } 790 791 context 'builds with coverage_regex defined' do 792 let!(:build_1) { create(:ci_build, :success, :trace_with_coverage, trace_coverage: 60.0, pipeline: pipeline) } 793 let!(:build_2) { create(:ci_build, :success, :trace_with_coverage, trace_coverage: 80.0, pipeline: pipeline) } 794 795 it 'updates the coverage value of each build from the trace' do 796 pipeline.update_builds_coverage 797 798 expect(build_1.reload.coverage).to eq(60.0) 799 expect(build_2.reload.coverage).to eq(80.0) 800 end 801 end 802 803 context 'builds without coverage_regex defined' do 804 let!(:build) { create(:ci_build, :success, :trace_with_coverage, coverage_regex: nil, trace_coverage: 60.0, pipeline: pipeline) } 805 806 it 'does not update the coverage value of each build from the trace' do 807 pipeline.update_builds_coverage 808 809 expect(build.reload.coverage).to eq(nil) 810 end 811 end 812 813 context 'builds with coverage values already present' do 814 let!(:build) { create(:ci_build, :success, :trace_with_coverage, trace_coverage: 60.0, coverage: 10.0, pipeline: pipeline) } 815 816 it 'does not update the coverage value of each build from the trace' do 817 pipeline.update_builds_coverage 818 819 expect(build.reload.coverage).to eq(10.0) 820 end 821 end 822 end 823 824 describe '#retryable?' do 825 subject { pipeline.retryable? } 826 827 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created, project: project) } 828 829 context 'no failed builds' do 830 before do 831 create_build('rspec', 'success') 832 end 833 834 it 'is not retryable' do 835 is_expected.to be_falsey 836 end 837 838 context 'one canceled job' do 839 before do 840 create_build('rubocop', 'canceled') 841 end 842 843 it 'is retryable' do 844 is_expected.to be_truthy 845 end 846 end 847 end 848 849 context 'with failed builds' do 850 before do 851 create_build('rspec', 'running') 852 create_build('rubocop', 'failed') 853 end 854 855 it 'is retryable' do 856 is_expected.to be_truthy 857 end 858 end 859 860 def create_build(name, status) 861 create(:ci_build, name: name, status: status, pipeline: pipeline) 862 end 863 end 864 865 describe '#persisted_variables' do 866 context 'when pipeline is not persisted yet' do 867 subject { build(:ci_pipeline).persisted_variables } 868 869 it 'does not contain some variables' do 870 keys = subject.map { |variable| variable[:key] } 871 872 expect(keys).not_to include 'CI_PIPELINE_ID' 873 end 874 end 875 876 context 'when pipeline is persisted' do 877 subject { build_stubbed(:ci_pipeline).persisted_variables } 878 879 it 'does contains persisted variables' do 880 keys = subject.map { |variable| variable[:key] } 881 882 expect(keys).to eq %w[CI_PIPELINE_ID CI_PIPELINE_URL] 883 end 884 end 885 end 886 887 describe '#predefined_variables' do 888 subject { pipeline.predefined_variables } 889 890 let(:pipeline) { build(:ci_empty_pipeline, :created) } 891 892 it 'includes all predefined variables in a valid order' do 893 keys = subject.map { |variable| variable[:key] } 894 895 expect(keys).to eq %w[ 896 CI_PIPELINE_IID 897 CI_PIPELINE_SOURCE 898 CI_PIPELINE_CREATED_AT 899 CI_COMMIT_SHA 900 CI_COMMIT_SHORT_SHA 901 CI_COMMIT_BEFORE_SHA 902 CI_COMMIT_REF_NAME 903 CI_COMMIT_REF_SLUG 904 CI_COMMIT_BRANCH 905 CI_COMMIT_MESSAGE 906 CI_COMMIT_TITLE 907 CI_COMMIT_DESCRIPTION 908 CI_COMMIT_REF_PROTECTED 909 CI_COMMIT_TIMESTAMP 910 CI_COMMIT_AUTHOR 911 CI_BUILD_REF 912 CI_BUILD_BEFORE_SHA 913 CI_BUILD_REF_NAME 914 CI_BUILD_REF_SLUG 915 ] 916 end 917 918 context 'when merge request is present' do 919 let_it_be(:assignees) { create_list(:user, 2) } 920 let_it_be(:milestone) { create(:milestone, project: project) } 921 let_it_be(:labels) { create_list(:label, 2) } 922 923 let(:merge_request) do 924 create(:merge_request, :simple, 925 source_project: project, 926 target_project: project, 927 assignees: assignees, 928 milestone: milestone, 929 labels: labels) 930 end 931 932 context 'when pipeline for merge request is created' do 933 let(:pipeline) do 934 create(:ci_pipeline, :detached_merge_request_pipeline, 935 ci_ref_presence: false, 936 user: user, 937 merge_request: merge_request) 938 end 939 940 before do 941 project.add_developer(user) 942 end 943 944 it 'exposes merge request pipeline variables' do 945 expect(subject.to_hash) 946 .to include( 947 'CI_MERGE_REQUEST_ID' => merge_request.id.to_s, 948 'CI_MERGE_REQUEST_IID' => merge_request.iid.to_s, 949 'CI_MERGE_REQUEST_REF_PATH' => merge_request.ref_path.to_s, 950 'CI_MERGE_REQUEST_PROJECT_ID' => merge_request.project.id.to_s, 951 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path, 952 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url, 953 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s, 954 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => '', 955 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s, 956 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path, 957 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url, 958 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s, 959 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => '', 960 'CI_MERGE_REQUEST_TITLE' => merge_request.title, 961 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list, 962 'CI_MERGE_REQUEST_MILESTONE' => milestone.title, 963 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','), 964 'CI_MERGE_REQUEST_EVENT_TYPE' => 'detached', 965 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true)) 966 end 967 968 it 'exposes diff variables' do 969 expect(subject.to_hash) 970 .to include( 971 'CI_MERGE_REQUEST_DIFF_ID' => merge_request.merge_request_diff.id.to_s, 972 'CI_MERGE_REQUEST_DIFF_BASE_SHA' => merge_request.merge_request_diff.base_commit_sha) 973 end 974 975 context 'without assignee' do 976 let(:assignees) { [] } 977 978 it 'does not expose assignee variable' do 979 expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_ASSIGNEES') 980 end 981 end 982 983 context 'without milestone' do 984 let(:milestone) { nil } 985 986 it 'does not expose milestone variable' do 987 expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_MILESTONE') 988 end 989 end 990 991 context 'without labels' do 992 let(:labels) { [] } 993 994 it 'does not expose labels variable' do 995 expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_LABELS') 996 end 997 end 998 end 999 1000 context 'when pipeline on branch is created' do 1001 let(:pipeline) do 1002 create(:ci_pipeline, project: project, user: user, ref: 'feature') 1003 end 1004 1005 context 'when a merge request is created' do 1006 before do 1007 merge_request 1008 end 1009 1010 context 'when user has access to project' do 1011 before do 1012 project.add_developer(user) 1013 end 1014 1015 it 'merge request references are returned matching the pipeline' do 1016 expect(subject.to_hash).to include( 1017 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true)) 1018 end 1019 end 1020 1021 context 'when user does not have access to project' do 1022 it 'CI_OPEN_MERGE_REQUESTS is not returned' do 1023 expect(subject.to_hash).not_to have_key('CI_OPEN_MERGE_REQUESTS') 1024 end 1025 end 1026 end 1027 1028 context 'when no a merge request is created' do 1029 it 'CI_OPEN_MERGE_REQUESTS is not returned' do 1030 expect(subject.to_hash).not_to have_key('CI_OPEN_MERGE_REQUESTS') 1031 end 1032 end 1033 end 1034 1035 context 'with merged results' do 1036 let(:pipeline) do 1037 create(:ci_pipeline, :merged_result_pipeline, merge_request: merge_request) 1038 end 1039 1040 it 'exposes merge request pipeline variables' do 1041 expect(subject.to_hash) 1042 .to include( 1043 'CI_MERGE_REQUEST_ID' => merge_request.id.to_s, 1044 'CI_MERGE_REQUEST_IID' => merge_request.iid.to_s, 1045 'CI_MERGE_REQUEST_REF_PATH' => merge_request.ref_path.to_s, 1046 'CI_MERGE_REQUEST_PROJECT_ID' => merge_request.project.id.to_s, 1047 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path, 1048 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url, 1049 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s, 1050 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => merge_request.target_branch_sha, 1051 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s, 1052 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path, 1053 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url, 1054 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s, 1055 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => merge_request.source_branch_sha, 1056 'CI_MERGE_REQUEST_TITLE' => merge_request.title, 1057 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list, 1058 'CI_MERGE_REQUEST_MILESTONE' => milestone.title, 1059 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','), 1060 'CI_MERGE_REQUEST_EVENT_TYPE' => 'merged_result') 1061 end 1062 1063 it 'exposes diff variables' do 1064 expect(subject.to_hash) 1065 .to include( 1066 'CI_MERGE_REQUEST_DIFF_ID' => merge_request.merge_request_diff.id.to_s, 1067 'CI_MERGE_REQUEST_DIFF_BASE_SHA' => merge_request.merge_request_diff.base_commit_sha) 1068 end 1069 end 1070 end 1071 1072 context 'when source is external pull request' do 1073 let(:pipeline) do 1074 create(:ci_pipeline, source: :external_pull_request_event, external_pull_request: pull_request) 1075 end 1076 1077 let(:pull_request) { create(:external_pull_request, project: project) } 1078 1079 it 'exposes external pull request pipeline variables' do 1080 expect(subject.to_hash) 1081 .to include( 1082 'CI_EXTERNAL_PULL_REQUEST_IID' => pull_request.pull_request_iid.to_s, 1083 'CI_EXTERNAL_PULL_REQUEST_SOURCE_REPOSITORY' => pull_request.source_repository, 1084 'CI_EXTERNAL_PULL_REQUEST_TARGET_REPOSITORY' => pull_request.target_repository, 1085 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_SHA' => pull_request.source_sha, 1086 'CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA' => pull_request.target_sha, 1087 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME' => pull_request.source_branch, 1088 'CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_NAME' => pull_request.target_branch 1089 ) 1090 end 1091 end 1092 1093 describe 'variable CI_KUBERNETES_ACTIVE' do 1094 context 'when pipeline.has_kubernetes_active? is true' do 1095 before do 1096 allow(pipeline).to receive(:has_kubernetes_active?).and_return(true) 1097 end 1098 1099 it "is included with value 'true'" do 1100 expect(subject.to_hash).to include('CI_KUBERNETES_ACTIVE' => 'true') 1101 end 1102 end 1103 1104 context 'when pipeline.has_kubernetes_active? is false' do 1105 before do 1106 allow(pipeline).to receive(:has_kubernetes_active?).and_return(false) 1107 end 1108 1109 it 'is not included' do 1110 expect(subject.to_hash).not_to have_key('CI_KUBERNETES_ACTIVE') 1111 end 1112 end 1113 end 1114 end 1115 1116 describe '#protected_ref?' do 1117 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1118 1119 it 'delegates method to project' do 1120 expect(pipeline).not_to be_protected_ref 1121 end 1122 end 1123 1124 describe '#legacy_trigger' do 1125 let(:trigger_request) { build(:ci_trigger_request) } 1126 let(:pipeline) { build(:ci_empty_pipeline, :created, trigger_requests: [trigger_request]) } 1127 1128 it 'returns first trigger request' do 1129 expect(pipeline.legacy_trigger).to eq trigger_request 1130 end 1131 end 1132 1133 describe '#auto_canceled?' do 1134 subject { pipeline.auto_canceled? } 1135 1136 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1137 1138 context 'when it is canceled' do 1139 before do 1140 pipeline.cancel 1141 end 1142 1143 context 'when there is auto_canceled_by' do 1144 before do 1145 pipeline.auto_canceled_by = create(:ci_empty_pipeline) 1146 end 1147 1148 it 'is auto canceled' do 1149 is_expected.to be_truthy 1150 end 1151 end 1152 1153 context 'when there is no auto_canceled_by' do 1154 it 'is not auto canceled' do 1155 is_expected.to be_falsey 1156 end 1157 end 1158 1159 context 'when it is retried and canceled manually' do 1160 before do 1161 pipeline.enqueue 1162 pipeline.cancel 1163 end 1164 1165 it 'is not auto canceled' do 1166 is_expected.to be_falsey 1167 end 1168 end 1169 end 1170 end 1171 1172 describe 'pipeline stages' do 1173 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1174 1175 describe 'legacy stages' do 1176 before do 1177 create(:commit_status, pipeline: pipeline, 1178 stage: 'build', 1179 name: 'linux', 1180 stage_idx: 0, 1181 status: 'success') 1182 1183 create(:commit_status, pipeline: pipeline, 1184 stage: 'build', 1185 name: 'mac', 1186 stage_idx: 0, 1187 status: 'failed') 1188 1189 create(:commit_status, pipeline: pipeline, 1190 stage: 'deploy', 1191 name: 'staging', 1192 stage_idx: 2, 1193 status: 'running') 1194 1195 create(:commit_status, pipeline: pipeline, 1196 stage: 'test', 1197 name: 'rspec', 1198 stage_idx: 1, 1199 status: 'success') 1200 end 1201 1202 describe '#legacy_stages' do 1203 using RSpec::Parameterized::TableSyntax 1204 1205 subject { pipeline.legacy_stages } 1206 1207 context 'stages list' do 1208 it 'returns ordered list of stages' do 1209 expect(subject.map(&:name)).to eq(%w[build test deploy]) 1210 end 1211 end 1212 1213 context 'stages with statuses' do 1214 let(:statuses) do 1215 subject.map { |stage| [stage.name, stage.status] } 1216 end 1217 1218 it 'returns list of stages with correct statuses' do 1219 expect(statuses).to eq([%w(build failed), 1220 %w(test success), 1221 %w(deploy running)]) 1222 end 1223 end 1224 1225 context 'when there is a stage with warnings' do 1226 before do 1227 create(:commit_status, pipeline: pipeline, 1228 stage: 'deploy', 1229 name: 'prod:2', 1230 stage_idx: 2, 1231 status: 'failed', 1232 allow_failure: true) 1233 end 1234 1235 it 'populates stage with correct number of warnings' do 1236 deploy_stage = pipeline.legacy_stages.third 1237 1238 expect(deploy_stage).not_to receive(:statuses) 1239 expect(deploy_stage).to have_warnings 1240 end 1241 end 1242 end 1243 1244 describe '#stages_count' do 1245 it 'returns a valid number of stages' do 1246 expect(pipeline.stages_count).to eq(3) 1247 end 1248 end 1249 1250 describe '#stages_names' do 1251 it 'returns a valid names of stages' do 1252 expect(pipeline.stages_names).to eq(%w(build test deploy)) 1253 end 1254 end 1255 end 1256 1257 describe '#legacy_stage' do 1258 subject { pipeline.legacy_stage('test') } 1259 1260 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1261 1262 context 'with status in stage' do 1263 before do 1264 create(:commit_status, pipeline: pipeline, stage: 'test') 1265 end 1266 1267 it { expect(subject).to be_a Ci::LegacyStage } 1268 it { expect(subject.name).to eq 'test' } 1269 it { expect(subject.statuses).not_to be_empty } 1270 end 1271 1272 context 'without status in stage' do 1273 before do 1274 create(:commit_status, pipeline: pipeline, stage: 'build') 1275 end 1276 1277 it 'return stage object' do 1278 is_expected.to be_nil 1279 end 1280 end 1281 end 1282 1283 describe '#stages' do 1284 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1285 1286 before do 1287 create(:ci_stage_entity, project: project, 1288 pipeline: pipeline, 1289 position: 4, 1290 name: 'deploy') 1291 1292 create(:ci_build, project: project, 1293 pipeline: pipeline, 1294 stage: 'test', 1295 stage_idx: 3, 1296 name: 'test') 1297 1298 create(:ci_build, project: project, 1299 pipeline: pipeline, 1300 stage: 'build', 1301 stage_idx: 2, 1302 name: 'build') 1303 1304 create(:ci_stage_entity, project: project, 1305 pipeline: pipeline, 1306 position: 1, 1307 name: 'sanity') 1308 1309 create(:ci_stage_entity, project: project, 1310 pipeline: pipeline, 1311 position: 5, 1312 name: 'cleanup') 1313 end 1314 1315 subject { pipeline.stages } 1316 1317 context 'when pipelines is not complete' do 1318 it 'returns stages in valid order' do 1319 expect(subject).to all(be_a Ci::Stage) 1320 expect(subject.map(&:name)) 1321 .to eq %w[sanity build test deploy cleanup] 1322 end 1323 end 1324 1325 context 'when pipeline is complete' do 1326 before do 1327 pipeline.succeed! 1328 end 1329 1330 it 'returns stages in valid order' do 1331 expect(subject).to all(be_a Ci::Stage) 1332 expect(subject.map(&:name)) 1333 .to eq %w[sanity build test deploy cleanup] 1334 end 1335 end 1336 end 1337 end 1338 1339 describe 'state machine' do 1340 let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline, :created) } 1341 1342 let(:current) { Time.current.change(usec: 0) } 1343 let(:build) { create_build('build1', queued_at: 0) } 1344 let(:build_b) { create_build('build2', queued_at: 0) } 1345 let(:build_c) { create_build('build3', queued_at: 0) } 1346 1347 %w[succeed! drop! cancel! skip!].each do |action| 1348 context "when the pipeline recieved #{action} event" do 1349 it 'deletes a persistent ref' do 1350 expect(pipeline.persistent_ref).to receive(:delete).once 1351 1352 pipeline.public_send(action) 1353 end 1354 end 1355 end 1356 1357 describe 'synching status to Jira' do 1358 let(:worker) { ::JiraConnect::SyncBuildsWorker } 1359 1360 context 'when Jira Connect subscription does not exist' do 1361 it 'does not trigger a Jira synch worker' do 1362 expect(worker).not_to receive(:perform_async) 1363 1364 pipeline.prepare! 1365 end 1366 end 1367 1368 context 'when Jira Connect subscription exists' do 1369 before_all do 1370 create(:jira_connect_subscription, namespace: project.namespace) 1371 end 1372 1373 %i[prepare! run! skip! drop! succeed! cancel! block! delay!].each do |event| 1374 context "when we call pipeline.#{event}" do 1375 it 'triggers a Jira synch worker' do 1376 expect(worker).to receive(:perform_async).with(pipeline.id, Integer) 1377 1378 pipeline.send(event) 1379 end 1380 end 1381 end 1382 end 1383 end 1384 1385 describe '#duration', :sidekiq_inline do 1386 context 'when multiple builds are finished' do 1387 before do 1388 travel_to(current + 30) do 1389 build.run! 1390 build.reload.success! 1391 build_b.run! 1392 build_c.run! 1393 end 1394 1395 travel_to(current + 40) do 1396 build_b.reload.drop! 1397 end 1398 1399 travel_to(current + 70) do 1400 build_c.reload.success! 1401 end 1402 end 1403 1404 it 'matches sum of builds duration' do 1405 pipeline.reload 1406 1407 expect(pipeline.duration).to eq(40) 1408 end 1409 end 1410 1411 context 'when pipeline becomes blocked' do 1412 let!(:build) { create_build('build:1') } 1413 let!(:action) { create_build('manual:action', :manual) } 1414 1415 before do 1416 travel_to(current + 1.minute) do 1417 build.run! 1418 end 1419 1420 travel_to(current + 5.minutes) do 1421 build.reload.success! 1422 end 1423 end 1424 1425 it 'recalculates pipeline duration' do 1426 pipeline.reload 1427 1428 expect(pipeline).to be_manual 1429 expect(pipeline.duration).to eq 4.minutes 1430 end 1431 end 1432 end 1433 1434 describe '#started_at' do 1435 let(:pipeline) { create(:ci_empty_pipeline, status: from_status) } 1436 1437 %i[created preparing pending].each do |status| 1438 context "from #{status}" do 1439 let(:from_status) { status } 1440 1441 it 'updates on transitioning to running' do 1442 pipeline.run 1443 1444 expect(pipeline.started_at).not_to be_nil 1445 end 1446 end 1447 end 1448 1449 context 'from created' do 1450 let(:from_status) { :created } 1451 1452 it 'does not update on transitioning to success' do 1453 pipeline.succeed 1454 1455 expect(pipeline.started_at).to be_nil 1456 end 1457 end 1458 end 1459 1460 describe '#finished_at' do 1461 it 'updates on transitioning to success', :sidekiq_might_not_need_inline do 1462 build.success 1463 1464 expect(pipeline.reload.finished_at).not_to be_nil 1465 end 1466 1467 it 'does not update on transitioning to running' do 1468 build.run 1469 1470 expect(pipeline.reload.finished_at).to be_nil 1471 end 1472 end 1473 1474 describe 'merge request metrics' do 1475 let(:pipeline) { create(:ci_empty_pipeline, status: from_status) } 1476 1477 before do 1478 expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id) 1479 end 1480 1481 context 'when transitioning to running' do 1482 %i[created preparing pending].each do |status| 1483 context "from #{status}" do 1484 let(:from_status) { status } 1485 1486 it 'schedules metrics workers' do 1487 pipeline.run 1488 end 1489 end 1490 end 1491 end 1492 1493 context 'when transitioning to success' do 1494 let(:from_status) { 'created' } 1495 1496 it 'schedules metrics workers' do 1497 pipeline.succeed 1498 end 1499 end 1500 end 1501 1502 describe 'merge on success' do 1503 let(:pipeline) { create(:ci_empty_pipeline, status: from_status) } 1504 1505 %i[created preparing pending running].each do |status| 1506 context "from #{status}" do 1507 let(:from_status) { status } 1508 1509 it 'schedules daily build group report results worker' do 1510 expect(Ci::DailyBuildGroupReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id) 1511 1512 pipeline.succeed 1513 end 1514 end 1515 end 1516 end 1517 1518 describe 'pipeline caching' do 1519 context 'when expire_job_and_pipeline_cache_synchronously is enabled' do 1520 before do 1521 stub_feature_flags(expire_job_and_pipeline_cache_synchronously: true) 1522 end 1523 1524 it 'executes Ci::ExpirePipelineCacheService' do 1525 expect_next_instance_of(Ci::ExpirePipelineCacheService) do |service| 1526 expect(service).to receive(:execute).with(pipeline) 1527 end 1528 1529 pipeline.cancel 1530 end 1531 end 1532 1533 context 'when expire_job_and_pipeline_cache_synchronously is disabled' do 1534 before do 1535 stub_feature_flags(expire_job_and_pipeline_cache_synchronously: false) 1536 end 1537 1538 it 'performs ExpirePipelinesCacheWorker' do 1539 expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id) 1540 1541 pipeline.cancel 1542 end 1543 end 1544 end 1545 1546 describe '#dangling?' do 1547 it 'returns true if pipeline comes from any dangling sources' do 1548 pipeline.source = Enums::Ci::Pipeline.dangling_sources.each_key.first 1549 1550 expect(pipeline).to be_dangling 1551 end 1552 1553 it 'returns true if pipeline comes from any CI sources' do 1554 pipeline.source = Enums::Ci::Pipeline.ci_sources.each_key.first 1555 1556 expect(pipeline).not_to be_dangling 1557 end 1558 end 1559 1560 describe 'auto merge' do 1561 context 'when auto merge is enabled' do 1562 let_it_be_with_reload(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) } 1563 let_it_be_with_reload(:pipeline) do 1564 create(:ci_pipeline, :running, project: merge_request.source_project, 1565 ref: merge_request.source_branch, 1566 sha: merge_request.diff_head_sha) 1567 end 1568 1569 before_all do 1570 merge_request.update_head_pipeline 1571 end 1572 1573 %w[succeed! drop! cancel! skip!].each do |action| 1574 context "when the pipeline recieved #{action} event" do 1575 it 'performs AutoMergeProcessWorker' do 1576 expect(AutoMergeProcessWorker).to receive(:perform_async).with(merge_request.id) 1577 1578 pipeline.public_send(action) 1579 end 1580 end 1581 end 1582 end 1583 1584 context 'when auto merge is not enabled in the merge request' do 1585 let(:merge_request) { create(:merge_request) } 1586 1587 it 'performs AutoMergeProcessWorker' do 1588 expect(AutoMergeProcessWorker).not_to receive(:perform_async) 1589 1590 pipeline.succeed! 1591 end 1592 end 1593 end 1594 1595 describe 'auto devops pipeline metrics' do 1596 using RSpec::Parameterized::TableSyntax 1597 1598 let(:pipeline) { create(:ci_empty_pipeline, config_source: config_source) } 1599 let(:config_source) { :auto_devops_source } 1600 1601 where(:action, :status) do 1602 :succeed | 'success' 1603 :drop | 'failed' 1604 :skip | 'skipped' 1605 :cancel | 'canceled' 1606 end 1607 1608 with_them do 1609 context "when pipeline receives action '#{params[:action]}'" do 1610 subject { pipeline.public_send(action) } 1611 1612 it { expect { subject }.to change { auto_devops_pipelines_completed_total(status) }.by(1) } 1613 1614 context 'when not auto_devops_source?' do 1615 let(:config_source) { :repository_source } 1616 1617 it { expect { subject }.not_to change { auto_devops_pipelines_completed_total(status) } } 1618 end 1619 end 1620 end 1621 1622 def auto_devops_pipelines_completed_total(status) 1623 Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines').get(status: status) 1624 end 1625 end 1626 1627 describe 'bridge triggered pipeline' do 1628 shared_examples 'upstream downstream pipeline' do 1629 let!(:source_pipeline) { create(:ci_sources_pipeline, pipeline: downstream_pipeline, source_job: bridge) } 1630 let!(:job) { downstream_pipeline.builds.first } 1631 1632 context 'when source bridge is dependent on pipeline status' do 1633 let!(:bridge) { create(:ci_bridge, :strategy_depend, pipeline: upstream_pipeline) } 1634 1635 it 'schedules the pipeline bridge worker' do 1636 expect(::Ci::PipelineBridgeStatusWorker).to receive(:perform_async).with(downstream_pipeline.id) 1637 1638 downstream_pipeline.succeed! 1639 end 1640 1641 context 'when the downstream pipeline first fails then retries and succeeds' do 1642 it 'makes the upstream pipeline successful' do 1643 Sidekiq::Testing.inline! { job.drop! } 1644 1645 expect(downstream_pipeline.reload).to be_failed 1646 expect(upstream_pipeline.reload).to be_failed 1647 1648 Sidekiq::Testing.inline! do 1649 new_job = Ci::Build.retry(job, project.users.first) 1650 1651 expect(downstream_pipeline.reload).to be_running 1652 expect(upstream_pipeline.reload).to be_running 1653 1654 new_job.success! 1655 end 1656 1657 expect(downstream_pipeline.reload).to be_success 1658 expect(upstream_pipeline.reload).to be_success 1659 end 1660 end 1661 1662 context 'when the downstream pipeline first succeeds then retries and fails' do 1663 it 'makes the upstream pipeline failed' do 1664 Sidekiq::Testing.inline! { job.success! } 1665 1666 expect(downstream_pipeline.reload).to be_success 1667 expect(upstream_pipeline.reload).to be_success 1668 1669 Sidekiq::Testing.inline! do 1670 new_job = Ci::Build.retry(job, project.users.first) 1671 1672 expect(downstream_pipeline.reload).to be_running 1673 expect(upstream_pipeline.reload).to be_running 1674 1675 new_job.drop! 1676 end 1677 1678 expect(downstream_pipeline.reload).to be_failed 1679 expect(upstream_pipeline.reload).to be_failed 1680 end 1681 end 1682 1683 context 'when the upstream pipeline has another dependent upstream pipeline' do 1684 let!(:upstream_of_upstream_pipeline) { create(:ci_pipeline) } 1685 1686 before do 1687 upstream_bridge = create(:ci_bridge, :strategy_depend, pipeline: upstream_of_upstream_pipeline) 1688 create(:ci_sources_pipeline, pipeline: upstream_pipeline, 1689 source_job: upstream_bridge) 1690 end 1691 1692 context 'when the downstream pipeline first fails then retries and succeeds' do 1693 it 'makes upstream pipelines successful' do 1694 Sidekiq::Testing.inline! { job.drop! } 1695 1696 expect(downstream_pipeline.reload).to be_failed 1697 expect(upstream_pipeline.reload).to be_failed 1698 expect(upstream_of_upstream_pipeline.reload).to be_failed 1699 1700 Sidekiq::Testing.inline! do 1701 new_job = Ci::Build.retry(job, project.users.first) 1702 1703 expect(downstream_pipeline.reload).to be_running 1704 expect(upstream_pipeline.reload).to be_running 1705 expect(upstream_of_upstream_pipeline.reload).to be_running 1706 1707 new_job.success! 1708 end 1709 1710 expect(downstream_pipeline.reload).to be_success 1711 expect(upstream_pipeline.reload).to be_success 1712 expect(upstream_of_upstream_pipeline.reload).to be_success 1713 end 1714 end 1715 end 1716 end 1717 1718 context 'when source bridge is not dependent on pipeline status' do 1719 let!(:bridge) { create(:ci_bridge, pipeline: upstream_pipeline) } 1720 1721 it 'does not schedule the pipeline bridge worker' do 1722 expect(::Ci::PipelineBridgeStatusWorker).not_to receive(:perform_async) 1723 1724 downstream_pipeline.succeed! 1725 end 1726 end 1727 end 1728 1729 context 'multi-project pipelines' do 1730 let!(:downstream_project) { create(:project, :repository) } 1731 let!(:upstream_pipeline) { create(:ci_pipeline) } 1732 let!(:downstream_pipeline) { create(:ci_pipeline, :with_job, project: downstream_project) } 1733 1734 it_behaves_like 'upstream downstream pipeline' 1735 end 1736 1737 context 'parent-child pipelines' do 1738 let!(:upstream_pipeline) { create(:ci_pipeline) } 1739 let!(:downstream_pipeline) { create(:ci_pipeline, :with_job) } 1740 1741 it_behaves_like 'upstream downstream pipeline' 1742 end 1743 end 1744 1745 def create_build(name, *traits, queued_at: current, started_from: 0, **opts) 1746 create(:ci_build, *traits, 1747 name: name, 1748 pipeline: pipeline, 1749 queued_at: queued_at, 1750 started_at: queued_at + started_from, 1751 **opts) 1752 end 1753 end 1754 1755 describe '#branch?' do 1756 subject { pipeline.branch? } 1757 1758 let(:pipeline) { build(:ci_empty_pipeline, :created) } 1759 1760 context 'when ref is not a tag' do 1761 before do 1762 pipeline.tag = false 1763 end 1764 1765 it 'return true' do 1766 is_expected.to be_truthy 1767 end 1768 1769 context 'when pipeline is merge request' do 1770 let(:pipeline) { build(:ci_pipeline, merge_request: merge_request) } 1771 1772 let(:merge_request) do 1773 create(:merge_request, :simple, 1774 source_project: project, 1775 target_project: project) 1776 end 1777 1778 it 'returns false' do 1779 is_expected.to be_falsey 1780 end 1781 end 1782 end 1783 1784 context 'when ref is a tag' do 1785 before do 1786 pipeline.tag = true 1787 end 1788 1789 it 'return false' do 1790 is_expected.to be_falsey 1791 end 1792 end 1793 end 1794 1795 describe '#git_ref' do 1796 subject { pipeline.send(:git_ref) } 1797 1798 context 'when ref is branch' do 1799 let(:pipeline) { create(:ci_pipeline, tag: false) } 1800 1801 it 'returns branch ref' do 1802 is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref.to_s) 1803 end 1804 end 1805 1806 context 'when ref is tag' do 1807 let(:pipeline) { create(:ci_pipeline, tag: true) } 1808 1809 it 'returns branch ref' do 1810 is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.ref.to_s) 1811 end 1812 end 1813 1814 context 'when ref is merge request' do 1815 let(:pipeline) do 1816 create(:ci_pipeline, 1817 source: :merge_request_event, 1818 merge_request: merge_request) 1819 end 1820 1821 let(:merge_request) do 1822 create(:merge_request, 1823 source_project: project, 1824 source_branch: 'feature', 1825 target_project: project, 1826 target_branch: 'master') 1827 end 1828 1829 it 'returns branch ref' do 1830 is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref.to_s) 1831 end 1832 end 1833 end 1834 1835 describe 'ref_exists?' do 1836 context 'when repository exists' do 1837 using RSpec::Parameterized::TableSyntax 1838 1839 let_it_be(:pipeline, refind: true) { create(:ci_empty_pipeline) } 1840 1841 where(:tag, :ref, :result) do 1842 false | 'master' | true 1843 false | 'non-existent-branch' | false 1844 true | 'v1.1.0' | true 1845 true | 'non-existent-tag' | false 1846 end 1847 1848 with_them do 1849 before do 1850 pipeline.update!(tag: tag, ref: ref) 1851 end 1852 1853 it "correctly detects ref" do 1854 expect(pipeline.ref_exists?).to be result 1855 end 1856 end 1857 end 1858 1859 context 'when repository does not exist' do 1860 let(:pipeline) { build(:ci_empty_pipeline, ref: 'master', project: build(:project)) } 1861 1862 it 'always returns false' do 1863 expect(pipeline.ref_exists?).to eq false 1864 end 1865 end 1866 end 1867 1868 context 'with non-empty project' do 1869 let(:pipeline) do 1870 create(:ci_pipeline, 1871 ref: project.default_branch, 1872 sha: project.commit.sha) 1873 end 1874 1875 describe '#lazy_ref_commit' do 1876 let(:another) do 1877 create(:ci_pipeline, 1878 ref: 'feature', 1879 sha: project.commit('feature').sha) 1880 end 1881 1882 let(:unicode) do 1883 create(:ci_pipeline, 1884 ref: 'ü/unicode/multi-byte') 1885 end 1886 1887 it 'returns the latest commit for a ref lazily' do 1888 expect(project.repository) 1889 .to receive(:list_commits_by_ref_name).once 1890 .and_call_original 1891 1892 pipeline.lazy_ref_commit 1893 another.lazy_ref_commit 1894 unicode.lazy_ref_commit 1895 1896 expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha 1897 expect(another.lazy_ref_commit.id).to eq another.sha 1898 expect(unicode.lazy_ref_commit).to be_nil 1899 end 1900 end 1901 1902 describe '#latest?' do 1903 context 'with latest sha' do 1904 it 'returns true' do 1905 expect(pipeline).to be_latest 1906 end 1907 end 1908 1909 context 'with a branch name as the ref' do 1910 it 'looks up a commit for a branch' do 1911 expect(pipeline.ref).to eq 'master' 1912 expect(pipeline).to be_latest 1913 end 1914 end 1915 1916 context 'with a tag name as a ref' do 1917 it 'looks up a commit for a tag' do 1918 expect(project.repository.branch_names).not_to include 'v1.0.0' 1919 1920 pipeline.update!(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true) 1921 1922 expect(pipeline).to be_tag 1923 expect(pipeline).to be_latest 1924 end 1925 end 1926 1927 context 'with not latest sha' do 1928 before do 1929 pipeline.update!(sha: project.commit("#{project.default_branch}~1").sha) 1930 end 1931 1932 it 'returns false' do 1933 expect(pipeline).not_to be_latest 1934 end 1935 end 1936 end 1937 end 1938 1939 describe '#manual_actions' do 1940 subject { pipeline.manual_actions } 1941 1942 let(:pipeline) { create(:ci_empty_pipeline, :created) } 1943 1944 it 'when none defined' do 1945 is_expected.to be_empty 1946 end 1947 1948 context 'when action defined' do 1949 let!(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') } 1950 1951 it 'returns one action' do 1952 is_expected.to contain_exactly(manual) 1953 end 1954 1955 context 'there are multiple of the same name' do 1956 let!(:manual2) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') } 1957 1958 before do 1959 manual.update!(retried: true) 1960 end 1961 1962 it 'returns latest one' do 1963 is_expected.to contain_exactly(manual2) 1964 end 1965 end 1966 end 1967 end 1968 1969 describe '#branch_updated?' do 1970 let(:pipeline) { create(:ci_empty_pipeline, :created) } 1971 1972 context 'when pipeline has before SHA' do 1973 before do 1974 pipeline.update!(before_sha: 'a1b2c3d4') 1975 end 1976 1977 it 'runs on a branch update push' do 1978 expect(pipeline.before_sha).not_to be Gitlab::Git::BLANK_SHA 1979 expect(pipeline.branch_updated?).to be true 1980 end 1981 end 1982 1983 context 'when pipeline does not have before SHA' do 1984 before do 1985 pipeline.update!(before_sha: Gitlab::Git::BLANK_SHA) 1986 end 1987 1988 it 'does not run on a branch updating push' do 1989 expect(pipeline.branch_updated?).to be false 1990 end 1991 end 1992 end 1993 1994 describe '#modified_paths' do 1995 let(:pipeline) { create(:ci_empty_pipeline, :created) } 1996 1997 context 'when old and new revisions are set' do 1998 before do 1999 pipeline.update!(before_sha: '1234abcd', sha: '2345bcde') 2000 end 2001 2002 it 'fetches stats for changes between commits' do 2003 expect(project.repository) 2004 .to receive(:diff_stats).with('1234abcd', '2345bcde') 2005 .and_call_original 2006 2007 pipeline.modified_paths 2008 end 2009 end 2010 2011 context 'when either old or new revision is missing' do 2012 before do 2013 pipeline.update!(before_sha: Gitlab::Git::BLANK_SHA) 2014 end 2015 2016 it 'returns nil' do 2017 expect(pipeline.modified_paths).to be_nil 2018 end 2019 end 2020 2021 context 'when source is merge request' do 2022 let(:pipeline) do 2023 create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) 2024 end 2025 2026 let(:merge_request) do 2027 create(:merge_request, :simple, 2028 source_project: project, 2029 target_project: project) 2030 end 2031 2032 it 'returns merge request modified paths' do 2033 expect(pipeline.modified_paths).to match(merge_request.modified_paths) 2034 end 2035 end 2036 2037 context 'when source is an external pull request' do 2038 let(:pipeline) do 2039 create(:ci_pipeline, source: :external_pull_request_event, external_pull_request: external_pull_request) 2040 end 2041 2042 let(:external_pull_request) do 2043 create(:external_pull_request, project: project, target_sha: '281d3a7', source_sha: '498214d') 2044 end 2045 2046 it 'returns external pull request modified paths' do 2047 expect(pipeline.modified_paths).to match(external_pull_request.modified_paths) 2048 end 2049 end 2050 end 2051 2052 describe '#all_worktree_paths' do 2053 let(:files) { { 'main.go' => '', 'mocks/mocks.go' => '' } } 2054 let(:project) { create(:project, :custom_repo, files: files) } 2055 let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.head_commit.sha) } 2056 2057 it 'returns all file paths cached' do 2058 expect(project.repository).to receive(:ls_files).with(pipeline.sha).once.and_call_original 2059 expect(pipeline.all_worktree_paths).to eq(files.keys) 2060 expect(pipeline.all_worktree_paths).to eq(files.keys) 2061 end 2062 end 2063 2064 describe '#top_level_worktree_paths' do 2065 let(:files) { { 'main.go' => '', 'mocks/mocks.go' => '' } } 2066 let(:project) { create(:project, :custom_repo, files: files) } 2067 let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.head_commit.sha) } 2068 2069 it 'returns top-level file paths cached' do 2070 expect(project.repository).to receive(:tree).with(pipeline.sha).once.and_call_original 2071 expect(pipeline.top_level_worktree_paths).to eq(['main.go']) 2072 expect(pipeline.top_level_worktree_paths).to eq(['main.go']) 2073 end 2074 end 2075 2076 describe '#has_kubernetes_active?' do 2077 let(:pipeline) { create(:ci_empty_pipeline, :created, project: project) } 2078 2079 context 'when kubernetes is active' do 2080 context 'when user configured kubernetes from CI/CD > Clusters' do 2081 let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } 2082 let(:project) { cluster.project } 2083 2084 it 'returns true' do 2085 expect(pipeline).to have_kubernetes_active 2086 end 2087 end 2088 end 2089 2090 context 'when kubernetes is not active' do 2091 it 'returns false' do 2092 expect(pipeline).not_to have_kubernetes_active 2093 end 2094 end 2095 end 2096 2097 describe '#has_warnings?' do 2098 subject { pipeline.has_warnings? } 2099 2100 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2101 2102 context 'build which is allowed to fail fails' do 2103 before do 2104 create :ci_build, :success, pipeline: pipeline, name: 'rspec' 2105 create :ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop' 2106 end 2107 2108 it 'returns true' do 2109 is_expected.to be_truthy 2110 end 2111 end 2112 2113 context 'build which is allowed to fail succeeds' do 2114 before do 2115 create :ci_build, :success, pipeline: pipeline, name: 'rspec' 2116 create :ci_build, :allowed_to_fail, :success, pipeline: pipeline, name: 'rubocop' 2117 end 2118 2119 it 'returns false' do 2120 is_expected.to be_falsey 2121 end 2122 end 2123 2124 context 'build is retried and succeeds' do 2125 before do 2126 create :ci_build, :success, pipeline: pipeline, name: 'rubocop' 2127 create :ci_build, :failed, pipeline: pipeline, name: 'rspec' 2128 create :ci_build, :success, pipeline: pipeline, name: 'rspec' 2129 end 2130 2131 it 'returns false' do 2132 is_expected.to be_falsey 2133 end 2134 end 2135 2136 context 'bridge which is allowed to fail fails' do 2137 before do 2138 create :ci_bridge, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop' 2139 end 2140 2141 it 'returns true' do 2142 is_expected.to be_truthy 2143 end 2144 end 2145 2146 context 'bridge which is allowed to fail is successful' do 2147 before do 2148 create :ci_bridge, :allowed_to_fail, :success, pipeline: pipeline, name: 'rubocop' 2149 end 2150 2151 it 'returns false' do 2152 is_expected.to be_falsey 2153 end 2154 end 2155 end 2156 2157 describe '#number_of_warnings' do 2158 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2159 2160 it 'returns the number of warnings' do 2161 create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop') 2162 create(:ci_bridge, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop') 2163 2164 expect(pipeline.number_of_warnings).to eq(2) 2165 end 2166 2167 it 'supports eager loading of the number of warnings' do 2168 pipeline2 = create(:ci_empty_pipeline, :created) 2169 2170 create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop') 2171 create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline2, name: 'rubocop') 2172 2173 pipelines = project.ci_pipelines.to_a 2174 2175 pipelines.each(&:number_of_warnings) 2176 2177 # To run the queries we need to actually use the lazy objects, which we do 2178 # by just sending "to_i" to them. 2179 amount = ActiveRecord::QueryRecorder 2180 .new { pipelines.each { |p| p.number_of_warnings.to_i } } 2181 .count 2182 2183 expect(amount).to eq(1) 2184 end 2185 end 2186 2187 describe '#needs_processing?' do 2188 using RSpec::Parameterized::TableSyntax 2189 2190 subject { pipeline.needs_processing? } 2191 2192 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2193 2194 where(:processed, :result) do 2195 nil | true 2196 false | true 2197 true | false 2198 end 2199 2200 with_them do 2201 let(:build) do 2202 create(:ci_build, :success, pipeline: pipeline, name: 'rubocop') 2203 end 2204 2205 before do 2206 build.update_column(:processed, processed) 2207 end 2208 2209 it { is_expected.to eq(result) } 2210 end 2211 end 2212 2213 context 'with outdated pipelines' do 2214 before_all do 2215 create_pipeline(:canceled, 'ref', 'A') 2216 create_pipeline(:success, 'ref', 'A') 2217 create_pipeline(:failed, 'ref', 'B') 2218 create_pipeline(:skipped, 'feature', 'C') 2219 end 2220 2221 def create_pipeline(status, ref, sha) 2222 create( 2223 :ci_empty_pipeline, 2224 status: status, 2225 ref: ref, 2226 sha: sha 2227 ) 2228 end 2229 2230 describe '.newest_first' do 2231 it 'returns the pipelines from new to old' do 2232 expect(described_class.newest_first.pluck(:status)) 2233 .to eq(%w[skipped failed success canceled]) 2234 end 2235 2236 it 'searches limited backlog' do 2237 expect(described_class.newest_first(limit: 1).pluck(:status)) 2238 .to eq(%w[skipped]) 2239 end 2240 end 2241 2242 describe '.latest_status' do 2243 context 'when no ref is specified' do 2244 it 'returns the status of the latest pipeline' do 2245 expect(described_class.latest_status).to eq('skipped') 2246 end 2247 end 2248 2249 context 'when ref is specified' do 2250 it 'returns the status of the latest pipeline for the given ref' do 2251 expect(described_class.latest_status('ref')).to eq('failed') 2252 end 2253 end 2254 end 2255 2256 describe '.latest_successful_for_ref' do 2257 let!(:latest_successful_pipeline) do 2258 create_pipeline(:success, 'ref', 'D') 2259 end 2260 2261 it 'returns the latest successful pipeline' do 2262 expect(described_class.latest_successful_for_ref('ref')) 2263 .to eq(latest_successful_pipeline) 2264 end 2265 end 2266 2267 describe '.latest_running_for_ref' do 2268 let!(:latest_running_pipeline) do 2269 create_pipeline(:running, 'ref', 'D') 2270 end 2271 2272 it 'returns the latest running pipeline' do 2273 expect(described_class.latest_running_for_ref('ref')) 2274 .to eq(latest_running_pipeline) 2275 end 2276 end 2277 2278 describe '.latest_failed_for_ref' do 2279 let!(:latest_failed_pipeline) do 2280 create_pipeline(:failed, 'ref', 'D') 2281 end 2282 2283 it 'returns the latest failed pipeline' do 2284 expect(described_class.latest_failed_for_ref('ref')) 2285 .to eq(latest_failed_pipeline) 2286 end 2287 end 2288 2289 describe '.latest_successful_for_sha' do 2290 let!(:latest_successful_pipeline) do 2291 create_pipeline(:success, 'ref', 'awesomesha') 2292 end 2293 2294 it 'returns the latest successful pipeline' do 2295 expect(described_class.latest_successful_for_sha('awesomesha')) 2296 .to eq(latest_successful_pipeline) 2297 end 2298 end 2299 2300 describe '.latest_successful_for_refs' do 2301 subject(:latest_successful_for_refs) { described_class.latest_successful_for_refs(refs) } 2302 2303 context 'when refs are specified' do 2304 let(:refs) { %w(first_ref second_ref third_ref) } 2305 2306 before do 2307 create(:ci_empty_pipeline, id: 1001, status: :success, ref: 'first_ref', sha: 'sha') 2308 create(:ci_empty_pipeline, id: 1002, status: :success, ref: 'second_ref', sha: 'sha') 2309 end 2310 2311 let!(:latest_successful_pipeline_for_first_ref) do 2312 create(:ci_empty_pipeline, id: 2001, status: :success, ref: 'first_ref', sha: 'sha') 2313 end 2314 2315 let!(:latest_successful_pipeline_for_second_ref) do 2316 create(:ci_empty_pipeline, id: 2002, status: :success, ref: 'second_ref', sha: 'sha') 2317 end 2318 2319 it 'returns the latest successful pipeline for both refs' do 2320 expect(latest_successful_for_refs).to eq({ 2321 'first_ref' => latest_successful_pipeline_for_first_ref, 2322 'second_ref' => latest_successful_pipeline_for_second_ref 2323 }) 2324 end 2325 end 2326 2327 context 'when no refs are specified' do 2328 let(:refs) { [] } 2329 2330 it 'returns an empty relation whenno refs are specified' do 2331 expect(latest_successful_for_refs).to be_empty 2332 end 2333 end 2334 end 2335 end 2336 2337 describe '.latest_pipeline_per_commit' do 2338 let!(:commit_123_ref_master) do 2339 create( 2340 :ci_empty_pipeline, 2341 status: 'success', 2342 ref: 'master', 2343 sha: '123' 2344 ) 2345 end 2346 2347 let!(:commit_123_ref_develop) do 2348 create( 2349 :ci_empty_pipeline, 2350 status: 'success', 2351 ref: 'develop', 2352 sha: '123' 2353 ) 2354 end 2355 2356 let!(:commit_456_ref_test) do 2357 create( 2358 :ci_empty_pipeline, 2359 status: 'success', 2360 ref: 'test', 2361 sha: '456' 2362 ) 2363 end 2364 2365 context 'without a ref' do 2366 it 'returns a Hash containing the latest pipeline per commit for all refs' do 2367 result = described_class.latest_pipeline_per_commit(%w[123 456]) 2368 2369 expect(result).to match( 2370 '123' => commit_123_ref_develop, 2371 '456' => commit_456_ref_test 2372 ) 2373 end 2374 2375 it 'only includes the latest pipeline of the given commit SHAs' do 2376 result = described_class.latest_pipeline_per_commit(%w[123]) 2377 2378 expect(result).to match( 2379 '123' => commit_123_ref_develop 2380 ) 2381 end 2382 2383 context 'when there are two pipelines for a ref and SHA' do 2384 let!(:commit_123_ref_master_latest) do 2385 create( 2386 :ci_empty_pipeline, 2387 status: 'failed', 2388 ref: 'master', 2389 sha: '123', 2390 project: project 2391 ) 2392 end 2393 2394 it 'returns the latest pipeline' do 2395 result = described_class.latest_pipeline_per_commit(%w[123]) 2396 2397 expect(result).to match( 2398 '123' => commit_123_ref_master_latest 2399 ) 2400 end 2401 end 2402 end 2403 2404 context 'with a ref' do 2405 it 'only includes the pipelines for the given ref' do 2406 result = described_class.latest_pipeline_per_commit(%w[123 456], 'master') 2407 2408 expect(result).to match( 2409 '123' => commit_123_ref_master 2410 ) 2411 end 2412 end 2413 2414 context 'when method is scoped' do 2415 let!(:commit_123_ref_master_parent_pipeline) do 2416 create( 2417 :ci_pipeline, 2418 sha: '123', 2419 ref: 'master', 2420 project: project 2421 ) 2422 end 2423 2424 let!(:commit_123_ref_master_child_pipeline) do 2425 create( 2426 :ci_pipeline, 2427 sha: '123', 2428 ref: 'master', 2429 project: project, 2430 child_of: commit_123_ref_master_parent_pipeline 2431 ) 2432 end 2433 2434 it 'returns the latest pipeline after applying the scope' do 2435 result = described_class.ci_sources.latest_pipeline_per_commit(%w[123], 'master') 2436 2437 expect(result).to match( 2438 '123' => commit_123_ref_master_parent_pipeline 2439 ) 2440 end 2441 end 2442 end 2443 2444 describe '.latest_successful_ids_per_project' do 2445 let(:projects) { create_list(:project, 2) } 2446 let!(:pipeline1) { create(:ci_pipeline, :success, project: projects[0]) } 2447 let!(:pipeline2) { create(:ci_pipeline, :success, project: projects[0]) } 2448 let!(:pipeline3) { create(:ci_pipeline, :failed, project: projects[0]) } 2449 let!(:pipeline4) { create(:ci_pipeline, :success, project: projects[1]) } 2450 2451 it 'returns expected pipeline ids' do 2452 expect(described_class.latest_successful_ids_per_project) 2453 .to contain_exactly(pipeline2, pipeline4) 2454 end 2455 end 2456 2457 describe '.last_finished_for_ref_id' do 2458 let(:branch) { project.default_branch } 2459 let(:ref) { project.ci_refs.take } 2460 let(:dangling_source) { Enums::Ci::Pipeline.sources[:ondemand_dast_scan] } 2461 let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) } 2462 let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) } 2463 let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) } 2464 let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) } 2465 let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, source: dangling_source) } 2466 2467 it 'returns the expected pipeline' do 2468 result = described_class.last_finished_for_ref_id(ref.id) 2469 expect(result).to eq(pipeline4) 2470 end 2471 end 2472 2473 describe '.internal_sources' do 2474 subject { described_class.internal_sources } 2475 2476 it { is_expected.to be_an(Array) } 2477 end 2478 2479 describe '.bridgeable_statuses' do 2480 subject { described_class.bridgeable_statuses } 2481 2482 it { is_expected.to be_an(Array) } 2483 it { is_expected.not_to include('created', 'waiting_for_resource', 'preparing', 'pending') } 2484 end 2485 2486 describe '#status', :sidekiq_inline do 2487 subject { pipeline.reload.status } 2488 2489 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2490 2491 let(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') } 2492 2493 context 'on waiting for resource' do 2494 before do 2495 allow(build).to receive(:with_resource_group?) { true } 2496 allow(Ci::ResourceGroups::AssignResourceFromResourceGroupWorker).to receive(:perform_async) 2497 2498 build.enqueue 2499 end 2500 2501 it { is_expected.to eq('waiting_for_resource') } 2502 end 2503 2504 context 'on prepare' do 2505 before do 2506 # Prevent skipping directly to 'pending' 2507 allow(build).to receive(:prerequisites).and_return([double]) 2508 allow(Ci::BuildPrepareWorker).to receive(:perform_async) 2509 2510 build.enqueue 2511 end 2512 2513 it { is_expected.to eq('preparing') } 2514 end 2515 2516 context 'on queuing' do 2517 before do 2518 build.enqueue 2519 end 2520 2521 it { is_expected.to eq('pending') } 2522 end 2523 2524 context 'on run' do 2525 before do 2526 build.enqueue 2527 build.reload.run 2528 end 2529 2530 it { is_expected.to eq('running') } 2531 end 2532 2533 context 'on drop' do 2534 before do 2535 build.drop 2536 end 2537 2538 it { is_expected.to eq('failed') } 2539 end 2540 2541 context 'on success' do 2542 before do 2543 build.success 2544 end 2545 2546 it { is_expected.to eq('success') } 2547 end 2548 2549 context 'on cancel' do 2550 before do 2551 build.cancel 2552 end 2553 2554 context 'when build is pending' do 2555 let(:build) do 2556 create(:ci_build, :pending, pipeline: pipeline) 2557 end 2558 2559 it { is_expected.to eq('canceled') } 2560 end 2561 end 2562 2563 context 'on failure and build retry' do 2564 before do 2565 stub_not_protect_default_branch 2566 2567 build.drop 2568 project.add_developer(user) 2569 2570 Ci::Build.retry(build, user) 2571 end 2572 2573 # We are changing a state: created > failed > running 2574 # Instead of: created > failed > pending 2575 # Since the pipeline already run, so it should not be pending anymore 2576 2577 it { is_expected.to eq('running') } 2578 end 2579 end 2580 2581 describe '#detailed_status' do 2582 subject { pipeline.detailed_status(user) } 2583 2584 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2585 2586 context 'when pipeline is created' do 2587 let(:pipeline) { create(:ci_pipeline, :created) } 2588 2589 it 'returns detailed status for created pipeline' do 2590 expect(subject.text).to eq s_('CiStatusText|created') 2591 end 2592 end 2593 2594 context 'when pipeline is pending' do 2595 let(:pipeline) { create(:ci_pipeline, status: :pending) } 2596 2597 it 'returns detailed status for pending pipeline' do 2598 expect(subject.text).to eq s_('CiStatusText|pending') 2599 end 2600 end 2601 2602 context 'when pipeline is running' do 2603 let(:pipeline) { create(:ci_pipeline, status: :running) } 2604 2605 it 'returns detailed status for running pipeline' do 2606 expect(subject.text).to eq s_('CiStatus|running') 2607 end 2608 end 2609 2610 context 'when pipeline is successful' do 2611 let(:pipeline) { create(:ci_pipeline, status: :success) } 2612 2613 it 'returns detailed status for successful pipeline' do 2614 expect(subject.text).to eq s_('CiStatusText|passed') 2615 end 2616 end 2617 2618 context 'when pipeline is failed' do 2619 let(:pipeline) { create(:ci_pipeline, status: :failed) } 2620 2621 it 'returns detailed status for failed pipeline' do 2622 expect(subject.text).to eq s_('CiStatusText|failed') 2623 end 2624 end 2625 2626 context 'when pipeline is canceled' do 2627 let(:pipeline) { create(:ci_pipeline, status: :canceled) } 2628 2629 it 'returns detailed status for canceled pipeline' do 2630 expect(subject.text).to eq s_('CiStatusText|canceled') 2631 end 2632 end 2633 2634 context 'when pipeline is skipped' do 2635 let(:pipeline) { create(:ci_pipeline, status: :skipped) } 2636 2637 it 'returns detailed status for skipped pipeline' do 2638 expect(subject.text).to eq s_('CiStatusText|skipped') 2639 end 2640 end 2641 2642 context 'when pipeline is blocked' do 2643 let(:pipeline) { create(:ci_pipeline, status: :manual) } 2644 2645 it 'returns detailed status for blocked pipeline' do 2646 expect(subject.text).to eq s_('CiStatusText|blocked') 2647 end 2648 end 2649 2650 context 'when pipeline is successful but with warnings' do 2651 let(:pipeline) { create(:ci_pipeline, status: :success) } 2652 2653 before do 2654 create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline) 2655 end 2656 2657 it 'retruns detailed status for successful pipeline with warnings' do 2658 expect(subject.label).to eq(s_('CiStatusLabel|passed with warnings')) 2659 end 2660 end 2661 end 2662 2663 describe '#cancelable?' do 2664 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2665 2666 %i[created running pending].each do |status0| 2667 context "when there is a build #{status0}" do 2668 before do 2669 create(:ci_build, status0, pipeline: pipeline) 2670 end 2671 2672 it 'is cancelable' do 2673 expect(pipeline.cancelable?).to be_truthy 2674 end 2675 end 2676 2677 context "when there is an external job #{status0}" do 2678 before do 2679 create(:generic_commit_status, status0, pipeline: pipeline) 2680 end 2681 2682 it 'is cancelable' do 2683 expect(pipeline.cancelable?).to be_truthy 2684 end 2685 end 2686 2687 %i[success failed canceled].each do |status1| 2688 context "when there are generic_commit_status jobs for #{status0} and #{status1}" do 2689 before do 2690 create(:generic_commit_status, status0, pipeline: pipeline) 2691 create(:generic_commit_status, status1, pipeline: pipeline) 2692 end 2693 2694 it 'is cancelable' do 2695 expect(pipeline.cancelable?).to be_truthy 2696 end 2697 end 2698 2699 context "when there are generic_commit_status and ci_build jobs for #{status0} and #{status1}" do 2700 before do 2701 create(:generic_commit_status, status0, pipeline: pipeline) 2702 create(:ci_build, status1, pipeline: pipeline) 2703 end 2704 2705 it 'is cancelable' do 2706 expect(pipeline.cancelable?).to be_truthy 2707 end 2708 end 2709 2710 context "when there are ci_build jobs for #{status0} and #{status1}" do 2711 before do 2712 create(:ci_build, status0, pipeline: pipeline) 2713 create(:ci_build, status1, pipeline: pipeline) 2714 end 2715 2716 it 'is cancelable' do 2717 expect(pipeline.cancelable?).to be_truthy 2718 end 2719 end 2720 end 2721 end 2722 2723 %i[success failed canceled].each do |status| 2724 context "when there is a build #{status}" do 2725 before do 2726 create(:ci_build, status, pipeline: pipeline) 2727 end 2728 2729 it 'is not cancelable' do 2730 expect(pipeline.cancelable?).to be_falsey 2731 end 2732 end 2733 2734 context "when there is an external job #{status}" do 2735 before do 2736 create(:generic_commit_status, status, pipeline: pipeline) 2737 end 2738 2739 it 'is not cancelable' do 2740 expect(pipeline.cancelable?).to be_falsey 2741 end 2742 end 2743 end 2744 2745 context 'when there is a manual action present in the pipeline' do 2746 before do 2747 create(:ci_build, :manual, pipeline: pipeline) 2748 end 2749 2750 it 'is not cancelable' do 2751 expect(pipeline).not_to be_cancelable 2752 end 2753 end 2754 end 2755 2756 describe '#cancel_running' do 2757 subject(:latest_status) { pipeline.statuses.pluck(:status) } 2758 2759 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2760 2761 context 'when there is a running external job and a regular job' do 2762 before do 2763 create(:ci_build, :running, pipeline: pipeline) 2764 create(:generic_commit_status, :running, pipeline: pipeline) 2765 2766 pipeline.cancel_running 2767 end 2768 2769 it 'cancels both jobs' do 2770 expect(latest_status).to contain_exactly('canceled', 'canceled') 2771 end 2772 end 2773 2774 context 'when jobs are in different stages' do 2775 before do 2776 create(:ci_build, :running, stage_idx: 0, pipeline: pipeline) 2777 create(:ci_build, :running, stage_idx: 1, pipeline: pipeline) 2778 2779 pipeline.cancel_running 2780 end 2781 2782 it 'cancels both jobs' do 2783 expect(latest_status).to contain_exactly('canceled', 'canceled') 2784 end 2785 end 2786 2787 context 'when there are created builds present in the pipeline' do 2788 before do 2789 create(:ci_build, :running, stage_idx: 0, pipeline: pipeline) 2790 create(:ci_build, :created, stage_idx: 1, pipeline: pipeline) 2791 2792 pipeline.cancel_running 2793 end 2794 2795 it 'cancels created builds' do 2796 expect(latest_status).to eq %w(canceled canceled) 2797 end 2798 end 2799 2800 context 'preloading relations' do 2801 let(:pipeline1) { create(:ci_empty_pipeline, :created) } 2802 let(:pipeline2) { create(:ci_empty_pipeline, :created) } 2803 2804 before do 2805 create(:ci_build, :pending, pipeline: pipeline1) 2806 create(:generic_commit_status, :pending, pipeline: pipeline1) 2807 2808 create(:ci_build, :pending, pipeline: pipeline2) 2809 create(:ci_build, :pending, pipeline: pipeline2) 2810 create(:generic_commit_status, :pending, pipeline: pipeline2) 2811 create(:generic_commit_status, :pending, pipeline: pipeline2) 2812 create(:generic_commit_status, :pending, pipeline: pipeline2) 2813 end 2814 2815 it 'preloads relations for each build to avoid N+1 queries' do 2816 control1 = ActiveRecord::QueryRecorder.new do 2817 pipeline1.cancel_running 2818 end 2819 2820 control2 = ActiveRecord::QueryRecorder.new do 2821 pipeline2.cancel_running 2822 end 2823 2824 extra_update_queries = 4 # transition ... => :canceled, queue pop 2825 extra_generic_commit_status_validation_queries = 2 # name_uniqueness_across_types 2826 2827 expect(control2.count).to eq(control1.count + extra_update_queries + extra_generic_commit_status_validation_queries) 2828 end 2829 end 2830 2831 context 'when the first try cannot get an exclusive lock' do 2832 let(:retries) { 1 } 2833 2834 subject(:cancel_running) { pipeline.cancel_running(retries: retries) } 2835 2836 before do 2837 build = create(:ci_build, :running, pipeline: pipeline) 2838 2839 allow(pipeline.cancelable_statuses).to receive(:find_in_batches).and_yield([build]) 2840 2841 call_count = 0 2842 allow(build).to receive(:cancel).and_wrap_original do |original, *args| 2843 call_count >= retries ? raise(ActiveRecord::StaleObjectError) : original.call(*args) 2844 2845 call_count += 1 2846 end 2847 end 2848 2849 it 'retries again and cancels the build' do 2850 cancel_running 2851 2852 expect(latest_status).to contain_exactly('canceled') 2853 end 2854 2855 context 'when the retries parameter is 0' do 2856 let(:retries) { 0 } 2857 2858 it 'raises error' do 2859 expect do 2860 cancel_running 2861 end.to raise_error(ActiveRecord::StaleObjectError) 2862 end 2863 end 2864 end 2865 end 2866 2867 describe '#retry_failed' do 2868 subject(:latest_status) { pipeline.latest_statuses.pluck(:status) } 2869 2870 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2871 2872 before do 2873 stub_not_protect_default_branch 2874 2875 project.add_developer(user) 2876 end 2877 2878 context 'when there is a failed build and failed external status' do 2879 before do 2880 create(:ci_build, :failed, name: 'build', pipeline: pipeline) 2881 create(:generic_commit_status, :failed, name: 'jenkins', pipeline: pipeline) 2882 2883 pipeline.retry_failed(user) 2884 end 2885 2886 it 'retries only build' do 2887 expect(latest_status).to contain_exactly('pending', 'failed') 2888 end 2889 end 2890 2891 context 'when builds are in different stages' do 2892 before do 2893 create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline) 2894 create(:ci_build, :failed, name: 'jenkins', stage_idx: 1, pipeline: pipeline) 2895 2896 pipeline.retry_failed(user) 2897 end 2898 2899 it 'retries both builds' do 2900 expect(latest_status).to contain_exactly('pending', 'created') 2901 end 2902 end 2903 2904 context 'when there are canceled and failed' do 2905 before do 2906 create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline) 2907 create(:ci_build, :canceled, name: 'jenkins', stage_idx: 1, pipeline: pipeline) 2908 2909 pipeline.retry_failed(user) 2910 end 2911 2912 it 'retries both builds' do 2913 expect(latest_status).to contain_exactly('pending', 'created') 2914 end 2915 end 2916 end 2917 2918 describe 'hooks trigerring' do 2919 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 2920 2921 %i[ 2922 enqueue 2923 request_resource 2924 prepare 2925 run 2926 skip 2927 drop 2928 succeed 2929 cancel 2930 block 2931 delay 2932 ].each do |action| 2933 context "when pipeline action is #{action}" do 2934 let(:pipeline_action) { action } 2935 2936 it 'schedules a new PipelineHooksWorker job' do 2937 expect(PipelineHooksWorker).to receive(:perform_async).with(pipeline.id) 2938 2939 pipeline.reload.public_send(pipeline_action) 2940 end 2941 end 2942 end 2943 end 2944 2945 describe "#merge_requests_as_head_pipeline" do 2946 let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline, status: 'created', ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') } 2947 2948 it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do 2949 allow_next_instance_of(MergeRequest) do |instance| 2950 allow(instance).to receive(:diff_head_sha) { 'a288a022a53a5a944fae87bcec6efc87b7061808' } 2951 end 2952 merge_request = create(:merge_request, source_project: project, head_pipeline: pipeline, source_branch: pipeline.ref) 2953 2954 expect(pipeline.merge_requests_as_head_pipeline).to eq([merge_request]) 2955 end 2956 2957 it "doesn't return merge requests whose source branch doesn't match the pipeline's ref" do 2958 create(:merge_request, :simple, source_project: project) 2959 2960 expect(pipeline.merge_requests_as_head_pipeline).to be_empty 2961 end 2962 2963 it "doesn't return merge requests whose `diff_head_sha` doesn't match the pipeline's SHA" do 2964 create(:merge_request, source_project: project, source_branch: pipeline.ref) 2965 allow_next_instance_of(MergeRequest) do |instance| 2966 allow(instance).to receive(:diff_head_sha) { '97de212e80737a608d939f648d959671fb0a0142b' } 2967 end 2968 2969 expect(pipeline.merge_requests_as_head_pipeline).to be_empty 2970 end 2971 end 2972 2973 describe '#all_merge_requests' do 2974 let_it_be_with_reload(:project) { create(:project) } 2975 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created, project: project) } 2976 2977 shared_examples 'a method that returns all merge requests for a given pipeline' do 2978 let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: pipeline_project, ref: 'master') } 2979 2980 it 'returns all merge requests having the same source branch and the pipeline sha' do 2981 merge_request = create(:merge_request, source_project: pipeline_project, target_project: project, source_branch: pipeline.ref) 2982 2983 create(:merge_request_diff, merge_request: merge_request).tap do |diff| 2984 create(:merge_request_diff_commit, merge_request_diff: diff, sha: pipeline.sha) 2985 end 2986 2987 expect(pipeline.all_merge_requests).to eq([merge_request]) 2988 end 2989 2990 it "doesn't return merge requests having the same source branch without the pipeline sha" do 2991 merge_request = create(:merge_request, source_project: pipeline_project, target_project: project, source_branch: pipeline.ref) 2992 create(:merge_request_diff, merge_request: merge_request).tap do |diff| 2993 create(:merge_request_diff_commit, merge_request_diff: diff, sha: 'unrelated') 2994 end 2995 2996 expect(pipeline.all_merge_requests).to be_empty 2997 end 2998 2999 it "doesn't return merge requests having a different source branch" do 3000 create(:merge_request, source_project: pipeline_project, target_project: project, source_branch: 'feature', target_branch: 'master') 3001 3002 expect(pipeline.all_merge_requests).to be_empty 3003 end 3004 3005 context 'when there is a merge request pipeline' do 3006 let(:source_branch) { 'feature' } 3007 let(:target_branch) { 'master' } 3008 3009 let!(:pipeline) do 3010 create(:ci_pipeline, 3011 source: :merge_request_event, 3012 project: pipeline_project, 3013 ref: source_branch, 3014 merge_request: merge_request) 3015 end 3016 3017 let(:merge_request) do 3018 create(:merge_request, 3019 source_project: pipeline_project, 3020 source_branch: source_branch, 3021 target_project: project, 3022 target_branch: target_branch) 3023 end 3024 3025 it 'returns an associated merge request' do 3026 expect(pipeline.all_merge_requests).to eq([merge_request]) 3027 end 3028 3029 context 'when there is another merge request pipeline that targets a different branch' do 3030 let(:target_branch_2) { 'merge-test' } 3031 3032 let!(:pipeline_2) do 3033 create(:ci_pipeline, 3034 source: :merge_request_event, 3035 project: pipeline_project, 3036 ref: source_branch, 3037 merge_request: merge_request_2) 3038 end 3039 3040 let(:merge_request_2) do 3041 create(:merge_request, 3042 source_project: pipeline_project, 3043 source_branch: source_branch, 3044 target_project: project, 3045 target_branch: target_branch_2) 3046 end 3047 3048 it 'does not return an associated merge request' do 3049 expect(pipeline.all_merge_requests).not_to include(merge_request_2) 3050 end 3051 end 3052 end 3053 end 3054 3055 it_behaves_like 'a method that returns all merge requests for a given pipeline' do 3056 let(:pipeline_project) { project } 3057 end 3058 3059 context 'for a fork' do 3060 let(:fork) { fork_project(project) } 3061 3062 it_behaves_like 'a method that returns all merge requests for a given pipeline' do 3063 let(:pipeline_project) { fork } 3064 end 3065 end 3066 end 3067 3068 describe '#related_merge_requests' do 3069 let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } 3070 let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'stable') } 3071 let(:branch_pipeline) { create(:ci_pipeline, ref: 'feature') } 3072 let(:merge_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, merge_request: merge_request) } 3073 3074 context 'for a branch pipeline' do 3075 subject { branch_pipeline.related_merge_requests } 3076 3077 it 'when no merge request is created' do 3078 is_expected.to be_empty 3079 end 3080 3081 it 'when another merge requests are created' do 3082 merge_request 3083 other_merge_request 3084 3085 is_expected.to contain_exactly(merge_request, other_merge_request) 3086 end 3087 end 3088 3089 context 'for a merge pipeline' do 3090 subject { merge_pipeline.related_merge_requests } 3091 3092 it 'when only merge pipeline is created' do 3093 merge_pipeline 3094 3095 is_expected.to contain_exactly(merge_request) 3096 end 3097 3098 it 'when a merge request is created' do 3099 merge_pipeline 3100 other_merge_request 3101 3102 is_expected.to contain_exactly(merge_request, other_merge_request) 3103 end 3104 end 3105 end 3106 3107 describe '#open_merge_requests_refs' do 3108 let!(:pipeline) { create(:ci_pipeline, user: user, ref: 'feature') } 3109 let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } 3110 3111 subject { pipeline.open_merge_requests_refs } 3112 3113 context 'when user is a developer' do 3114 before do 3115 project.add_developer(user) 3116 end 3117 3118 it 'returns open merge requests' do 3119 is_expected.to eq([merge_request.to_reference(full: true)]) 3120 end 3121 3122 it 'does not return closed merge requests' do 3123 merge_request.close! 3124 3125 is_expected.to be_empty 3126 end 3127 3128 context 'limits amount of returned merge requests' do 3129 let!(:other_merge_requests) do 3130 Array.new(4) do |idx| 3131 create(:merge_request, source_project: project, source_branch: 'feature', target_branch: "master-#{idx}") 3132 end 3133 end 3134 3135 let(:other_merge_requests_refs) do 3136 other_merge_requests.map { |mr| mr.to_reference(full: true) } 3137 end 3138 3139 it 'returns only last 4 in a reverse order' do 3140 is_expected.to eq(other_merge_requests_refs.reverse) 3141 end 3142 end 3143 end 3144 3145 context 'when user does not have permissions' do 3146 it 'does not return any merge requests' do 3147 is_expected.to be_empty 3148 end 3149 end 3150 end 3151 3152 describe '#same_family_pipeline_ids' do 3153 subject { pipeline.same_family_pipeline_ids.map(&:id) } 3154 3155 let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) } 3156 3157 context 'when pipeline is not child nor parent' do 3158 it 'returns just the pipeline id' do 3159 expect(subject).to contain_exactly(pipeline.id) 3160 end 3161 end 3162 3163 context 'when pipeline is child' do 3164 let(:parent) { create(:ci_pipeline) } 3165 let!(:pipeline) { create(:ci_pipeline, child_of: parent) } 3166 let!(:sibling) { create(:ci_pipeline, child_of: parent) } 3167 3168 it 'returns parent sibling and self ids' do 3169 expect(subject).to contain_exactly(parent.id, pipeline.id, sibling.id) 3170 end 3171 end 3172 3173 context 'when pipeline is parent' do 3174 let!(:child) { create(:ci_pipeline, child_of: pipeline) } 3175 3176 it 'returns self and child ids' do 3177 expect(subject).to contain_exactly(pipeline.id, child.id) 3178 end 3179 end 3180 3181 context 'when pipeline is a child of a child pipeline' do 3182 let(:ancestor) { create(:ci_pipeline) } 3183 let!(:parent) { create(:ci_pipeline, child_of: ancestor) } 3184 let!(:pipeline) { create(:ci_pipeline, child_of: parent) } 3185 let!(:cousin_parent) { create(:ci_pipeline, child_of: ancestor) } 3186 let!(:cousin) { create(:ci_pipeline, child_of: cousin_parent) } 3187 3188 it 'returns all family ids' do 3189 expect(subject).to contain_exactly( 3190 ancestor.id, parent.id, cousin_parent.id, cousin.id, pipeline.id 3191 ) 3192 end 3193 end 3194 3195 context 'when pipeline is a triggered pipeline' do 3196 let!(:upstream) { create(:ci_pipeline, project: create(:project), upstream_of: pipeline)} 3197 3198 it 'returns self id' do 3199 expect(subject).to contain_exactly(pipeline.id) 3200 end 3201 end 3202 end 3203 3204 describe '#environments_in_self_and_descendants' do 3205 subject { pipeline.environments_in_self_and_descendants } 3206 3207 context 'when pipeline is not child nor parent' do 3208 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 3209 let_it_be(:build, refind: true) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) } 3210 3211 it 'returns just the pipeline environment' do 3212 expect(subject).to contain_exactly(build.deployment.environment) 3213 end 3214 3215 context 'when deployment SHA is not matched' do 3216 before do 3217 build.deployment.update!(sha: 'old-sha') 3218 end 3219 3220 it 'does not return environments' do 3221 expect(subject).to be_empty 3222 end 3223 end 3224 end 3225 3226 context 'when an associated environment does not have deployments' do 3227 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 3228 let_it_be(:build) { create(:ci_build, :stop_review_app, pipeline: pipeline) } 3229 let_it_be(:environment) { create(:environment, project: pipeline.project) } 3230 3231 before_all do 3232 build.metadata.update!(expanded_environment_name: environment.name) 3233 end 3234 3235 it 'does not return environments' do 3236 expect(subject).to be_empty 3237 end 3238 end 3239 3240 context 'when pipeline is in extended family' do 3241 let_it_be(:parent) { create(:ci_pipeline) } 3242 let_it_be(:parent_build) { create(:ci_build, :with_deployment, environment: 'staging', pipeline: parent) } 3243 3244 let_it_be(:pipeline) { create(:ci_pipeline, child_of: parent) } 3245 let_it_be(:build) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) } 3246 3247 let_it_be(:child) { create(:ci_pipeline, child_of: pipeline) } 3248 let_it_be(:child_build) { create(:ci_build, :with_deployment, environment: 'canary', pipeline: child) } 3249 3250 let_it_be(:grandchild) { create(:ci_pipeline, child_of: child) } 3251 let_it_be(:grandchild_build) { create(:ci_build, :with_deployment, environment: 'test', pipeline: grandchild) } 3252 3253 let_it_be(:sibling) { create(:ci_pipeline, child_of: parent) } 3254 let_it_be(:sibling_build) { create(:ci_build, :with_deployment, environment: 'review', pipeline: sibling) } 3255 3256 it 'returns its own environment and from all descendants' do 3257 expected_environments = [ 3258 build.deployment.environment, 3259 child_build.deployment.environment, 3260 grandchild_build.deployment.environment 3261 ] 3262 expect(subject).to match_array(expected_environments) 3263 end 3264 3265 it 'does not return parent environment' do 3266 expect(subject).not_to include(parent_build.deployment.environment) 3267 end 3268 3269 it 'does not return sibling environment' do 3270 expect(subject).not_to include(sibling_build.deployment.environment) 3271 end 3272 end 3273 3274 context 'when each pipeline has multiple environments' do 3275 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 3276 let_it_be(:build1) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) } 3277 let_it_be(:build2) { create(:ci_build, :with_deployment, environment: 'staging', pipeline: pipeline) } 3278 3279 let_it_be(:child) { create(:ci_pipeline, child_of: pipeline) } 3280 let_it_be(:child_build1) { create(:ci_build, :with_deployment, environment: 'canary', pipeline: child) } 3281 let_it_be(:child_build2) { create(:ci_build, :with_deployment, environment: 'test', pipeline: child) } 3282 3283 it 'returns all related environments' do 3284 expected_environments = [ 3285 build1.deployment.environment, 3286 build2.deployment.environment, 3287 child_build1.deployment.environment, 3288 child_build2.deployment.environment 3289 ] 3290 expect(subject).to match_array(expected_environments) 3291 end 3292 end 3293 3294 context 'when pipeline has no environment' do 3295 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 3296 3297 it 'returns empty' do 3298 expect(subject).to be_empty 3299 end 3300 end 3301 end 3302 3303 describe '#root_ancestor' do 3304 subject { pipeline.root_ancestor } 3305 3306 let_it_be(:pipeline) { create(:ci_pipeline) } 3307 3308 context 'when pipeline is child of child pipeline' do 3309 let!(:root_ancestor) { create(:ci_pipeline) } 3310 let!(:parent_pipeline) { create(:ci_pipeline, child_of: root_ancestor) } 3311 let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) } 3312 3313 it 'returns the root ancestor' do 3314 expect(subject).to eq(root_ancestor) 3315 end 3316 end 3317 3318 context 'when pipeline is root ancestor' do 3319 let!(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) } 3320 3321 it 'returns itself' do 3322 expect(subject).to eq(pipeline) 3323 end 3324 end 3325 3326 context 'when pipeline is standalone' do 3327 it 'returns itself' do 3328 expect(subject).to eq(pipeline) 3329 end 3330 end 3331 3332 context 'when pipeline is multi-project downstream pipeline' do 3333 let!(:upstream_pipeline) do 3334 create(:ci_pipeline, project: create(:project), upstream_of: pipeline) 3335 end 3336 3337 it 'ignores cross project ancestors' do 3338 expect(subject).to eq(pipeline) 3339 end 3340 end 3341 end 3342 3343 describe '#stuck?' do 3344 let(:pipeline) { create(:ci_empty_pipeline, :created) } 3345 3346 before do 3347 create(:ci_build, :pending, pipeline: pipeline) 3348 end 3349 3350 context 'when pipeline is stuck' do 3351 it 'is stuck' do 3352 expect(pipeline).to be_stuck 3353 end 3354 end 3355 3356 context 'when pipeline is not stuck' do 3357 before do 3358 create(:ci_runner, :instance, :online) 3359 end 3360 3361 it 'is not stuck' do 3362 expect(pipeline).not_to be_stuck 3363 end 3364 end 3365 end 3366 3367 describe '#add_error_message' do 3368 let(:pipeline) { build_stubbed(:ci_pipeline) } 3369 3370 it 'adds a new pipeline error message' do 3371 pipeline.add_error_message('The error message') 3372 3373 expect(pipeline.messages.map(&:content)).to contain_exactly('The error message') 3374 end 3375 end 3376 3377 describe '#has_yaml_errors?' do 3378 let(:pipeline) { build_stubbed(:ci_pipeline) } 3379 3380 context 'when yaml_errors is set' do 3381 before do 3382 pipeline.yaml_errors = 'File not found' 3383 end 3384 3385 it 'returns true if yaml_errors is set' do 3386 expect(pipeline).to have_yaml_errors 3387 expect(pipeline.yaml_errors).to include('File not foun') 3388 end 3389 end 3390 3391 it 'returns false if yaml_errors is not set' do 3392 expect(pipeline).not_to have_yaml_errors 3393 end 3394 end 3395 3396 describe 'notifications when pipeline success or failed' do 3397 let(:namespace) { create(:namespace) } 3398 let(:project) { create(:project, :repository, namespace: namespace) } 3399 3400 let(:pipeline) do 3401 create(:ci_pipeline, 3402 project: project, 3403 sha: project.commit('master').sha, 3404 user: project.owner) 3405 end 3406 3407 before do 3408 project.add_developer(pipeline.user) 3409 3410 pipeline.user.global_notification_setting 3411 .update!(level: 'custom', failed_pipeline: true, success_pipeline: true) 3412 3413 perform_enqueued_jobs do 3414 pipeline.enqueue 3415 pipeline.run 3416 end 3417 end 3418 3419 shared_examples 'sending a notification' do 3420 it 'sends an email', :sidekiq_might_not_need_inline do 3421 should_only_email(pipeline.user) 3422 end 3423 end 3424 3425 shared_examples 'not sending any notification' do 3426 it 'does not send any email' do 3427 should_not_email_anyone 3428 end 3429 end 3430 3431 context 'with success pipeline' do 3432 it_behaves_like 'sending a notification' do 3433 before do 3434 perform_enqueued_jobs do 3435 pipeline.succeed 3436 end 3437 end 3438 end 3439 3440 it 'enqueues PipelineNotificationWorker' do 3441 expect(PipelineNotificationWorker) 3442 .to receive(:perform_async).with(pipeline.id, ref_status: :success) 3443 3444 pipeline.succeed 3445 end 3446 3447 context 'when pipeline is not the latest' do 3448 before do 3449 create(:ci_pipeline, :success, ci_ref: pipeline.ci_ref) 3450 end 3451 3452 it 'does not pass ref_status' do 3453 expect(PipelineNotificationWorker) 3454 .to receive(:perform_async).with(pipeline.id, ref_status: nil) 3455 3456 pipeline.succeed! 3457 end 3458 end 3459 end 3460 3461 context 'with failed pipeline' do 3462 it_behaves_like 'sending a notification' do 3463 before do 3464 perform_enqueued_jobs do 3465 create(:ci_build, :failed, pipeline: pipeline) 3466 create(:generic_commit_status, :failed, pipeline: pipeline) 3467 3468 pipeline.drop 3469 end 3470 end 3471 end 3472 3473 it 'enqueues PipelineNotificationWorker' do 3474 expect(PipelineNotificationWorker) 3475 .to receive(:perform_async).with(pipeline.id, ref_status: :failed) 3476 3477 pipeline.drop 3478 end 3479 end 3480 3481 context 'with skipped pipeline' do 3482 before do 3483 perform_enqueued_jobs do 3484 pipeline.skip 3485 end 3486 end 3487 3488 it_behaves_like 'not sending any notification' 3489 end 3490 3491 context 'with cancelled pipeline' do 3492 before do 3493 perform_enqueued_jobs do 3494 pipeline.cancel 3495 end 3496 end 3497 3498 it_behaves_like 'not sending any notification' 3499 end 3500 end 3501 3502 describe 'updates ci_ref when pipeline finished' do 3503 context 'when ci_ref exists' do 3504 let!(:pipeline) { create(:ci_pipeline, :running) } 3505 3506 it 'updates the ci_ref' do 3507 expect(pipeline.ci_ref) 3508 .to receive(:update_status_by!).with(pipeline).and_call_original 3509 3510 pipeline.succeed! 3511 end 3512 end 3513 3514 context 'when ci_ref does not exist' do 3515 let!(:pipeline) { create(:ci_pipeline, :running, ci_ref_presence: false) } 3516 3517 it 'does not raise an exception' do 3518 expect { pipeline.succeed! }.not_to raise_error 3519 end 3520 end 3521 end 3522 3523 describe '#ensure_ci_ref!' do 3524 subject { pipeline.ensure_ci_ref! } 3525 3526 context 'when ci_ref does not exist yet' do 3527 let!(:pipeline) { create(:ci_pipeline, ci_ref_presence: false) } 3528 3529 it 'creates a new ci_ref and assigns it' do 3530 expect { subject }.to change { Ci::Ref.count }.by(1) 3531 3532 expect(pipeline.ci_ref).to be_present 3533 end 3534 end 3535 3536 context 'when ci_ref already exists' do 3537 let!(:pipeline) { create(:ci_pipeline) } 3538 3539 it 'fetches a new ci_ref and assigns it' do 3540 expect { subject }.not_to change { Ci::Ref.count } 3541 3542 expect(pipeline.ci_ref).to be_present 3543 end 3544 end 3545 end 3546 3547 describe '#builds_in_self_and_descendants' do 3548 subject(:builds) { pipeline.builds_in_self_and_descendants } 3549 3550 let(:pipeline) { create(:ci_pipeline) } 3551 let!(:build) { create(:ci_build, pipeline: pipeline) } 3552 3553 context 'when pipeline is standalone' do 3554 it 'returns the list of builds' do 3555 expect(builds).to contain_exactly(build) 3556 end 3557 end 3558 3559 context 'when pipeline is parent of another pipeline' do 3560 let(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) } 3561 let!(:child_build) { create(:ci_build, pipeline: child_pipeline) } 3562 3563 it 'returns the list of builds' do 3564 expect(builds).to contain_exactly(build, child_build) 3565 end 3566 end 3567 3568 context 'when pipeline is parent of another parent pipeline' do 3569 let(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) } 3570 let!(:child_build) { create(:ci_build, pipeline: child_pipeline) } 3571 let(:child_of_child_pipeline) { create(:ci_pipeline, child_of: child_pipeline) } 3572 let!(:child_of_child_build) { create(:ci_build, pipeline: child_of_child_pipeline) } 3573 3574 it 'returns the list of builds' do 3575 expect(builds).to contain_exactly(build, child_build, child_of_child_build) 3576 end 3577 end 3578 end 3579 3580 describe '#build_with_artifacts_in_self_and_descendants' do 3581 let_it_be(:pipeline) { create(:ci_pipeline) } 3582 3583 let!(:build) { create(:ci_build, name: 'test', pipeline: pipeline) } 3584 let(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) } 3585 let!(:child_build) { create(:ci_build, :artifacts, name: 'test', pipeline: child_pipeline) } 3586 3587 it 'returns the build with a given name, having artifacts' do 3588 expect(pipeline.build_with_artifacts_in_self_and_descendants('test')).to eq(child_build) 3589 end 3590 3591 context 'when same job name is present in both parent and child pipeline' do 3592 let!(:build) { create(:ci_build, :artifacts, name: 'test', pipeline: pipeline) } 3593 3594 it 'returns the job in the parent pipeline' do 3595 expect(pipeline.build_with_artifacts_in_self_and_descendants('test')).to eq(build) 3596 end 3597 end 3598 end 3599 3600 describe '#find_job_with_archive_artifacts' do 3601 let(:pipeline) { create(:ci_pipeline) } 3602 let!(:old_job) { create(:ci_build, name: 'rspec', retried: true, pipeline: pipeline) } 3603 let!(:job_without_artifacts) { create(:ci_build, name: 'rspec', pipeline: pipeline) } 3604 let!(:expected_job) { create(:ci_build, :artifacts, name: 'rspec', pipeline: pipeline ) } 3605 let!(:different_job) { create(:ci_build, name: 'deploy', pipeline: pipeline) } 3606 3607 subject { pipeline.find_job_with_archive_artifacts('rspec') } 3608 3609 it 'finds the expected job' do 3610 expect(subject).to eq(expected_job) 3611 end 3612 end 3613 3614 describe '#latest_builds_with_artifacts' do 3615 let(:pipeline) { create(:ci_pipeline) } 3616 let!(:fresh_build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } 3617 let!(:stale_build) { create(:ci_build, :success, :expired, :artifacts, pipeline: pipeline) } 3618 3619 it 'returns an Array' do 3620 expect(pipeline.latest_builds_with_artifacts).to be_an_instance_of(Array) 3621 end 3622 3623 it 'returns the latest builds with non-expired artifacts' do 3624 expect(pipeline.latest_builds_with_artifacts).to contain_exactly(fresh_build) 3625 end 3626 3627 it 'does not return builds with expired artifacts' do 3628 expect(pipeline.latest_builds_with_artifacts).not_to include(stale_build) 3629 end 3630 3631 it 'memoizes the returned relation' do 3632 query_count = ActiveRecord::QueryRecorder 3633 .new { 2.times { pipeline.latest_builds_with_artifacts.to_a } } 3634 .count 3635 3636 expect(query_count).to eq(1) 3637 end 3638 end 3639 3640 describe '#batch_lookup_report_artifact_for_file_type' do 3641 context 'with code quality report artifact' do 3642 let(:pipeline) { create(:ci_pipeline, :with_codequality_reports) } 3643 3644 it "returns the code quality artifact" do 3645 expect(pipeline.batch_lookup_report_artifact_for_file_type(:codequality)).to eq(pipeline.job_artifacts.sample) 3646 end 3647 end 3648 end 3649 3650 describe '#latest_report_builds' do 3651 let_it_be(:pipeline) { create(:ci_pipeline, project: project) } 3652 3653 it 'returns build with test artifacts' do 3654 test_build = create(:ci_build, :test_reports, pipeline: pipeline) 3655 coverage_build = create(:ci_build, :coverage_reports, pipeline: pipeline) 3656 create(:ci_build, :artifacts, pipeline: pipeline, project: project) 3657 3658 expect(pipeline.latest_report_builds).to contain_exactly(test_build, coverage_build) 3659 end 3660 3661 it 'filters builds by scope' do 3662 test_build = create(:ci_build, :test_reports, pipeline: pipeline) 3663 create(:ci_build, :coverage_reports, pipeline: pipeline) 3664 3665 expect(pipeline.latest_report_builds(Ci::JobArtifact.test_reports)).to contain_exactly(test_build) 3666 end 3667 3668 it 'only returns not retried builds' do 3669 test_build = create(:ci_build, :test_reports, pipeline: pipeline) 3670 create(:ci_build, :test_reports, :retried, pipeline: pipeline) 3671 3672 expect(pipeline.latest_report_builds).to contain_exactly(test_build) 3673 end 3674 end 3675 3676 describe '#has_reports?' do 3677 subject { pipeline.has_reports?(Ci::JobArtifact.test_reports) } 3678 3679 context 'when pipeline has builds with test reports' do 3680 before do 3681 create(:ci_build, :test_reports, pipeline: pipeline) 3682 end 3683 3684 context 'when pipeline status is running' do 3685 let(:pipeline) { create(:ci_pipeline, :running) } 3686 3687 it { is_expected.to be_falsey } 3688 end 3689 3690 context 'when pipeline status is success' do 3691 let(:pipeline) { create(:ci_pipeline, :success) } 3692 3693 it { is_expected.to be_truthy } 3694 end 3695 end 3696 3697 context 'when pipeline does not have builds with test reports' do 3698 before do 3699 create(:ci_build, :artifacts, pipeline: pipeline) 3700 end 3701 3702 let(:pipeline) { create(:ci_pipeline, :success) } 3703 3704 it { is_expected.to be_falsey } 3705 end 3706 3707 context 'when retried build has test reports' do 3708 before do 3709 create(:ci_build, :retried, :test_reports, pipeline: pipeline) 3710 end 3711 3712 let(:pipeline) { create(:ci_pipeline, :success) } 3713 3714 it { is_expected.to be_falsey } 3715 end 3716 end 3717 3718 describe '#has_coverage_reports?' do 3719 subject { pipeline.has_coverage_reports? } 3720 3721 context 'when pipeline has a code coverage artifact' do 3722 let(:pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, :running) } 3723 3724 it { expect(subject).to be_truthy } 3725 end 3726 3727 context 'when pipeline does not have a code coverage artifact' do 3728 let(:pipeline) { create(:ci_pipeline, :success) } 3729 3730 it { expect(subject).to be_falsey } 3731 end 3732 end 3733 3734 describe '#can_generate_coverage_reports?' do 3735 subject { pipeline.can_generate_coverage_reports? } 3736 3737 context 'when pipeline has builds with coverage reports' do 3738 before do 3739 create(:ci_build, :coverage_reports, pipeline: pipeline) 3740 end 3741 3742 context 'when pipeline status is running' do 3743 let(:pipeline) { create(:ci_pipeline, :running) } 3744 3745 it { expect(subject).to be_falsey } 3746 end 3747 3748 context 'when pipeline status is success' do 3749 let(:pipeline) { create(:ci_pipeline, :success) } 3750 3751 it { expect(subject).to be_truthy } 3752 end 3753 end 3754 3755 context 'when pipeline does not have builds with coverage reports' do 3756 before do 3757 create(:ci_build, :artifacts, pipeline: pipeline) 3758 end 3759 3760 let(:pipeline) { create(:ci_pipeline, :success) } 3761 3762 it { expect(subject).to be_falsey } 3763 end 3764 end 3765 3766 describe '#has_codequality_mr_diff_report?' do 3767 subject { pipeline.has_codequality_mr_diff_report? } 3768 3769 context 'when pipeline has a codequality mr diff report' do 3770 let(:pipeline) { create(:ci_pipeline, :with_codequality_mr_diff_report, :running) } 3771 3772 it { expect(subject).to be_truthy } 3773 end 3774 3775 context 'when pipeline does not have a codequality mr diff report' do 3776 let(:pipeline) { create(:ci_pipeline, :success) } 3777 3778 it { expect(subject).to be_falsey } 3779 end 3780 end 3781 3782 describe '#can_generate_codequality_reports?' do 3783 subject { pipeline.can_generate_codequality_reports? } 3784 3785 context 'when pipeline has builds with codequality reports' do 3786 before do 3787 create(:ci_build, :codequality_reports, pipeline: pipeline) 3788 end 3789 3790 context 'when pipeline status is running' do 3791 let(:pipeline) { create(:ci_pipeline, :running) } 3792 3793 it { expect(subject).to be_falsey } 3794 end 3795 3796 context 'when pipeline status is success' do 3797 let(:pipeline) { create(:ci_pipeline, :success) } 3798 3799 it 'can generate a codequality report' do 3800 expect(subject).to be_truthy 3801 end 3802 end 3803 end 3804 3805 context 'when pipeline does not have builds with codequality reports' do 3806 before do 3807 create(:ci_build, :artifacts, pipeline: pipeline) 3808 end 3809 3810 let(:pipeline) { create(:ci_pipeline, :success) } 3811 3812 it { expect(subject).to be_falsey } 3813 end 3814 end 3815 3816 describe '#test_report_summary' do 3817 subject { pipeline.test_report_summary } 3818 3819 let(:pipeline) { create(:ci_pipeline, :success) } 3820 3821 context 'when pipeline has multiple builds with report results' do 3822 before do 3823 create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline) 3824 create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline) 3825 end 3826 3827 it 'returns test report summary with collected data' do 3828 expect(subject.total).to include(time: 0.84, count: 4, success: 0, failed: 0, skipped: 0, error: 4) 3829 end 3830 end 3831 3832 context 'when pipeline does not have any builds with report results' do 3833 it 'returns empty test report summary' do 3834 expect(subject.total).to include(time: 0, count: 0, success: 0, failed: 0, skipped: 0, error: 0) 3835 end 3836 end 3837 end 3838 3839 describe '#test_reports' do 3840 subject { pipeline.test_reports } 3841 3842 let_it_be(:pipeline) { create(:ci_pipeline) } 3843 3844 context 'when pipeline has multiple builds with test reports' do 3845 let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) } 3846 let!(:build_java) { create(:ci_build, :success, name: 'java', pipeline: pipeline) } 3847 3848 before do 3849 create(:ci_job_artifact, :junit, job: build_rspec) 3850 create(:ci_job_artifact, :junit_with_ant, job: build_java) 3851 end 3852 3853 it 'returns test reports with collected data' do 3854 expect(subject.total_count).to be(7) 3855 expect(subject.success_count).to be(5) 3856 expect(subject.failed_count).to be(2) 3857 end 3858 3859 context 'when builds are retried' do 3860 let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) } 3861 let!(:build_java) { create(:ci_build, :retried, :success, name: 'java', pipeline: pipeline) } 3862 3863 it 'does not take retried builds into account' do 3864 expect(subject.total_count).to be(0) 3865 expect(subject.success_count).to be(0) 3866 expect(subject.failed_count).to be(0) 3867 end 3868 end 3869 end 3870 3871 context 'when pipeline does not have any builds with test reports' do 3872 it 'returns empty test reports' do 3873 expect(subject.total_count).to be(0) 3874 end 3875 end 3876 end 3877 3878 describe '#accessibility_reports' do 3879 subject { pipeline.accessibility_reports } 3880 3881 let_it_be(:pipeline) { create(:ci_pipeline) } 3882 3883 context 'when pipeline has multiple builds with accessibility reports' do 3884 let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) } 3885 let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) } 3886 3887 before do 3888 create(:ci_job_artifact, :accessibility, job: build_rspec) 3889 create(:ci_job_artifact, :accessibility_without_errors, job: build_golang) 3890 end 3891 3892 it 'returns accessibility report with collected data' do 3893 expect(subject.urls.keys).to match_array([ 3894 "https://pa11y.org/", 3895 "https://about.gitlab.com/" 3896 ]) 3897 end 3898 3899 context 'when builds are retried' do 3900 let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) } 3901 let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) } 3902 3903 it 'returns empty urls for accessibility reports' do 3904 expect(subject.urls).to be_empty 3905 end 3906 end 3907 end 3908 3909 context 'when pipeline does not have any builds with accessibility reports' do 3910 it 'returns empty urls for accessibility reports' do 3911 expect(subject.urls).to be_empty 3912 end 3913 end 3914 end 3915 3916 describe '#coverage_reports' do 3917 subject { pipeline.coverage_reports } 3918 3919 let_it_be(:pipeline) { create(:ci_pipeline) } 3920 3921 context 'when pipeline has multiple builds with coverage reports' do 3922 let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) } 3923 let!(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) } 3924 3925 before do 3926 create(:ci_job_artifact, :cobertura, job: build_rspec) 3927 create(:ci_job_artifact, :coverage_gocov_xml, job: build_golang) 3928 end 3929 3930 it 'returns coverage reports with collected data' do 3931 expect(subject.files.keys).to match_array([ 3932 "auth/token.go", 3933 "auth/rpccredentials.go", 3934 "app/controllers/abuse_reports_controller.rb" 3935 ]) 3936 end 3937 3938 it 'does not execute N+1 queries' do 3939 single_build_pipeline = create(:ci_empty_pipeline, :created) 3940 single_rspec = create(:ci_build, :success, name: 'rspec', pipeline: single_build_pipeline) 3941 create(:ci_job_artifact, :cobertura, job: single_rspec, project: project) 3942 3943 control = ActiveRecord::QueryRecorder.new { single_build_pipeline.coverage_reports } 3944 3945 expect { subject }.not_to exceed_query_limit(control) 3946 end 3947 3948 context 'when builds are retried' do 3949 let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) } 3950 let!(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) } 3951 3952 it 'does not take retried builds into account' do 3953 expect(subject.files).to eql({}) 3954 end 3955 end 3956 end 3957 3958 context 'when pipeline does not have any builds with coverage reports' do 3959 it 'returns empty coverage reports' do 3960 expect(subject.files).to eql({}) 3961 end 3962 end 3963 end 3964 3965 describe '#codequality_reports' do 3966 subject(:codequality_reports) { pipeline.codequality_reports } 3967 3968 let_it_be(:pipeline) { create(:ci_pipeline) } 3969 3970 context 'when pipeline has multiple builds with codequality reports' do 3971 let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) } 3972 let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) } 3973 3974 before do 3975 create(:ci_job_artifact, :codequality, job: build_rspec) 3976 create(:ci_job_artifact, :codequality_without_errors, job: build_golang) 3977 end 3978 3979 it 'returns codequality report with collected data' do 3980 expect(codequality_reports.degradations_count).to eq(3) 3981 end 3982 3983 context 'when builds are retried' do 3984 let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) } 3985 let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) } 3986 3987 it 'returns a codequality reports without degradations' do 3988 expect(codequality_reports.degradations).to be_empty 3989 end 3990 end 3991 end 3992 3993 context 'when pipeline does not have any builds with codequality reports' do 3994 it 'returns codequality reports without degradations' do 3995 expect(codequality_reports.degradations).to be_empty 3996 end 3997 end 3998 end 3999 4000 describe '#uses_needs?' do 4001 let_it_be(:pipeline) { create(:ci_pipeline) } 4002 4003 context 'when the scheduling type is `dag`' do 4004 it 'returns true' do 4005 create(:ci_build, pipeline: pipeline, scheduling_type: :dag) 4006 4007 expect(pipeline.uses_needs?).to eq(true) 4008 end 4009 end 4010 4011 context 'when the scheduling type is nil or stage' do 4012 it 'returns false' do 4013 create(:ci_build, pipeline: pipeline, scheduling_type: :stage) 4014 4015 expect(pipeline.uses_needs?).to eq(false) 4016 end 4017 end 4018 end 4019 4020 describe '#total_size' do 4021 let(:pipeline) { create(:ci_pipeline) } 4022 let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) } 4023 let!(:build_job2) { create(:ci_build, pipeline: pipeline, stage_idx: 0) } 4024 let!(:test_job_failed_and_retried) { create(:ci_build, :failed, :retried, pipeline: pipeline, stage_idx: 1) } 4025 let!(:second_test_job) { create(:ci_build, pipeline: pipeline, stage_idx: 1) } 4026 let!(:deploy_job) { create(:ci_build, pipeline: pipeline, stage_idx: 2) } 4027 4028 it 'returns all jobs (including failed and retried)' do 4029 expect(pipeline.total_size).to eq(5) 4030 end 4031 end 4032 4033 describe '#status' do 4034 context 'when transitioning to failed' do 4035 context 'when pipeline has autodevops as source' do 4036 let(:pipeline) { create(:ci_pipeline, :running, :auto_devops_source) } 4037 4038 it 'calls autodevops disable service' do 4039 expect(AutoDevops::DisableWorker).to receive(:perform_async).with(pipeline.id) 4040 4041 pipeline.drop 4042 end 4043 end 4044 4045 context 'when pipeline has other source' do 4046 let(:pipeline) { create(:ci_pipeline, :running, :repository_source) } 4047 4048 it 'does not call auto devops disable service' do 4049 expect(AutoDevops::DisableWorker).not_to receive(:perform_async) 4050 4051 pipeline.drop 4052 end 4053 end 4054 4055 context 'with failure_reason' do 4056 let(:pipeline) { create(:ci_pipeline, :running) } 4057 let(:failure_reason) { 'config_error' } 4058 let(:counter) { Gitlab::Metrics.counter(:gitlab_ci_pipeline_failure_reasons, 'desc') } 4059 4060 it 'increments the counter with the failure_reason' do 4061 expect { pipeline.drop!(failure_reason) }.to change { counter.get(reason: failure_reason) }.by(1) 4062 end 4063 end 4064 end 4065 end 4066 4067 describe '#default_branch?' do 4068 subject { pipeline.default_branch? } 4069 4070 context 'when pipeline ref is the default branch of the project' do 4071 let(:pipeline) do 4072 build(:ci_empty_pipeline, :created, project: project, ref: project.default_branch) 4073 end 4074 4075 it "returns true" do 4076 expect(subject).to be_truthy 4077 end 4078 end 4079 4080 context 'when pipeline ref is not the default branch of the project' do 4081 let(:pipeline) do 4082 build(:ci_empty_pipeline, :created, project: project, ref: 'another_branch') 4083 end 4084 4085 it "returns false" do 4086 expect(subject).to be_falsey 4087 end 4088 end 4089 end 4090 4091 describe '#find_stage_by_name' do 4092 let_it_be(:pipeline) { create(:ci_pipeline) } 4093 4094 let(:stage_name) { 'test' } 4095 4096 let(:stage) do 4097 create(:ci_stage_entity, 4098 pipeline: pipeline, 4099 project: pipeline.project, 4100 name: 'test') 4101 end 4102 4103 before do 4104 create_list(:ci_build, 2, pipeline: pipeline, stage: stage.name) 4105 end 4106 4107 subject { pipeline.find_stage_by_name!(stage_name) } 4108 4109 context 'when stage exists' do 4110 it { is_expected.to eq(stage) } 4111 end 4112 4113 context 'when stage does not exist' do 4114 let(:stage_name) { 'build' } 4115 4116 it 'raises an ActiveRecord exception' do 4117 expect do 4118 subject 4119 end.to raise_exception(ActiveRecord::RecordNotFound) 4120 end 4121 end 4122 end 4123 4124 describe '#full_error_messages' do 4125 subject { pipeline.full_error_messages } 4126 4127 before do 4128 pipeline.valid? 4129 end 4130 4131 context 'when pipeline has errors' do 4132 let(:pipeline) { build(:ci_pipeline, sha: nil, ref: nil) } 4133 4134 it 'returns the full error messages' do 4135 is_expected.to eq("Sha can't be blank and Ref can't be blank") 4136 end 4137 end 4138 4139 context 'when pipeline does not have errors' do 4140 let(:pipeline) { build(:ci_pipeline) } 4141 4142 it 'returns empty string' do 4143 is_expected.to be_empty 4144 end 4145 end 4146 end 4147 4148 describe '#created_successfully?' do 4149 subject { pipeline.created_successfully? } 4150 4151 context 'when pipeline is not persisted' do 4152 let(:pipeline) { build(:ci_pipeline) } 4153 4154 it { is_expected.to be_falsey } 4155 end 4156 4157 context 'when pipeline is persisted' do 4158 context 'when pipeline has failure reasons' do 4159 let(:pipeline) { create(:ci_pipeline, failure_reason: :config_error) } 4160 4161 it { is_expected.to be_falsey } 4162 end 4163 4164 context 'when pipeline has no failure reasons' do 4165 let(:pipeline) { create(:ci_pipeline, failure_reason: nil) } 4166 4167 it { is_expected.to be_truthy } 4168 end 4169 end 4170 end 4171 4172 describe '#parent_pipeline' do 4173 let_it_be_with_reload(:pipeline) { create(:ci_pipeline) } 4174 4175 context 'when pipeline is triggered by a pipeline from the same project' do 4176 let_it_be(:upstream_pipeline) { create(:ci_pipeline) } 4177 let_it_be(:pipeline) { create(:ci_pipeline, child_of: upstream_pipeline) } 4178 4179 it 'returns the parent pipeline' do 4180 expect(pipeline.parent_pipeline).to eq(upstream_pipeline) 4181 end 4182 4183 it 'is child' do 4184 expect(pipeline).to be_child 4185 end 4186 end 4187 4188 context 'when pipeline is triggered by a pipeline from another project' do 4189 let(:pipeline) { create(:ci_pipeline) } 4190 let!(:upstream_pipeline) { create(:ci_pipeline, project: create(:project), upstream_of: pipeline) } 4191 4192 it 'returns nil' do 4193 expect(pipeline.parent_pipeline).to be_nil 4194 end 4195 4196 it 'is not child' do 4197 expect(pipeline).not_to be_child 4198 end 4199 end 4200 4201 context 'when pipeline is not triggered by a pipeline' do 4202 let_it_be(:pipeline) { create(:ci_pipeline) } 4203 4204 it 'returns nil' do 4205 expect(pipeline.parent_pipeline).to be_nil 4206 end 4207 4208 it 'is not child' do 4209 expect(pipeline).not_to be_child 4210 end 4211 end 4212 end 4213 4214 describe '#child_pipelines' do 4215 let_it_be(:project) { create(:project) } 4216 let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) } 4217 4218 context 'when pipeline triggered other pipelines on same project' do 4219 let(:downstream_pipeline) { create(:ci_pipeline, project: pipeline.project) } 4220 4221 before do 4222 create(:ci_sources_pipeline, 4223 source_pipeline: pipeline, 4224 source_project: pipeline.project, 4225 pipeline: downstream_pipeline, 4226 project: pipeline.project) 4227 end 4228 4229 it 'returns the child pipelines' do 4230 expect(pipeline.child_pipelines).to eq [downstream_pipeline] 4231 end 4232 4233 it 'is parent' do 4234 expect(pipeline).to be_parent 4235 end 4236 end 4237 4238 context 'when pipeline triggered other pipelines on another project' do 4239 let(:downstream_pipeline) { create(:ci_pipeline) } 4240 4241 before do 4242 create(:ci_sources_pipeline, 4243 source_pipeline: pipeline, 4244 source_project: pipeline.project, 4245 pipeline: downstream_pipeline, 4246 project: downstream_pipeline.project) 4247 end 4248 4249 it 'returns empty array' do 4250 expect(pipeline.child_pipelines).to be_empty 4251 end 4252 4253 it 'is not parent' do 4254 expect(pipeline).not_to be_parent 4255 end 4256 end 4257 4258 context 'when pipeline did not trigger any pipelines' do 4259 it 'returns empty array' do 4260 expect(pipeline.child_pipelines).to be_empty 4261 end 4262 4263 it 'is not parent' do 4264 expect(pipeline).not_to be_parent 4265 end 4266 end 4267 end 4268 4269 describe 'upstream status interactions' do 4270 let_it_be_with_reload(:pipeline) { create(:ci_pipeline, :created) } 4271 4272 context 'when a pipeline has an upstream status' do 4273 context 'when an upstream status is a bridge' do 4274 let(:bridge) { create(:ci_bridge, status: :pending) } 4275 4276 before do 4277 create(:ci_sources_pipeline, pipeline: pipeline, source_job: bridge) 4278 end 4279 4280 describe '#bridge_triggered?' do 4281 it 'is a pipeline triggered by a bridge' do 4282 expect(pipeline).to be_bridge_triggered 4283 end 4284 end 4285 4286 describe '#source_job' do 4287 it 'has a correct source job' do 4288 expect(pipeline.source_job).to eq bridge 4289 end 4290 end 4291 4292 describe '#source_bridge' do 4293 it 'has a correct bridge source' do 4294 expect(pipeline.source_bridge).to eq bridge 4295 end 4296 end 4297 end 4298 4299 context 'when an upstream status is a build' do 4300 let(:build) { create(:ci_build) } 4301 4302 before do 4303 create(:ci_sources_pipeline, pipeline: pipeline, source_job: build) 4304 end 4305 4306 describe '#bridge_triggered?' do 4307 it 'is a pipeline that has not been triggered by a bridge' do 4308 expect(pipeline).not_to be_bridge_triggered 4309 end 4310 end 4311 4312 describe '#source_job' do 4313 it 'has a correct source job' do 4314 expect(pipeline.source_job).to eq build 4315 end 4316 end 4317 4318 describe '#source_bridge' do 4319 it 'does not have a bridge source' do 4320 expect(pipeline.source_bridge).to be_nil 4321 end 4322 end 4323 end 4324 end 4325 end 4326 4327 describe '#source_ref_path' do 4328 subject { pipeline.source_ref_path } 4329 4330 let(:pipeline) { create(:ci_pipeline, :created) } 4331 4332 context 'when pipeline is for a branch' do 4333 it { is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.source_ref.to_s) } 4334 end 4335 4336 context 'when pipeline is for a merge request' do 4337 let(:merge_request) { create(:merge_request, source_project: project) } 4338 let(:pipeline) { create(:ci_pipeline, project: project, head_pipeline_of: merge_request) } 4339 4340 it { is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.source_ref.to_s) } 4341 end 4342 4343 context 'when pipeline is for a tag' do 4344 let(:pipeline) { create(:ci_pipeline, tag: true) } 4345 4346 it { is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.source_ref.to_s) } 4347 end 4348 end 4349 4350 describe '#builds_with_coverage' do 4351 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4352 4353 it 'returns builds with coverage only' do 4354 rspec = create(:ci_build, name: 'rspec', coverage: 97.1, pipeline: pipeline) 4355 jest = create(:ci_build, name: 'jest', coverage: 94.1, pipeline: pipeline) 4356 karma = create(:ci_build, name: 'karma', coverage: nil, pipeline: pipeline) 4357 4358 builds = pipeline.builds_with_coverage 4359 4360 expect(builds).to include(rspec, jest) 4361 expect(builds).not_to include(karma) 4362 end 4363 4364 it 'returns only latest builds' do 4365 obsolete = create(:ci_build, name: "jest", coverage: 10.12, pipeline: pipeline, retried: true) 4366 retried = create(:ci_build, name: "jest", coverage: 20.11, pipeline: pipeline) 4367 4368 builds = pipeline.builds_with_coverage 4369 4370 expect(builds).to include(retried) 4371 expect(builds).not_to include(obsolete) 4372 end 4373 end 4374 4375 describe '#self_and_upstreams' do 4376 subject(:self_and_upstreams) { pipeline.self_and_upstreams } 4377 4378 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4379 4380 context 'when pipeline is not child nor parent' do 4381 it 'returns just the pipeline itself' do 4382 expect(self_and_upstreams).to contain_exactly(pipeline) 4383 end 4384 end 4385 4386 context 'when pipeline is child' do 4387 let(:parent) { create(:ci_pipeline) } 4388 let(:sibling) { create(:ci_pipeline) } 4389 4390 before do 4391 create_source_pipeline(parent, pipeline) 4392 create_source_pipeline(parent, sibling) 4393 end 4394 4395 it 'returns parent and self' do 4396 expect(self_and_upstreams).to contain_exactly(parent, pipeline) 4397 end 4398 end 4399 4400 context 'when pipeline is parent' do 4401 let(:child) { create(:ci_pipeline) } 4402 4403 before do 4404 create_source_pipeline(pipeline, child) 4405 end 4406 4407 it 'returns self' do 4408 expect(self_and_upstreams).to contain_exactly(pipeline) 4409 end 4410 end 4411 4412 context 'when pipeline is a child of a child pipeline' do 4413 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4414 4415 let(:ancestor) { create(:ci_pipeline) } 4416 let(:parent) { create(:ci_pipeline) } 4417 4418 before do 4419 create_source_pipeline(ancestor, parent) 4420 create_source_pipeline(parent, pipeline) 4421 end 4422 4423 it 'returns self, parent and ancestor' do 4424 expect(self_and_upstreams).to contain_exactly(ancestor, parent, pipeline) 4425 end 4426 end 4427 4428 context 'when pipeline is a triggered pipeline from a different project' do 4429 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4430 4431 let(:upstream) { create(:ci_pipeline, project: create(:project)) } 4432 4433 before do 4434 create_source_pipeline(upstream, pipeline) 4435 end 4436 4437 it 'returns upstream and self' do 4438 expect(self_and_upstreams).to contain_exactly(pipeline, upstream) 4439 end 4440 end 4441 end 4442 4443 describe '#self_and_ancestors' do 4444 subject(:self_and_ancestors) { pipeline.self_and_ancestors } 4445 4446 context 'when pipeline is child' do 4447 let(:pipeline) { create(:ci_pipeline, :created) } 4448 let(:parent) { create(:ci_pipeline) } 4449 let(:sibling) { create(:ci_pipeline) } 4450 4451 before do 4452 create_source_pipeline(parent, pipeline) 4453 create_source_pipeline(parent, sibling) 4454 end 4455 4456 it 'returns parent and self' do 4457 expect(self_and_ancestors).to contain_exactly(parent, pipeline) 4458 end 4459 end 4460 4461 context 'when pipeline is a triggered pipeline from a different project' do 4462 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4463 4464 let(:upstream) { create(:ci_pipeline, project: create(:project)) } 4465 4466 before do 4467 create_source_pipeline(upstream, pipeline) 4468 end 4469 4470 it 'returns only self' do 4471 expect(self_and_ancestors).to contain_exactly(pipeline) 4472 end 4473 end 4474 end 4475 4476 describe '#reset_source_bridge!' do 4477 let(:pipeline) { create(:ci_pipeline, :created, project: project) } 4478 4479 subject(:reset_bridge) { pipeline.reset_source_bridge!(project.owner) } 4480 4481 context 'when the pipeline is a child pipeline and the bridge is depended' do 4482 let!(:parent_pipeline) { create(:ci_pipeline) } 4483 let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) } 4484 4485 it 'marks source bridge as pending' do 4486 reset_bridge 4487 4488 expect(bridge.reload).to be_pending 4489 end 4490 4491 context 'when the parent pipeline has subsequent jobs after the bridge' do 4492 let!(:after_bridge_job) { create(:ci_build, :skipped, pipeline: parent_pipeline, stage_idx: bridge.stage_idx + 1) } 4493 4494 it 'marks subsequent jobs of the bridge as processable' do 4495 reset_bridge 4496 4497 expect(after_bridge_job.reload).to be_created 4498 end 4499 end 4500 4501 context 'when the parent pipeline has a dependent upstream pipeline' do 4502 let!(:upstream_bridge) do 4503 create_bridge(create(:ci_pipeline, project: create(:project)), parent_pipeline, true) 4504 end 4505 4506 it 'marks all source bridges as pending' do 4507 reset_bridge 4508 4509 expect(bridge.reload).to be_pending 4510 expect(upstream_bridge.reload).to be_pending 4511 end 4512 end 4513 end 4514 4515 context 'when the pipeline is a child pipeline and the bridge is not depended' do 4516 let!(:parent_pipeline) { create(:ci_pipeline) } 4517 let!(:bridge) { create_bridge(parent_pipeline, pipeline, false) } 4518 4519 it 'does not touch source bridge' do 4520 reset_bridge 4521 4522 expect(bridge.reload).to be_success 4523 end 4524 4525 context 'when the parent pipeline has a dependent upstream pipeline' do 4526 let!(:upstream_bridge) do 4527 create_bridge(create(:ci_pipeline, project: create(:project)), parent_pipeline, true) 4528 end 4529 4530 it 'does not touch any source bridge' do 4531 reset_bridge 4532 4533 expect(bridge.reload).to be_success 4534 expect(upstream_bridge.reload).to be_success 4535 end 4536 end 4537 end 4538 4539 private 4540 4541 def create_bridge(upstream, downstream, depend = false) 4542 options = depend ? { trigger: { strategy: 'depend' } } : {} 4543 4544 bridge = create(:ci_bridge, pipeline: upstream, status: 'success', options: options) 4545 create(:ci_sources_pipeline, pipeline: downstream, source_job: bridge) 4546 4547 bridge 4548 end 4549 end 4550 4551 describe 'test failure history processing' do 4552 let(:pipeline) { build(:ci_pipeline, :created) } 4553 4554 it 'performs the service asynchronously when the pipeline is completed' do 4555 service = double 4556 4557 expect(Ci::TestFailureHistoryService).to receive(:new).with(pipeline).and_return(service) 4558 expect(service).to receive_message_chain(:async, :perform_if_needed) 4559 4560 pipeline.succeed! 4561 end 4562 end 4563 4564 describe '#latest_test_report_builds' do 4565 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4566 4567 it 'returns pipeline builds with test report artifacts' do 4568 test_build = create(:ci_build, :test_reports, pipeline: pipeline) 4569 create(:ci_build, :artifacts, pipeline: pipeline, project: project) 4570 4571 expect(pipeline.latest_test_report_builds).to contain_exactly(test_build) 4572 end 4573 4574 it 'preloads project on each build to avoid N+1 queries' do 4575 create(:ci_build, :test_reports, pipeline: pipeline) 4576 4577 control_count = ActiveRecord::QueryRecorder.new do 4578 pipeline.latest_test_report_builds.map(&:project).map(&:full_path) 4579 end 4580 4581 multi_build_pipeline = create(:ci_empty_pipeline, :created) 4582 create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project) 4583 create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project) 4584 4585 expect { multi_build_pipeline.latest_test_report_builds.map(&:project).map(&:full_path) } 4586 .not_to exceed_query_limit(control_count) 4587 end 4588 end 4589 4590 describe '#builds_with_failed_tests' do 4591 let_it_be(:pipeline) { create(:ci_pipeline, :created) } 4592 4593 it 'returns pipeline builds with test report artifacts' do 4594 failed_build = create(:ci_build, :failed, :test_reports, pipeline: pipeline) 4595 create(:ci_build, :success, :test_reports, pipeline: pipeline) 4596 4597 expect(pipeline.builds_with_failed_tests).to contain_exactly(failed_build) 4598 end 4599 4600 it 'supports limiting the number of builds to fetch' do 4601 create(:ci_build, :failed, :test_reports, pipeline: pipeline) 4602 create(:ci_build, :failed, :test_reports, pipeline: pipeline) 4603 4604 expect(pipeline.builds_with_failed_tests(limit: 1).count).to eq(1) 4605 end 4606 4607 it 'preloads project on each build to avoid N+1 queries' do 4608 create(:ci_build, :failed, :test_reports, pipeline: pipeline) 4609 4610 control_count = ActiveRecord::QueryRecorder.new do 4611 pipeline.builds_with_failed_tests.map(&:project).map(&:full_path) 4612 end 4613 4614 multi_build_pipeline = create(:ci_empty_pipeline, :created) 4615 create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline) 4616 create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline) 4617 4618 expect { multi_build_pipeline.builds_with_failed_tests.map(&:project).map(&:full_path) } 4619 .not_to exceed_query_limit(control_count) 4620 end 4621 end 4622 4623 describe '#build_matchers' do 4624 let_it_be(:user) { create(:user) } 4625 let_it_be(:pipeline) { create(:ci_pipeline, user: user) } 4626 let_it_be(:builds) { create_list(:ci_build, 2, pipeline: pipeline, project: pipeline.project, user: user) } 4627 4628 let(:project) { pipeline.project } 4629 4630 subject(:matchers) { pipeline.build_matchers } 4631 4632 it 'returns build matchers' do 4633 expect(matchers.size).to eq(1) 4634 expect(matchers).to all be_a(Gitlab::Ci::Matching::BuildMatcher) 4635 expect(matchers.first.build_ids).to match_array(builds.map(&:id)) 4636 end 4637 4638 context 'with retried builds' do 4639 let(:retried_build) { builds.first } 4640 4641 before do 4642 stub_not_protect_default_branch 4643 project.add_developer(user) 4644 4645 retried_build.cancel! 4646 ::Ci::Build.retry(retried_build, user) 4647 end 4648 4649 it 'does not include retried builds' do 4650 expect(matchers.size).to eq(1) 4651 expect(matchers.first.build_ids).not_to include(retried_build.id) 4652 end 4653 end 4654 end 4655 4656 describe '#authorized_cluster_agents' do 4657 let(:pipeline) { create(:ci_empty_pipeline, :created) } 4658 let(:agent) { instance_double(Clusters::Agent) } 4659 let(:authorization) { instance_double(Clusters::Agents::GroupAuthorization, agent: agent) } 4660 let(:finder) { double(execute: [authorization]) } 4661 4662 it 'retrieves agent records from the finder and caches the result' do 4663 expect(Clusters::AgentAuthorizationsFinder).to receive(:new).once 4664 .with(pipeline.project) 4665 .and_return(finder) 4666 4667 expect(pipeline.authorized_cluster_agents).to contain_exactly(agent) 4668 expect(pipeline.authorized_cluster_agents).to contain_exactly(agent) # cached 4669 end 4670 end 4671 4672 it_behaves_like 'it has loose foreign keys' do 4673 let(:factory_name) { :ci_pipeline } 4674 end 4675 4676 it_behaves_like 'cleanup by a loose foreign key' do 4677 let!(:model) { create(:ci_pipeline, user: create(:user)) } 4678 let!(:parent) { model.user } 4679 end 4680end 4681