diff options
Diffstat (limited to 'spec/models/ci/build_spec.rb')
-rw-r--r-- | spec/models/ci/build_spec.rb | 504 |
1 files changed, 336 insertions, 168 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index dd1fbd7d0d5..2b3dc97e06d 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2,16 +2,16 @@ require 'spec_helper' -RSpec.describe Ci::Build, feature_category: :continuous_integration do +RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_default: :keep do include Ci::TemplateHelpers include AfterNextHelpers let_it_be(:user) { create(:user) } - let_it_be(:group, reload: true) { create(:group) } - let_it_be(:project, reload: true) { create(:project, :repository, group: group) } + let_it_be(:group, reload: true) { create_default(:group) } + let_it_be(:project, reload: true) { create_default(:project, :repository, group: group) } let_it_be(:pipeline, reload: true) do - create(:ci_pipeline, project: project, + create_default(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch, status: 'success') @@ -23,17 +23,20 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do it { is_expected.to belong_to(:trigger_request) } it { is_expected.to belong_to(:erased_by) } - it { is_expected.to have_many(:needs) } - it { is_expected.to have_many(:sourced_pipelines) } - it { is_expected.to have_one(:sourced_pipeline) } - it { is_expected.to have_many(:job_variables) } - it { is_expected.to have_many(:report_results) } - it { is_expected.to have_many(:pages_deployments) } + it { is_expected.to have_many(:needs).with_foreign_key(:build_id) } + it { is_expected.to have_many(:sourced_pipelines).with_foreign_key(:source_job_id) } + it { is_expected.to have_one(:sourced_pipeline).with_foreign_key(:source_job_id) } + it { is_expected.to have_many(:job_variables).with_foreign_key(:job_id) } + it { is_expected.to have_many(:report_results).with_foreign_key(:build_id) } + it { is_expected.to have_many(:pages_deployments).with_foreign_key(:ci_build_id) } it { is_expected.to have_one(:deployment) } - it { is_expected.to have_one(:runner_session) } - it { is_expected.to have_one(:trace_metadata) } - it { is_expected.to have_many(:terraform_state_versions).inverse_of(:build) } + it { is_expected.to have_one(:runner_machine).through(:metadata) } + it { is_expected.to have_one(:runner_session).with_foreign_key(:build_id) } + it { is_expected.to have_one(:trace_metadata).with_foreign_key(:build_id) } + it { is_expected.to have_one(:runtime_metadata).with_foreign_key(:build_id) } + it { is_expected.to have_one(:pending_state).with_foreign_key(:build_id) } + it { is_expected.to have_many(:terraform_state_versions).inverse_of(:build).with_foreign_key(:ci_build_id) } it { is_expected.to validate_presence_of(:ref) } @@ -66,7 +69,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do it 'executes hooks' do expect_next(described_class).to receive(:execute_hooks) - create(:ci_build) + create(:ci_build, pipeline: pipeline) end end end @@ -105,19 +108,19 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { described_class.ref_protected } context 'when protected is true' do - let!(:job) { create(:ci_build, :protected) } + let!(:job) { create(:ci_build, :protected, pipeline: pipeline) } it { is_expected.to include(job) } end context 'when protected is false' do - let!(:job) { create(:ci_build) } + let!(:job) { create(:ci_build, pipeline: pipeline) } it { is_expected.not_to include(job) } end context 'when protected is nil' do - let!(:job) { create(:ci_build) } + let!(:job) { create(:ci_build, pipeline: pipeline) } before do job.update_attribute(:protected, nil) @@ -131,7 +134,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { described_class.with_downloadable_artifacts } context 'when job does not have a downloadable artifact' do - let!(:job) { create(:ci_build) } + let!(:job) { create(:ci_build, pipeline: pipeline) } it 'does not return the job' do is_expected.not_to include(job) @@ -141,7 +144,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do ::Ci::JobArtifact::DOWNLOADABLE_TYPES.each do |type| context "when job has a #{type} artifact" do it 'returns the job' do - job = create(:ci_build) + job = create(:ci_build, pipeline: pipeline) create( :ci_job_artifact, file_format: ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[type.to_sym], @@ -155,7 +158,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when job has a non-downloadable artifact' do - let!(:job) { create(:ci_build, :trace_artifact) } + let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) } it 'does not return the job' do is_expected.not_to include(job) @@ -167,7 +170,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { described_class.with_erasable_artifacts } context 'when job does not have any artifacts' do - let!(:job) { create(:ci_build) } + let!(:job) { create(:ci_build, pipeline: pipeline) } it 'does not return the job' do is_expected.not_to include(job) @@ -177,7 +180,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do ::Ci::JobArtifact.erasable_file_types.each do |type| context "when job has a #{type} artifact" do it 'returns the job' do - job = create(:ci_build) + job = create(:ci_build, pipeline: pipeline) create( :ci_job_artifact, file_format: ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[type.to_sym], @@ -191,7 +194,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when job has a non-erasable artifact' do - let!(:job) { create(:ci_build, :trace_artifact) } + let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) } it 'does not return the job' do is_expected.not_to include(job) @@ -199,11 +202,39 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end end + describe '.with_any_artifacts' do + subject { described_class.with_any_artifacts } + + context 'when job does not have any artifacts' do + it 'does not return the job' do + job = create(:ci_build, project: project) + + is_expected.not_to include(job) + end + end + + ::Ci::JobArtifact.file_types.each_key do |type| + context "when job has a #{type} artifact" do + it 'returns the job' do + job = create(:ci_build, project: project) + create( + :ci_job_artifact, + file_format: ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[type.to_sym], + file_type: type, + job: job + ) + + is_expected.to include(job) + end + end + end + end + describe '.with_live_trace' do subject { described_class.with_live_trace } context 'when build has live trace' do - let!(:build) { create(:ci_build, :success, :trace_live) } + let!(:build) { create(:ci_build, :success, :trace_live, pipeline: pipeline) } it 'selects the build' do is_expected.to eq([build]) @@ -211,7 +242,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build does not have live trace' do - let!(:build) { create(:ci_build, :success, :trace_artifact) } + let!(:build) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) } it 'does not select the build' do is_expected.to be_empty @@ -223,7 +254,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { described_class.with_stale_live_trace } context 'when build has a stale live trace' do - let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 1.day.ago) } + let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 1.day.ago, pipeline: pipeline) } it 'selects the build' do is_expected.to eq([build]) @@ -231,7 +262,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build does not have a stale live trace' do - let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 1.hour.ago) } + let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 1.hour.ago, pipeline: pipeline) } it 'does not select the build' do is_expected.to be_empty @@ -242,9 +273,9 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '.license_management_jobs' do subject { described_class.license_management_jobs } - let!(:management_build) { create(:ci_build, :success, name: :license_management) } - let!(:scanning_build) { create(:ci_build, :success, name: :license_scanning) } - let!(:another_build) { create(:ci_build, :success, name: :another_type) } + let!(:management_build) { create(:ci_build, :success, name: :license_management, pipeline: pipeline) } + let!(:scanning_build) { create(:ci_build, :success, name: :license_scanning, pipeline: pipeline) } + let!(:another_build) { create(:ci_build, :success, name: :another_type, pipeline: pipeline) } it 'returns license_scanning jobs' do is_expected.to include(scanning_build) @@ -265,7 +296,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do let(:date) { 1.hour.ago } context 'when build has finished one day ago' do - let!(:build) { create(:ci_build, :success, finished_at: 1.day.ago) } + let!(:build) { create(:ci_build, :success, finished_at: 1.day.ago, pipeline: pipeline) } it 'selects the build' do is_expected.to eq([build]) @@ -273,7 +304,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build has finished 30 minutes ago' do - let!(:build) { create(:ci_build, :success, finished_at: 30.minutes.ago) } + let!(:build) { create(:ci_build, :success, finished_at: 30.minutes.ago, pipeline: pipeline) } it 'returns an empty array' do is_expected.to be_empty @@ -281,7 +312,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is still running' do - let!(:build) { create(:ci_build, :running) } + let!(:build) { create(:ci_build, :running, pipeline: pipeline) } it 'returns an empty array' do is_expected.to be_empty @@ -292,9 +323,9 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '.with_exposed_artifacts' do subject { described_class.with_exposed_artifacts } - let!(:job1) { create(:ci_build) } - let!(:job2) { create(:ci_build, options: options) } - let!(:job3) { create(:ci_build) } + let!(:job1) { create(:ci_build, pipeline: pipeline) } + let!(:job2) { create(:ci_build, options: options, pipeline: pipeline) } + let!(:job3) { create(:ci_build, pipeline: pipeline) } context 'when some jobs have exposed artifacs and some not' do let(:options) { { artifacts: { expose_as: 'test', paths: ['test'] } } } @@ -334,7 +365,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when there are multiple builds containing artifacts' do before do - create_list(:ci_build, 5, :success, :test_reports) + create_list(:ci_build, 5, :success, :test_reports, pipeline: pipeline) end it 'does not execute a query for selecting job artifact one by one' do @@ -350,8 +381,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '.with_needs' do - let!(:build) { create(:ci_build) } - let!(:build_b) { create(:ci_build) } + let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:build_b) { create(:ci_build, pipeline: pipeline) } let!(:build_need_a) { create(:ci_build_need, build: build) } let!(:build_need_b) { create(:ci_build_need, build: build_b) } @@ -390,7 +421,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#stick_build_if_status_changed' do it 'sticks the build if the status changed' do - job = create(:ci_build, :pending) + job = create(:ci_build, :pending, pipeline: pipeline) expect(described_class.sticking).to receive(:stick) .with(:build, job.id) @@ -400,7 +431,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#enqueue' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } before do allow(build).to receive(:any_unmet_prerequisites?).and_return(has_prerequisites) @@ -477,7 +508,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#enqueue_preparing' do - let(:build) { create(:ci_build, :preparing) } + let(:build) { create(:ci_build, :preparing, pipeline: pipeline) } before do allow(build).to receive(:any_unmet_prerequisites?).and_return(has_unmet_prerequisites) @@ -532,7 +563,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#run' do context 'when build has been just created' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } it 'creates queuing entry and then removes it' do build.enqueue! @@ -544,7 +575,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build status transition fails' do - let(:build) { create(:ci_build, :pending) } + let(:build) { create(:ci_build, :pending, pipeline: pipeline) } before do create(:ci_pending_build, build: build, project: build.project) @@ -560,7 +591,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build has been picked by a shared runner' do - let(:build) { create(:ci_build, :pending) } + let(:build) { create(:ci_build, :pending, pipeline: pipeline) } it 'creates runtime metadata entry' do build.runner = create(:ci_runner, :instance_type) @@ -574,7 +605,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#drop' do context 'when has a runtime tracking entry' do - let(:build) { create(:ci_build, :pending) } + let(:build) { create(:ci_build, :pending, pipeline: pipeline) } it 'removes runtime tracking entry' do build.runner = create(:ci_runner, :instance_type) @@ -611,10 +642,10 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#outdated_deployment?' do subject { build.outdated_deployment? } - let(:build) { create(:ci_build, :created, :with_deployment, project: project, environment: 'production') } + let(:build) { create(:ci_build, :created, :with_deployment, pipeline: pipeline, environment: 'production') } context 'when build has no environment' do - let(:build) { create(:ci_build, :created, project: project, environment: nil) } + let(:build) { create(:ci_build, :created, pipeline: pipeline, environment: nil) } it { expect(subject).to be_falsey } end @@ -644,7 +675,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is older than the latest deployment but succeeded once' do - let(:build) { create(:ci_build, :success, :with_deployment, project: project, environment: 'production') } + let(:build) { create(:ci_build, :success, :with_deployment, pipeline: pipeline, environment: 'production') } before do allow(build.deployment).to receive(:older_than_last_successful_deployment?).and_return(true) @@ -660,13 +691,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.schedulable? } context 'when build is schedulable' do - let(:build) { create(:ci_build, :created, :schedulable, project: project) } + let(:build) { create(:ci_build, :created, :schedulable, pipeline: pipeline) } it { expect(subject).to be_truthy } end context 'when build is not schedulable' do - let(:build) { create(:ci_build, :created, project: project) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } it { expect(subject).to be_falsy } end @@ -679,7 +710,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do project.add_developer(user) end - let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) } + let(:build) { create(:ci_build, :created, :schedulable, user: user, pipeline: pipeline) } it 'transits to scheduled' do allow(Ci::BuildScheduleWorker).to receive(:perform_at) @@ -740,7 +771,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#options_scheduled_at' do subject { build.options_scheduled_at } - let(:build) { build_stubbed(:ci_build, options: option) } + let(:build) { build_stubbed(:ci_build, options: option, pipeline: pipeline) } context 'when start_in is 1 day' do let(:option) { { start_in: '1 day' } } @@ -878,18 +909,18 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when new artifacts are used' do context 'artifacts archive does not exist' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsy } end context 'artifacts archive exists' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } context 'is expired' do - let(:build) { create(:ci_build, :artifacts, :expired) } + let(:build) { create(:ci_build, :artifacts, :expired, pipeline: pipeline) } it { is_expected.to be_falsy } end @@ -901,36 +932,32 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject(:locked_artifacts) { build.locked_artifacts? } context 'when pipeline is artifacts_locked' do - before do - build.pipeline.artifacts_locked! - end + let(:pipeline) { create(:ci_pipeline, locked: :artifacts_locked) } context 'artifacts archive does not exist' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsy } end context 'artifacts archive exists' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } end end context 'when pipeline is unlocked' do - before do - build.pipeline.unlocked! - end + let(:pipeline) { create(:ci_pipeline, locked: :unlocked) } context 'artifacts archive does not exist' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsy } end context 'artifacts archive exists' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_falsy } end @@ -938,7 +965,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#available_artifacts?' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } subject { build.available_artifacts? } @@ -997,7 +1024,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.browsable_artifacts? } context 'artifacts metadata does exists' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } end @@ -1007,13 +1034,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.artifacts_public? } context 'artifacts with defaults' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } end context 'non public artifacts' do - let(:build) { create(:ci_build, :artifacts, :non_public_artifacts) } + let(:build) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) } it { is_expected.to be_falsey } end @@ -1047,7 +1074,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'artifacts archive is a zip file and metadata exists' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } end @@ -1274,12 +1301,12 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#has_live_trace?' do subject { build.has_live_trace? } - let(:build) { create(:ci_build, :trace_live) } + let(:build) { create(:ci_build, :trace_live, pipeline: pipeline) } it { is_expected.to be_truthy } context 'when build does not have live trace' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsy } end @@ -1288,12 +1315,12 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#has_archived_trace?' do subject { build.has_archived_trace? } - let(:build) { create(:ci_build, :trace_artifact) } + let(:build) { create(:ci_build, :trace_artifact, pipeline: pipeline) } it { is_expected.to be_truthy } context 'when build does not have archived trace' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsy } end @@ -1303,7 +1330,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.has_job_artifacts? } context 'when build has a job artifact' do - let(:build) { create(:ci_build, :artifacts) } + let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } it { is_expected.to be_truthy } end @@ -1313,13 +1340,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.has_test_reports? } context 'when build has a test report' do - let(:build) { create(:ci_build, :test_reports) } + let(:build) { create(:ci_build, :test_reports, pipeline: pipeline) } it { is_expected.to be_truthy } end context 'when build does not have a test report' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_falsey } end @@ -1392,7 +1419,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end with_them do - let(:build) { create(:ci_build, trait, project: project, pipeline: pipeline) } + let(:build) { create(:ci_build, trait, pipeline: pipeline) } let(:event) { state } context "when transitioning to #{params[:state]}" do @@ -1416,7 +1443,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe 'state transition as a deployable' do subject { build.send(event) } - let!(:build) { create(:ci_build, :with_deployment, :start_review_app, project: project, pipeline: pipeline) } + let!(:build) { create(:ci_build, :with_deployment, :start_review_app, pipeline: pipeline) } let(:deployment) { build.deployment } let(:environment) { deployment.environment } @@ -1565,7 +1592,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do it 'transitions to running and calls webhook' do freeze_time do expect(Deployments::HooksWorker) - .to receive(:perform_async).with(deployment_id: deployment.id, status: 'running', status_changed_at: Time.current) + .to receive(:perform_async).with(hash_including({ 'deployment_id' => deployment.id, 'status' => 'running', 'status_changed_at' => Time.current.to_s })) subject end @@ -1580,7 +1607,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.on_stop } context 'when a job has a specification that it can be stopped from the other job' do - let(:build) { create(:ci_build, :start_review_app) } + let(:build) { create(:ci_build, :start_review_app, pipeline: pipeline) } it 'returns the other job name' do is_expected.to eq('stop_review_app') @@ -1588,7 +1615,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when a job does not have environment information' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it 'returns nil' do is_expected.to be_nil @@ -1663,7 +1690,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do let(:build) do create(:ci_build, ref: 'master', - environment: 'review/$CI_COMMIT_REF_NAME') + environment: 'review/$CI_COMMIT_REF_NAME', + pipeline: pipeline) end it { is_expected.to eq('review/master') } @@ -1673,7 +1701,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do let(:build) do create(:ci_build, yaml_variables: [{ key: :APP_HOST, value: 'host' }], - environment: 'review/$APP_HOST') + environment: 'review/$APP_HOST', + pipeline: pipeline) end it 'returns an expanded environment name with a list of variables' do @@ -1695,7 +1724,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when using persisted variables' do let(:build) do - create(:ci_build, environment: 'review/x$CI_BUILD_ID') + create(:ci_build, environment: 'review/x$CI_BUILD_ID', pipeline: pipeline) end it { is_expected.to eq('review/x') } @@ -1712,7 +1741,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do create(:ci_build, ref: 'master', yaml_variables: yaml_variables, - environment: 'review/$ENVIRONMENT_NAME') + environment: 'review/$ENVIRONMENT_NAME', + pipeline: pipeline) end it { is_expected.to eq('review/master') } @@ -1720,7 +1750,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#expanded_kubernetes_namespace' do - let(:build) { create(:ci_build, environment: environment, options: options) } + let(:build) { create(:ci_build, environment: environment, options: options, pipeline: pipeline) } subject { build.expanded_kubernetes_namespace } @@ -1856,7 +1886,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'build is not erasable' do - let!(:build) { create(:ci_build) } + let!(:build) { create(:ci_build, pipeline: pipeline) } describe '#erasable?' do subject { build.erasable? } @@ -1867,7 +1897,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'build is erasable' do context 'new artifacts' do - let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) } + let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts, pipeline: pipeline) } describe '#erasable?' do subject { build.erasable? } @@ -1876,7 +1906,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#erased?' do - let!(:build) { create(:ci_build, :trace_artifact, :success, :artifacts) } + let!(:build) { create(:ci_build, :trace_artifact, :success, :artifacts, pipeline: pipeline) } subject { build.erased? } @@ -1970,13 +2000,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is created' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } it { is_expected.to be_cancelable } end context 'when build is waiting for resource' do - let(:build) { create(:ci_build, :waiting_for_resource) } + let(:build) { create(:ci_build, :waiting_for_resource, pipeline: pipeline) } it { is_expected.to be_cancelable } end @@ -2028,8 +2058,18 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end end + describe '#runner_machine' do + let_it_be(:runner) { create(:ci_runner) } + let_it_be(:runner_machine) { create(:ci_runner_machine, runner: runner) } + let_it_be(:build) { create(:ci_build, runner_machine: runner_machine) } + + subject(:build_runner_machine) { described_class.find(build.id).runner_machine } + + it { is_expected.to eq(runner_machine) } + end + describe '#tag_list' do - let_it_be(:build) { create(:ci_build, tag_list: ['tag']) } + let_it_be(:build) { create(:ci_build, tag_list: ['tag'], pipeline: pipeline) } context 'when tags are preloaded' do it 'does not trigger queries' do @@ -2046,7 +2086,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#save_tags' do - let(:build) { create(:ci_build, tag_list: ['tag']) } + let(:build) { create(:ci_build, tag_list: ['tag'], pipeline: pipeline) } it 'saves tags' do build.save! @@ -2075,13 +2115,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#has_tags?' do context 'when build has tags' do - subject { create(:ci_build, tag_list: ['tag']) } + subject { create(:ci_build, tag_list: ['tag'], pipeline: pipeline) } it { is_expected.to have_tags } end context 'when build does not have tags' do - subject { create(:ci_build, tag_list: []) } + subject { create(:ci_build, tag_list: [], pipeline: pipeline) } it { is_expected.not_to have_tags } end @@ -2136,9 +2176,9 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '.keep_artifacts!' do - let!(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } + let!(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days, pipeline: pipeline) } let!(:builds_for_update) do - Ci::Build.where(id: create_list(:ci_build, 3, artifacts_expire_at: Time.current + 7.days).map(&:id)) + Ci::Build.where(id: create_list(:ci_build, 3, artifacts_expire_at: Time.current + 7.days, pipeline: pipeline).map(&:id)) end it 'resets expire_at' do @@ -2180,7 +2220,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#keep_artifacts!' do - let(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } + let(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days, pipeline: pipeline) } subject { build.keep_artifacts! } @@ -2202,7 +2242,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#auto_retry_expected?' do - subject { create(:ci_build, :failed) } + subject { create(:ci_build, :failed, pipeline: pipeline) } context 'when build is failed and auto retry is configured' do before do @@ -2223,20 +2263,20 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end end - describe '#artifacts_file_for_type' do - let(:build) { create(:ci_build, :artifacts) } + describe '#artifact_for_type' do + let(:build) { create(:ci_build) } + let!(:archive) { create(:ci_job_artifact, :archive, job: build) } + let!(:codequality) { create(:ci_job_artifact, :codequality, job: build) } let(:file_type) { :archive } - subject { build.artifacts_file_for_type(file_type) } - - it 'queries artifacts for type' do - expect(build).to receive_message_chain(:job_artifacts, :find_by).with(file_type: [Ci::JobArtifact.file_types[file_type]]) + subject { build.artifact_for_type(file_type) } - subject - end + it { is_expected.to eq(archive) } end describe '#merge_request' do + let_it_be(:merge_request) { create(:merge_request, source_project: project) } + subject { pipeline.builds.take.merge_request } context 'on a branch pipeline' do @@ -2281,19 +2321,23 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'on a detached merged request pipeline' do - let(:pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, :with_job) } + let(:pipeline) do + create(:ci_pipeline, :detached_merge_request_pipeline, :with_job, merge_request: merge_request) + end it { is_expected.to eq(pipeline.merge_request) } end context 'on a legacy detached merged request pipeline' do - let(:pipeline) { create(:ci_pipeline, :legacy_detached_merge_request_pipeline, :with_job) } + let(:pipeline) do + create(:ci_pipeline, :legacy_detached_merge_request_pipeline, :with_job, merge_request: merge_request) + end it { is_expected.to eq(pipeline.merge_request) } end context 'on a pipeline for merged results' do - let(:pipeline) { create(:ci_pipeline, :merged_result_pipeline, :with_job) } + let(:pipeline) { create(:ci_pipeline, :merged_result_pipeline, :with_job, merge_request: merge_request) } it { is_expected.to eq(pipeline.merge_request) } end @@ -2329,7 +2373,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when options include artifacts:expose_as' do - let(:build) { create(:ci_build, options: { artifacts: { expose_as: 'test' } }) } + let(:build) { create(:ci_build, options: { artifacts: { expose_as: 'test' } }, pipeline: pipeline) } it 'saves the presence of expose_as into build metadata' do expect(build.metadata).to have_exposed_artifacts @@ -2455,56 +2499,56 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#playable?' do context 'when build is a manual action' do context 'when build has been skipped' do - subject { build_stubbed(:ci_build, :manual, status: :skipped) } + subject { build_stubbed(:ci_build, :manual, status: :skipped, pipeline: pipeline) } it { is_expected.not_to be_playable } end context 'when build has been canceled' do - subject { build_stubbed(:ci_build, :manual, status: :canceled) } + subject { build_stubbed(:ci_build, :manual, status: :canceled, pipeline: pipeline) } it { is_expected.to be_playable } end context 'when build is successful' do - subject { build_stubbed(:ci_build, :manual, status: :success) } + subject { build_stubbed(:ci_build, :manual, status: :success, pipeline: pipeline) } it { is_expected.to be_playable } end context 'when build has failed' do - subject { build_stubbed(:ci_build, :manual, status: :failed) } + subject { build_stubbed(:ci_build, :manual, status: :failed, pipeline: pipeline) } it { is_expected.to be_playable } end context 'when build is a manual untriggered action' do - subject { build_stubbed(:ci_build, :manual, status: :manual) } + subject { build_stubbed(:ci_build, :manual, status: :manual, pipeline: pipeline) } it { is_expected.to be_playable } end context 'when build is a manual and degenerated' do - subject { build_stubbed(:ci_build, :manual, :degenerated, status: :manual) } + subject { build_stubbed(:ci_build, :manual, :degenerated, status: :manual, pipeline: pipeline) } it { is_expected.not_to be_playable } end end context 'when build is scheduled' do - subject { build_stubbed(:ci_build, :scheduled) } + subject { build_stubbed(:ci_build, :scheduled, pipeline: pipeline) } it { is_expected.to be_playable } end context 'when build is not a manual action' do - subject { build_stubbed(:ci_build, :success) } + subject { build_stubbed(:ci_build, :success, pipeline: pipeline) } it { is_expected.not_to be_playable } end context 'when build is waiting for deployment approval' do - subject { build_stubbed(:ci_build, :manual, environment: 'production') } + subject { build_stubbed(:ci_build, :manual, environment: 'production', pipeline: pipeline) } before do create(:deployment, :blocked, deployable: subject) @@ -2601,7 +2645,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do it { is_expected.to be_truthy } - context "and there are specific runner" do + context "and there is a project runner" do let!(:runner) { create(:ci_runner, :project, projects: [build.project], contacted_at: 1.second.ago) } it { is_expected.to be_falsey } @@ -2855,7 +2899,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do before do allow_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder| + pipeline_variables_builder = double( + ::Gitlab::Ci::Variables::Builder::Pipeline, + predefined_variables: [pipeline_pre_var] + ) + allow(builder).to receive(:predefined_variables) { [build_pre_var] } + allow(builder).to receive(:pipeline_variables_builder) { pipeline_variables_builder } end allow(build).to receive(:yaml_variables) { [build_yaml_var] } @@ -2868,9 +2918,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do .to receive(:predefined_variables) { [project_pre_var] } project.variables.create!(key: 'secret', value: 'value') - - allow(build.pipeline) - .to receive(:predefined_variables).and_return([pipeline_pre_var]) end it 'returns variables in order depending on resource hierarchy' do @@ -3754,7 +3801,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#any_unmet_prerequisites?' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } subject { build.any_unmet_prerequisites? } @@ -3841,7 +3888,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'state transition: any => [:preparing]' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } before do allow(build).to receive(:prerequisites).and_return([double]) @@ -3855,7 +3902,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'when the build is waiting for deployment approval' do - let(:build) { create(:ci_build, :manual, environment: 'production') } + let(:build) { create(:ci_build, :manual, environment: 'production', pipeline: pipeline) } before do create(:deployment, :blocked, deployable: build) @@ -3867,7 +3914,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'state transition: any => [:pending]' do - let(:build) { create(:ci_build, :created) } + let(:build) { create(:ci_build, :created, pipeline: pipeline) } it 'queues BuildQueueWorker' do expect(BuildQueueWorker).to receive(:perform_async).with(build.id) @@ -3887,8 +3934,10 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'state transition: pending: :running' do - let(:runner) { create(:ci_runner) } - let(:job) { create(:ci_build, :pending, runner: runner) } + let_it_be_with_reload(:runner) { create(:ci_runner) } + let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) } + + let(:job) { create(:ci_build, :pending, runner: runner, pipeline: pipeline) } before do job.project.update_attribute(:build_timeout, 1800) @@ -3992,7 +4041,9 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when artifacts of depended job has been erased' do - let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) } + let!(:pre_stage_job) do + create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) + end it { expect(job).not_to have_valid_build_dependencies } end @@ -4049,7 +4100,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is configured to be retried' do - subject { create(:ci_build, :running, options: { script: ["ls -al"], retry: 3 }, project: project, user: user) } + subject { create(:ci_build, :running, options: { script: ["ls -al"], retry: 3 }, pipeline: pipeline, user: user) } it 'retries build and assigns the same user to it' do expect_next_instance_of(::Ci::RetryJobService) do |service| @@ -4098,7 +4149,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is not configured to be retried' do - subject { create(:ci_build, :running, project: project, user: user, pipeline: pipeline) } + subject { create(:ci_build, :running, pipeline: pipeline, user: user) } let(:pipeline) do create(:ci_pipeline, @@ -4162,7 +4213,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '.matches_tag_ids' do - let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, pipeline: pipeline, user: user) } let(:tag_ids) { ::ActsAsTaggableOn::Tag.named_any(tag_list).ids } @@ -4210,7 +4261,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '.matches_tags' do - let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, pipeline: pipeline, user: user) } subject { described_class.where(id: build).with_any_tags } @@ -4236,7 +4287,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'pages deployments' do - let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, pipeline: pipeline, user: user) } context 'when job is "pages"' do before do @@ -4562,7 +4613,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#artifacts_metadata_entry' do - let_it_be(:build) { create(:ci_build, project: project) } + let_it_be(:build) { create(:ci_build, pipeline: pipeline) } let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' } @@ -4622,7 +4673,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#publishes_artifacts_reports?' do - let(:build) { create(:ci_build, options: options) } + let(:build) { create(:ci_build, options: options, pipeline: pipeline) } subject { build.publishes_artifacts_reports? } @@ -4650,7 +4701,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#runner_required_feature_names' do - let(:build) { create(:ci_build, options: options) } + let(:build) { create(:ci_build, options: options, pipeline: pipeline) } subject { build.runner_required_feature_names } @@ -4672,7 +4723,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#supported_runner?' do - let_it_be_with_refind(:build) { create(:ci_build) } + let_it_be_with_refind(:build) { create(:ci_build, pipeline: pipeline) } subject { build.supported_runner?(runner_features) } @@ -4780,7 +4831,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is a last deployment' do - let(:build) { create(:ci_build, :success, environment: 'production', pipeline: pipeline, project: project) } + let(:build) { create(:ci_build, :success, environment: 'production', pipeline: pipeline) } let(:environment) { create(:environment, name: 'production', project: build.project) } let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) } @@ -4788,7 +4839,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when there is a newer build with deployment' do - let(:build) { create(:ci_build, :success, environment: 'production', pipeline: pipeline, project: project) } + let(:build) { create(:ci_build, :success, environment: 'production', pipeline: pipeline) } let(:environment) { create(:environment, name: 'production', project: build.project) } let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) } let!(:last_deployment) { create(:deployment, :success, environment: environment, project: environment.project) } @@ -4797,7 +4848,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build with deployment has failed' do - let(:build) { create(:ci_build, :failed, environment: 'production', pipeline: pipeline, project: project) } + let(:build) { create(:ci_build, :failed, environment: 'production', pipeline: pipeline) } let(:environment) { create(:environment, name: 'production', project: build.project) } let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) } @@ -4805,7 +4856,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build with deployment is running' do - let(:build) { create(:ci_build, environment: 'production', pipeline: pipeline, project: project) } + let(:build) { create(:ci_build, environment: 'production', pipeline: pipeline) } let(:environment) { create(:environment, name: 'production', project: build.project) } let!(:deployment) { create(:deployment, :success, environment: environment, project: environment.project, deployable: build) } @@ -4815,13 +4866,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#degenerated?' do context 'when build is degenerated' do - subject { create(:ci_build, :degenerated) } + subject { create(:ci_build, :degenerated, pipeline: pipeline) } it { is_expected.to be_degenerated } end context 'when build is valid' do - subject { create(:ci_build) } + subject { create(:ci_build, pipeline: pipeline) } it { is_expected.not_to be_degenerated } @@ -4836,7 +4887,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe 'degenerate!' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } subject { build.degenerate! } @@ -4856,13 +4907,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#archived?' do context 'when build is degenerated' do - subject { create(:ci_build, :degenerated) } + subject { create(:ci_build, :degenerated, pipeline: pipeline) } it { is_expected.to be_archived } end context 'for old build' do - subject { create(:ci_build, created_at: 1.day.ago) } + subject { create(:ci_build, created_at: 1.day.ago, pipeline: pipeline) } context 'when archive_builds_in is set' do before do @@ -4883,7 +4934,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#read_metadata_attribute' do - let(:build) { create(:ci_build, :degenerated) } + let(:build) { create(:ci_build, :degenerated, pipeline: pipeline) } let(:build_options) { { key: "build" } } let(:metadata_options) { { key: "metadata" } } let(:default_options) { { key: "default" } } @@ -4920,7 +4971,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#write_metadata_attribute' do - let(:build) { create(:ci_build, :degenerated) } + let(:build) { create(:ci_build, :degenerated, pipeline: pipeline) } let(:options) { { key: "new options" } } let(:existing_options) { { key: "existing options" } } @@ -5046,13 +5097,15 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do subject { build.environment_auto_stop_in } context 'when build option has environment auto_stop_in' do - let(:build) { create(:ci_build, options: { environment: { name: 'test', auto_stop_in: '1 day' } }) } + let(:build) do + create(:ci_build, options: { environment: { name: 'test', auto_stop_in: '1 day' } }, pipeline: pipeline) + end it { is_expected.to eq('1 day') } end context 'when build option does not have environment auto_stop_in' do - let(:build) { create(:ci_build) } + let(:build) { create(:ci_build, pipeline: pipeline) } it { is_expected.to be_nil } end @@ -5372,7 +5425,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '.build_matchers' do - let_it_be(:pipeline) { create(:ci_pipeline, :protected) } + let_it_be(:pipeline) { create(:ci_pipeline, :protected, project: project) } subject(:matchers) { pipeline.builds.build_matchers(pipeline.project) } @@ -5421,7 +5474,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#build_matcher' do let_it_be(:build) do - build_stubbed(:ci_build, tag_list: %w[tag1 tag2]) + build_stubbed(:ci_build, tag_list: %w[tag1 tag2], pipeline: pipeline) end subject(:matcher) { build.build_matcher } @@ -5557,7 +5610,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do it 'does not generate cross DB queries when a record is created via FactoryBot' do with_cross_database_modification_prevented do - create(:ci_build) + create(:ci_build, pipeline: pipeline) end end @@ -5585,7 +5638,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end it_behaves_like 'cleanup by a loose foreign key' do - let!(:model) { create(:ci_build, user: create(:user)) } + let!(:model) { create(:ci_build, user: create(:user), pipeline: pipeline) } let!(:parent) { model.user } end @@ -5595,7 +5648,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when given new job variables' do context 'when the cloned build has an action' do it 'applies the new job variables' do - build = create(:ci_build, :actionable) + build = create(:ci_build, :actionable, pipeline: pipeline) create(:ci_job_variable, job: build, key: 'TEST_KEY', value: 'old value') create(:ci_job_variable, job: build, key: 'OLD_KEY', value: 'i will not live for long') @@ -5614,7 +5667,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when the cloned build does not have an action' do it 'applies the old job variables' do - build = create(:ci_build) + build = create(:ci_build, pipeline: pipeline) create(:ci_job_variable, job: build, key: 'TEST_KEY', value: 'old value') new_build = build.clone( @@ -5632,7 +5685,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do context 'when not given new job variables' do it 'applies the old job variables' do - build = create(:ci_build) + build = create(:ci_build, pipeline: pipeline) create(:ci_job_variable, job: build, key: 'TEST_KEY', value: 'old value') new_build = build.clone(current_user: user) @@ -5646,14 +5699,14 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end describe '#test_suite_name' do - let(:build) { create(:ci_build, name: 'test') } + let(:build) { create(:ci_build, name: 'test', pipeline: pipeline) } it 'uses the group name for test suite name' do expect(build.test_suite_name).to eq('test') end context 'when build is part of parallel build' do - let(:build) { create(:ci_build, name: 'build 1/2') } + let(:build) { create(:ci_build, name: 'build 1/2', pipeline: pipeline) } it 'uses the group name for test suite name' do expect(build.test_suite_name).to eq('build') @@ -5661,7 +5714,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do end context 'when build is part of matrix build' do - let!(:matrix_build) { create(:ci_build, :matrix) } + let!(:matrix_build) { create(:ci_build, :matrix, pipeline: pipeline) } it 'uses the job name for the test suite' do expect(matrix_build.test_suite_name).to eq(matrix_build.name) @@ -5672,7 +5725,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe '#runtime_hooks' do let(:build1) do FactoryBot.build(:ci_build, - options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } }) + options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } }, + pipeline: pipeline) end subject(:runtime_hooks) { build1.runtime_hooks } @@ -5687,7 +5741,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe 'partitioning', :ci_partitionable do include Ci::PartitioningHelpers - let(:new_pipeline) { create(:ci_pipeline) } + let(:new_pipeline) { create(:ci_pipeline, project: project) } let(:ci_build) { FactoryBot.build(:ci_build, pipeline: new_pipeline) } before do @@ -5711,7 +5765,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do describe 'assigning token', :ci_partitionable do include Ci::PartitioningHelpers - let(:new_pipeline) { create(:ci_pipeline) } + let(:new_pipeline) { create(:ci_pipeline, project: project) } let(:ci_build) { create(:ci_build, pipeline: new_pipeline) } before do @@ -5741,4 +5795,118 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do expect { build.remove_token! }.not_to change(build, :token) end end + + describe 'metadata partitioning', :ci_partitioning do + let(:pipeline) { create(:ci_pipeline, project: project, partition_id: ci_testing_partition_id) } + + let(:build) do + FactoryBot.build(:ci_build, pipeline: pipeline) + end + + it 'creates the metadata record and assigns its partition' do + # The record is initialized by the factory calling metadatable setters + build.metadata = nil + + expect(build.metadata).to be_nil + + expect(build.save!).to be_truthy + + expect(build.metadata).to be_present + expect(build.metadata).to be_valid + expect(build.metadata.partition_id).to eq(ci_testing_partition_id) + end + end + + describe 'secrets management id_tokens usage data' do + context 'when ID tokens are defined' do + context 'on create' do + let(:ci_build) { FactoryBot.build(:ci_build, user: user, id_tokens: { 'ID_TOKEN_1' => { aud: 'developers' } }) } + + it 'tracks RedisHLL event with user_id' do + expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event) + .with('i_ci_secrets_management_id_tokens_build_created', values: user.id) + + ci_build.save! + end + + it 'tracks Snowplow event with RedisHLL context' do + params = { + category: described_class.to_s, + action: 'create_id_tokens', + namespace: ci_build.namespace, + user: user, + label: 'redis_hll_counters.ci_secrets_management.i_ci_secrets_management_id_tokens_build_created_monthly', + ultimate_namespace_id: ci_build.namespace.root_ancestor.id, + context: [Gitlab::Tracking::ServicePingContext.new( + data_source: :redis_hll, + event: 'i_ci_secrets_management_id_tokens_build_created' + ).to_context.to_json] + } + + ci_build.save! + expect_snowplow_event(**params) + end + end + + context 'on update' do + let_it_be(:ci_build) { create(:ci_build, user: user, id_tokens: { 'ID_TOKEN_1' => { aud: 'developers' } }) } + + it 'does not track RedisHLL event' do + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + + ci_build.success + end + + it 'does not track Snowplow event' do + ci_build.success + + expect_no_snowplow_event + end + end + end + + context 'when ID tokens are not defined' do + let(:ci_build) { FactoryBot.build(:ci_build, user: user) } + + context 'on create' do + it 'does not track RedisHLL event' do + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + + ci_build.save! + end + + it 'does not track Snowplow event' do + ci_build.save! + expect_no_snowplow_event + end + end + end + end + + describe 'job artifact associations' do + Ci::JobArtifact.file_types.each do |type, _| + method = "job_artifacts_#{type}" + + describe "##{method}" do + subject { build.send(method) } + + context "when job has an artifact of type #{type}" do + let!(:artifact) do + create( + :ci_job_artifact, + job: build, + file_type: type, + file_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[type.to_sym] + ) + end + + it { is_expected.to eq(artifact) } + end + + context "when job has no artifact of type #{type}" do + it { is_expected.to be_nil } + end + end + end + end end |