diff options
Diffstat (limited to 'spec/lib')
20 files changed, 657 insertions, 165 deletions
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index ebd6c79077e..fe7a8c84c9e 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -296,7 +296,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do context 'project milestones' do let(:milestone) { create(:milestone, project: project) } - let(:reference) { milestone.to_reference } + let(:reference) { milestone.to_reference(format: :iid) } include_examples 'reference parsing' diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 4f4a27e4c41..af1db2c3455 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -16,20 +16,20 @@ describe Gitlab::Auth do expect(subject::DEFAULT_SCOPES).to eq [:api] end - it 'OPTIONAL_SCOPES contains all non-default scopes' do + it 'optional_scopes contains all non-default scopes' do stub_container_registry_config(enabled: true) - expect(subject::OPTIONAL_SCOPES).to eq %i[read_user read_registry openid] + expect(subject.optional_scopes).to eq %i[read_user read_registry openid] end - context 'REGISTRY_SCOPES' do + context 'registry_scopes' do context 'when registry is disabled' do before do stub_container_registry_config(enabled: false) end it 'is empty' do - expect(subject::REGISTRY_SCOPES).to eq [] + expect(subject.registry_scopes).to eq [] end end @@ -39,7 +39,7 @@ describe Gitlab::Auth do end it 'contains all registry related scopes' do - expect(subject::REGISTRY_SCOPES).to eq %i[read_registry] + expect(subject.registry_scopes).to eq %i[read_registry] end end end diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb index 8772d3d5ada..422f2af7266 100644 --- a/spec/lib/gitlab/backup/manager_spec.rb +++ b/spec/lib/gitlab/backup/manager_spec.rb @@ -26,6 +26,9 @@ describe Backup::Manager do [ '1451606400_2016_01_01_1.2.3_gitlab_backup.tar', '1451520000_2015_12_31_4.5.6_gitlab_backup.tar', + '1451520000_2015_12_31_4.5.6-pre_gitlab_backup.tar', + '1451520000_2015_12_31_4.5.6-rc1_gitlab_backup.tar', + '1451520000_2015_12_31_4.5.6-pre-ee_gitlab_backup.tar', '1451510000_2015_12_30_gitlab_backup.tar', '1450742400_2015_12_22_gitlab_backup.tar', '1449878400_gitlab_backup.tar', @@ -57,6 +60,30 @@ describe Backup::Manager do end end + context 'when no valid file is found' do + let(:files) do + [ + '14516064000_2016_01_01_1.2.3_gitlab_backup.tar', + 'foo_1451520000_2015_12_31_4.5.6_gitlab_backup.tar', + '1451520000_2015_12_31_4.5.6-foo_gitlab_backup.tar' + ] + end + + before do + allow(Gitlab.config.backup).to receive(:keep_time).and_return(1) + + subject.remove_old + end + + it 'removes no files' do + expect(FileUtils).not_to have_received(:rm) + end + + it 'prints a done message' do + expect(progress).to have_received(:puts).with('done. (0 removed)') + end + end + context 'when there are no files older than keep_time' do before do # Set to 30 days @@ -84,16 +111,22 @@ describe Backup::Manager do it 'removes matching files with a human-readable versioned timestamp' do expect(FileUtils).to have_received(:rm).with(files[1]) - end - - it 'removes matching files with a human-readable non-versioned timestamp' do expect(FileUtils).to have_received(:rm).with(files[2]) expect(FileUtils).to have_received(:rm).with(files[3]) end - it 'removes matching files without a human-readable timestamp' do + it 'removes matching files with a human-readable versioned timestamp with tagged EE' do expect(FileUtils).to have_received(:rm).with(files[4]) + end + + it 'removes matching files with a human-readable non-versioned timestamp' do expect(FileUtils).to have_received(:rm).with(files[5]) + expect(FileUtils).to have_received(:rm).with(files[6]) + end + + it 'removes matching files without a human-readable timestamp' do + expect(FileUtils).to have_received(:rm).with(files[7]) + expect(FileUtils).to have_received(:rm).with(files[8]) end it 'does not remove files that are not old enough' do @@ -101,11 +134,11 @@ describe Backup::Manager do end it 'does not remove non-matching files' do - expect(FileUtils).not_to have_received(:rm).with(files[6]) + expect(FileUtils).not_to have_received(:rm).with(files[9]) end it 'prints a done message' do - expect(progress).to have_received(:puts).with('done. (5 removed)') + expect(progress).to have_received(:puts).with('done. (8 removed)') end end @@ -121,14 +154,15 @@ describe Backup::Manager do end it 'removes the remaining expected files' do - expect(FileUtils).to have_received(:rm).with(files[2]) - expect(FileUtils).to have_received(:rm).with(files[3]) expect(FileUtils).to have_received(:rm).with(files[4]) expect(FileUtils).to have_received(:rm).with(files[5]) + expect(FileUtils).to have_received(:rm).with(files[6]) + expect(FileUtils).to have_received(:rm).with(files[7]) + expect(FileUtils).to have_received(:rm).with(files[8]) end it 'sets the correct removed count' do - expect(progress).to have_received(:puts).with('done. (4 removed)') + expect(progress).to have_received(:puts).with('done. (7 removed)') end it 'prints the error from file that could not be removed' do diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index f8c8b83a3ac..2c7ef622c51 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -5,13 +5,13 @@ describe Gitlab::Checks::ForcePush do context "exit code checking", skip_gitaly_mock: true do it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do - allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) + allow_any_instance_of(Gitlab::Git::RevList).to receive(:popen).and_return(['normal output', 0]) expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error end it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do - allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) + allow_any_instance_of(Gitlab::Git::RevList).to receive(:popen).and_return(['error', 1]) expect { described_class.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) end diff --git a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb new file mode 100644 index 00000000000..15eb01eb472 --- /dev/null +++ b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Gitlab::Ci::Build::Policy::Kubernetes do + let(:pipeline) { create(:ci_pipeline, project: project) } + + context 'when kubernetes service is active' do + set(:project) { create(:kubernetes_project) } + + it 'is satisfied by a kubernetes pipeline' do + expect(described_class.new('active')) + .to be_satisfied_by(pipeline) + end + end + + context 'when kubernetes service is inactive' do + set(:project) { create(:project) } + + it 'is not satisfied by a pipeline without kubernetes available' do + expect(described_class.new('active')) + .not_to be_satisfied_by(pipeline) + end + end + + context 'when kubernetes policy is invalid' do + it 'raises an error' do + expect { described_class.new('unknown') } + .to raise_error(described_class::UnknownPolicyError) + end + end +end diff --git a/spec/lib/gitlab/ci/build/policy/refs_spec.rb b/spec/lib/gitlab/ci/build/policy/refs_spec.rb new file mode 100644 index 00000000000..7211187e511 --- /dev/null +++ b/spec/lib/gitlab/ci/build/policy/refs_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe Gitlab::Ci::Build::Policy::Refs do + describe '#satisfied_by?' do + context 'when matching ref' do + let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'master') } + + it 'is satisfied when pipeline branch matches' do + expect(described_class.new(%w[master deploy])) + .to be_satisfied_by(pipeline) + end + + it 'is not satisfied when pipeline branch does not match' do + expect(described_class.new(%w[feature fix])) + .not_to be_satisfied_by(pipeline) + end + end + + context 'when maching tags' do + context 'when pipeline runs for a tag' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'feature', tag: true) + end + + it 'is satisfied when tags matcher is specified' do + expect(described_class.new(%w[master tags])) + .to be_satisfied_by(pipeline) + end + end + + context 'when pipeline is not created for a tag' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'feature', tag: false) + end + + it 'is not satisfied when tag match is specified' do + expect(described_class.new(%w[master tags])) + .not_to be_satisfied_by(pipeline) + end + end + end + + context 'when also matching a path' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'master') + end + + it 'is satisfied when provided patch matches specified one' do + expect(described_class.new(%W[master@#{pipeline.project_full_path}])) + .to be_satisfied_by(pipeline) + end + + it 'is not satisfied when path differs' do + expect(described_class.new(%w[master@some/fork/repository])) + .not_to be_satisfied_by(pipeline) + end + end + + context 'when maching a source' do + let(:pipeline) { build_stubbed(:ci_pipeline, source: :push) } + + it 'is satisifed when provided source keyword matches' do + expect(described_class.new(%w[pushes])) + .to be_satisfied_by(pipeline) + end + + it 'is not satisfied when provided source keyword does not match' do + expect(described_class.new(%w[triggers])) + .not_to be_satisfied_by(pipeline) + end + end + + context 'when matching a ref by a regular expression' do + let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'docs-something') } + + it 'is satisfied when regexp matches pipeline ref' do + expect(described_class.new(['/docs-.*/'])) + .to be_satisfied_by(pipeline) + end + + it 'is not satisfied when regexp does not match pipeline ref' do + expect(described_class.new(['/fix-.*/'])) + .not_to be_satisfied_by(pipeline) + end + end + end +end diff --git a/spec/lib/gitlab/ci/build/policy_spec.rb b/spec/lib/gitlab/ci/build/policy_spec.rb new file mode 100644 index 00000000000..20ee3dd3e89 --- /dev/null +++ b/spec/lib/gitlab/ci/build/policy_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Gitlab::Ci::Build::Policy do + let(:policy) { spy('policy specification') } + + before do + stub_const("#{described_class}::Something", policy) + end + + describe '.fabricate' do + context 'when policy exists' do + it 'fabricates and initializes relevant policy' do + specs = described_class.fabricate(something: 'some value') + + expect(specs).to be_an Array + expect(specs).to be_one + expect(policy).to have_received(:new).with('some value') + end + end + + context 'when some policies are not defined' do + it 'gracefully skips unknown policies' do + expect { described_class.fabricate(unknown: 'first') } + .to raise_error(NameError) + end + end + + context 'when passing a nil value as specs' do + it 'returns an empty array' do + specs = described_class.fabricate(nil) + + expect(specs).to be_an Array + expect(specs).to be_empty + end + end + end +end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 2278230f338..d72f8553f55 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -3,8 +3,7 @@ require 'spec_helper' module Gitlab module Ci describe YamlProcessor, :lib do - subject { described_class.new(config, path) } - let(:path) { 'path' } + subject { described_class.new(config) } describe 'our current .gitlab-ci.yml' do let(:config) { File.read("#{Rails.root}/.gitlab-ci.yml") } @@ -17,7 +16,7 @@ module Gitlab end describe '#build_attributes' do - subject { described_class.new(config, path).build_attributes(:rspec) } + subject { described_class.new(config).build_attributes(:rspec) } describe 'coverage entry' do describe 'code coverage regexp' do @@ -167,8 +166,6 @@ module Gitlab end context 'when kubernetes policy is specified' do - let(:pipeline) { create(:ci_empty_pipeline) } - let(:config) do YAML.dump( spinach: { stage: 'test', script: 'spinach' }, @@ -204,7 +201,7 @@ module Gitlab end end - describe "#builds_for_stage_and_ref" do + describe "#pipeline_stage_builds" do let(:type) { 'test' } it "returns builds if no branch specified" do @@ -213,10 +210,10 @@ module Gitlab rspec: { script: "rspec" } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({ + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -241,9 +238,9 @@ module Gitlab rspec: { script: "rspec", only: ["deploy"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0) end it "does not return builds if only has regexp with another branch" do @@ -252,9 +249,9 @@ module Gitlab rspec: { script: "rspec", only: ["/^deploy$/"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0) end it "returns builds if only has specified this branch" do @@ -263,9 +260,9 @@ module Gitlab rspec: { script: "rspec", only: ["master"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1) end it "returns builds if only has a list of branches including specified" do @@ -274,9 +271,9 @@ module Gitlab rspec: { script: "rspec", type: type, only: %w(master deploy) } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1) end it "returns builds if only has a branches keyword specified" do @@ -285,9 +282,9 @@ module Gitlab rspec: { script: "rspec", type: type, only: ["branches"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1) end it "does not return builds if only has a tags keyword" do @@ -296,9 +293,9 @@ module Gitlab rspec: { script: "rspec", type: type, only: ["tags"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0) end it "returns builds if only has special keywords specified and source matches" do @@ -315,9 +312,9 @@ module Gitlab rspec: { script: "rspec", type: type, only: [possibility[:keyword]] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(1) end end @@ -335,21 +332,27 @@ module Gitlab rspec: { script: "rspec", type: type, only: [possibility[:keyword]] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(0) end end it "returns builds if only has current repository path" do + seed_pipeline = pipeline(ref: 'deploy') + config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["branches@path"] } + rspec: { + script: "rspec", + type: type, + only: ["branches@#{seed_pipeline.project_full_path}"] + } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, seed_pipeline).size).to eq(1) end it "does not return builds if only has different repository path" do @@ -358,9 +361,9 @@ module Gitlab rspec: { script: "rspec", type: type, only: ["branches@fork"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0) end it "returns build only for specified type" do @@ -371,11 +374,11 @@ module Gitlab production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, 'fork') + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) - expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1) + expect(config_processor.pipeline_stage_builds("deploy", pipeline(ref: "deploy")).size).to eq(2) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "deploy")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("deploy", pipeline(ref: "master")).size).to eq(1) end context 'for invalid value' do @@ -418,9 +421,9 @@ module Gitlab rspec: { script: "rspec", except: ["deploy"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1) end it "returns builds if except has regexp with another branch" do @@ -429,9 +432,9 @@ module Gitlab rspec: { script: "rspec", except: ["/^deploy$/"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1) end it "does not return builds if except has specified this branch" do @@ -440,9 +443,9 @@ module Gitlab rspec: { script: "rspec", except: ["master"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0) end it "does not return builds if except has a list of branches including specified" do @@ -451,9 +454,9 @@ module Gitlab rspec: { script: "rspec", type: type, except: %w(master deploy) } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0) end it "does not return builds if except has a branches keyword specified" do @@ -462,9 +465,9 @@ module Gitlab rspec: { script: "rspec", type: type, except: ["branches"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0) end it "returns builds if except has a tags keyword" do @@ -473,9 +476,9 @@ module Gitlab rspec: { script: "rspec", type: type, except: ["tags"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1) end it "does not return builds if except has special keywords specified and source matches" do @@ -492,9 +495,9 @@ module Gitlab rspec: { script: "rspec", type: type, except: [possibility[:keyword]] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(0) end end @@ -512,21 +515,27 @@ module Gitlab rspec: { script: "rspec", type: type, except: [possibility[:keyword]] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(1) end end it "does not return builds if except has current repository path" do + seed_pipeline = pipeline(ref: 'deploy') + config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, except: ["branches@path"] } + rspec: { + script: "rspec", + type: type, + except: ["branches@#{seed_pipeline.project_full_path}"] + } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + expect(config_processor.pipeline_stage_builds(type, seed_pipeline).size).to eq(0) end it "returns builds if except has different repository path" do @@ -535,24 +544,28 @@ module Gitlab rspec: { script: "rspec", type: type, except: ["branches@fork"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1) end it "returns build except specified type" do + master_pipeline = pipeline(ref: 'master') + test_pipeline = pipeline(ref: 'test') + deploy_pipeline = pipeline(ref: 'deploy') + config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] }, + rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@#{test_pipeline.project_full_path}"] }, staging: { script: "deploy", type: "deploy", except: ["master"] }, - production: { script: "deploy", type: "deploy", except: ["master@fork"] } + production: { script: "deploy", type: "deploy", except: ["master@#{master_pipeline.project_full_path}"] } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, 'fork') + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) - expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0) - expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0) + expect(config_processor.pipeline_stage_builds("deploy", deploy_pipeline).size).to eq(2) + expect(config_processor.pipeline_stage_builds("test", test_pipeline).size).to eq(0) + expect(config_processor.pipeline_stage_builds("deploy", master_pipeline).size).to eq(0) end context 'for invalid value' do @@ -591,9 +604,9 @@ module Gitlab describe "Scripts handling" do let(:config_data) { YAML.dump(config) } - let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config_data, path) } + let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config_data) } - subject { config_processor.builds_for_stage_and_ref("test", "master").first } + subject { config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first } describe "before_script" do context "in global context" do @@ -674,10 +687,10 @@ module Gitlab before_script: ["pwd"], rspec: { script: "rspec" } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -709,10 +722,10 @@ module Gitlab command: ["/usr/local/bin/init", "run"] }, "docker:dind"], script: "rspec" } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -742,10 +755,10 @@ module Gitlab before_script: ["pwd"], rspec: { script: "rspec" } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -771,10 +784,10 @@ module Gitlab before_script: ["pwd"], rspec: { image: "ruby:2.5", services: ["postgresql", "docker:dind"], script: "rspec" } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -797,7 +810,7 @@ module Gitlab end describe 'Variables' do - let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config), path) } + let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) } subject { config_processor.builds.first[:yaml_variables] } @@ -918,9 +931,9 @@ module Gitlab rspec: { script: "rspec", when: when_state } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - builds = config_processor.builds_for_stage_and_ref("test", "master") + builds = config_processor.pipeline_stage_builds("test", pipeline(ref: "master")) expect(builds.size).to eq(1) expect(builds.first[:when]).to eq(when_state) end @@ -951,8 +964,8 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq( paths: ["logs/", "binaries/"], untracked: true, key: 'key', @@ -970,8 +983,8 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq( paths: ["logs/", "binaries/"], untracked: true, key: 'key', @@ -990,8 +1003,8 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq( paths: ["test/"], untracked: false, key: 'local', @@ -1019,8 +1032,8 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1) + expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({ stage: "test", stage_idx: 1, name: "rspec", @@ -1055,9 +1068,9 @@ module Gitlab } }) - config_processor = Gitlab::Ci::YamlProcessor.new(config, path) + config_processor = Gitlab::Ci::YamlProcessor.new(config) - builds = config_processor.builds_for_stage_and_ref("test", "master") + builds = config_processor.pipeline_stage_builds("test", pipeline(ref: "master")) expect(builds.size).to eq(1) expect(builds.first[:options][:artifacts][:when]).to eq(when_state) end @@ -1072,7 +1085,7 @@ module Gitlab end let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) } - let(:builds) { processor.builds_for_stage_and_ref('deploy', 'master') } + let(:builds) { processor.pipeline_stage_builds('deploy', pipeline(ref: 'master')) } context 'when a production environment is specified' do let(:environment) { 'production' } @@ -1229,7 +1242,7 @@ module Gitlab describe "Hidden jobs" do let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) } - subject { config_processor.builds_for_stage_and_ref("test", "master") } + subject { config_processor.pipeline_stage_builds("test", pipeline(ref: "master")) } shared_examples 'hidden_job_handling' do it "doesn't create jobs that start with dot" do @@ -1277,7 +1290,7 @@ module Gitlab describe "YAML Alias/Anchor" do let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) } - subject { config_processor.builds_for_stage_and_ref("build", "master") } + subject { config_processor.pipeline_stage_builds("build", pipeline(ref: "master")) } shared_examples 'job_templates_handling' do it "is correctly supported for jobs" do @@ -1377,182 +1390,182 @@ EOT it "returns errors if tags parameter is invalid" do config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec tags should be an array of strings") end it "returns errors if before_script parameter is invalid" do config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "before_script config should be an array of strings") end it "returns errors if job before_script parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array of strings") end it "returns errors if after_script parameter is invalid" do config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "after_script config should be an array of strings") end it "returns errors if job after_script parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array of strings") end it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "image config should be a hash or a string") end it "returns errors if job name is blank" do config = YAML.dump({ '' => { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:job name can't be blank") end it "returns errors if job name is non-string" do config = YAML.dump({ 10 => { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:10 name should be a symbol") end it "returns errors if job image parameter is invalid" do config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:image config should be a hash or a string") end it "returns errors if services parameter is not an array" do config = YAML.dump({ services: "test", rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "services config should be a array") end it "returns errors if services parameter is not an array of strings" do config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "service config should be a hash or a string") end it "returns errors if job services parameter is not an array" do config = YAML.dump({ rspec: { script: "test", services: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:services config should be a array") end it "returns errors if job services parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "service config should be a hash or a string") end it "returns error if job configuration is invalid" do config = YAML.dump({ extra: "bundle update" }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:extra config should be a hash") end it "returns errors if services configuration is not correct" do config = YAML.dump({ extra: { script: 'rspec', services: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:extra:services config should be a array") end it "returns errors if there are no jobs defined" do config = YAML.dump({ before_script: ["bundle update"] }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs config should contain at least one visible job") end it "returns errors if there are no visible jobs defined" do config = YAML.dump({ before_script: ["bundle update"], '.hidden'.to_sym => { script: 'ls' } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs config should contain at least one visible job") end it "returns errors if job allow_failure parameter is not an boolean" do config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec allow failure should be a boolean value") end it "returns errors if job stage is not a string" do config = YAML.dump({ rspec: { script: "test", type: 1 } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:type config should be a string") end it "returns errors if job stage is not a pre-defined stage" do config = YAML.dump({ rspec: { script: "test", type: "acceptance" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") end it "returns errors if stages is not an array" do config = YAML.dump({ stages: "test", rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "stages config should be an array of strings") end it "returns errors if stages is not an array of strings" do config = YAML.dump({ stages: [true, "test"], rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "stages config should be an array of strings") end it "returns errors if variables is not a map" do config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "variables config should be a hash of key value pairs") end it "returns errors if variables is not a map of key-value strings" do config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "variables config should be a hash of key value pairs") end it "returns errors if job when is not on_success, on_failure or always" do config = YAML.dump({ rspec: { script: "test", when: 1 } }) expect do - Gitlab::Ci::YamlProcessor.new(config, path) + Gitlab::Ci::YamlProcessor.new(config) end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always or manual") end @@ -1694,6 +1707,10 @@ EOT end end end + + def pipeline(**attributes) + build_stubbed(:ci_empty_pipeline, **attributes) + end end end end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 15012495247..9e528392756 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -347,10 +347,10 @@ describe Gitlab::ClosingIssueExtractor do end it "fetches cross-project URL references" do - message = "Closes #{urls.project_issue_url(issue2.project, issue2)} and #{reference}" + message = "Closes #{urls.project_issue_url(issue2.project, issue2)}, #{reference} and #{urls.project_issue_url(other_issue.project, other_issue)}" expect(subject.closed_by_message(message)) - .to match_array([issue, issue2]) + .to match_array([issue, issue2, other_issue]) end it "ignores invalid cross-project URL references" do diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index a3d323fe28a..7dc06c90078 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -1,11 +1,14 @@ require 'spec_helper' describe Gitlab::Gfm::ReferenceRewriter do - let(:text) { 'some text' } - let(:old_project) { create(:project, name: 'old-project') } - let(:new_project) { create(:project, name: 'new-project') } + let(:group) { create(:group) } + let(:old_project) { create(:project, name: 'old-project', group: group) } + let(:new_project) { create(:project, name: 'new-project', group: group) } let(:user) { create(:user) } + let(:old_project_ref) { old_project.to_reference(new_project) } + let(:text) { 'some text' } + before do old_project.team << [user, :reporter] end @@ -39,7 +42,7 @@ describe Gitlab::Gfm::ReferenceRewriter do it { is_expected.not_to include merge_request.to_reference(new_project) } end - context 'description ambigous elements' do + context 'rewrite ambigous references' do context 'url' do let(:url) { 'http://gitlab.com/#1' } let(:text) { "This references #1, but not #{url}" } @@ -66,23 +69,21 @@ describe Gitlab::Gfm::ReferenceRewriter do context 'description with project labels' do let!(:label) { create(:label, id: 123, name: 'test', project: old_project) } - let(:project_ref) { old_project.to_reference(new_project) } context 'label referenced by id' do let(:text) { '#1 and ~123' } - it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} } + it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~123} } end context 'label referenced by text' do let(:text) { '#1 and ~"test"' } - it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} } + it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~123} } end end context 'description with group labels' do let(:old_group) { create(:group) } let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) } - let(:project_ref) { old_project.to_reference(new_project) } before do old_project.update(namespace: old_group) @@ -90,21 +91,53 @@ describe Gitlab::Gfm::ReferenceRewriter do context 'label referenced by id' do let(:text) { '#1 and ~321' } - it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~321} } + it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~321} } end context 'label referenced by text' do let(:text) { '#1 and ~"group label"' } - it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~321} } + it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~321} } end end end + end + + context 'reference contains project milestone' do + let!(:milestone) do + create(:milestone, title: '9.0', project: old_project) + end + + let(:text) { 'milestone: %"9.0"' } + + it { is_expected.to eq %Q[milestone: #{old_project_ref}%"9.0"] } + end + + context 'when referring to group milestone' do + let!(:milestone) do + create(:milestone, title: '10.0', group: group) + end + + let(:text) { 'milestone %"10.0"' } + + it { is_expected.to eq text } + end + + context 'when referable has a nil reference' do + before do + create(:milestone, title: '9.0', project: old_project) + + allow_any_instance_of(Milestone) + .to receive(:to_reference) + .and_return(nil) + end - context 'reference contains milestone' do - let(:milestone) { create(:milestone) } - let(:text) { "milestone ref: #{milestone.to_reference}" } + let(:text) { 'milestone: %"9.0"' } - it { is_expected.to eq text } + it 'raises an error that should be fixed' do + expect { subject }.to raise_error( + described_class::RewriteError, + 'Unspecified reference detected for Milestone' + ) end end end diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 66ba00acb7d..f3945e748ab 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -119,10 +119,13 @@ describe Gitlab::Git::Blob, seed_helper: true do shared_examples 'finding blobs by ID' do let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) } + let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) } + it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) } it { expect(raw_blob.data[0..10]).to eq("require \'fi") } it { expect(raw_blob.size).to eq(669) } it { expect(raw_blob.truncated?).to be_falsey } + it { expect(bad_blob).to be_nil } context 'large file' do it 'limits the size of a large file' do diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index ea3e4680b1d..0ff4f3bd105 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -28,6 +28,7 @@ describe Gitlab::Git::Hook do f.write(<<-HOOK) echo 'regular message from the hook' echo 'error message from the hook' 1>&2 + echo 'error message from the hook line 2' 1>&2 exit 1 HOOK end @@ -73,7 +74,7 @@ describe Gitlab::Git::Hook do status, errors = hook.trigger(gl_id, blank, blank, ref) expect(status).to be false - expect(errors).to eq("error message from the hook\n") + expect(errors).to eq("error message from the hook<br>error message from the hook line 2<br>") end end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 556a148c3bc..4fc26c625a5 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -481,7 +481,7 @@ describe Gitlab::Git::Repository, seed_helper: true do end it 'raises an error if it failed' do - expect(Gitlab::Popen).to receive(:popen).and_return(['Error', 1]) + expect(@repo).to receive(:popen).and_return(['Error', 1]) expect do @repo.delete_refs('refs/heads/fix') diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index b051a088171..c0eac98d718 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -14,7 +14,7 @@ describe Gitlab::Git::RevList do let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } it 'calls out to `popen`' do - expect(Gitlab::Popen).to receive(:popen).with([ + expect(rev_list).to receive(:popen).with([ Gitlab.config.git.bin_path, "--git-dir=#{project.repository.path_to_repo}", 'rev-list', @@ -36,7 +36,7 @@ describe Gitlab::Git::RevList do let(:rev_list) { described_class.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } it 'calls out to `popen`' do - expect(Gitlab::Popen).to receive(:popen).with([ + expect(rev_list).to receive(:popen).with([ Gitlab.config.git.bin_path, "--git-dir=#{project.repository.path_to_repo}", 'rev-list', diff --git a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb index c86353abb7c..98cf7966dad 100644 --- a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb +++ b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: true, broken_storage: true do let(:storage_name) { 'default' } - let(:circuit_breaker) { described_class.new(storage_name) } + let(:circuit_breaker) { described_class.new(storage_name, hostname) } let(:hostname) { Gitlab::Environment.hostname } let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" } @@ -22,7 +22,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: 'failure_wait_time' => 30, 'failure_reset_time' => 1800, 'storage_timeout' => 5 - } + }, + 'nopath' => { 'path' => nil } ) end @@ -59,6 +60,14 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: expect(breaker).to be_a(described_class) expect(described_class.for_storage('default')).to eq(breaker) end + + it 'returns a broken circuit breaker for an unknown storage' do + expect(described_class.for_storage('unknown').circuit_broken?).to be_truthy + end + + it 'returns a broken circuit breaker when the path is not set' do + expect(described_class.for_storage('nopath').circuit_broken?).to be_truthy + end end describe '#initialize' do diff --git a/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb new file mode 100644 index 00000000000..0e645008c88 --- /dev/null +++ b/spec/lib/gitlab/git/storage/null_circuit_breaker_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +describe Gitlab::Git::Storage::NullCircuitBreaker do + let(:storage) { 'default' } + let(:hostname) { 'localhost' } + let(:error) { nil } + + subject(:breaker) { described_class.new(storage, hostname, error: error) } + + context 'with an error' do + let(:error) { Gitlab::Git::Storage::Misconfiguration.new('error') } + + describe '#perform' do + it { expect { breaker.perform { 'ok' } }.to raise_error(error) } + end + + describe '#circuit_broken?' do + it { expect(breaker.circuit_broken?).to be_truthy } + end + + describe '#last_failure' do + it { Timecop.freeze { expect(breaker.last_failure).to eq(Time.now) } } + end + + describe '#failure_count' do + it { expect(breaker.failure_count).to eq(breaker.failure_count_threshold) } + end + + describe '#failure_info' do + it { Timecop.freeze { expect(breaker.failure_info).to eq(Gitlab::Git::Storage::CircuitBreaker::FailureInfo.new(Time.now, breaker.failure_count_threshold)) } } + end + end + + context 'not broken' do + describe '#perform' do + it { expect(breaker.perform { 'ok' }).to eq('ok') } + end + + describe '#circuit_broken?' do + it { expect(breaker.circuit_broken?).to be_falsy } + end + + describe '#last_failure' do + it { expect(breaker.last_failure).to be_nil } + end + + describe '#failure_count' do + it { expect(breaker.failure_count).to eq(0) } + end + + describe '#failure_info' do + it { expect(breaker.failure_info).to eq(Gitlab::Git::Storage::CircuitBreaker::FailureInfo.new(nil, 0)) } + end + end + + describe '#failure_count_threshold' do + it { expect(breaker.failure_count_threshold).to eq(1) } + end + + it 'implements the CircuitBreaker interface' do + ours = described_class.public_instance_methods + theirs = Gitlab::Git::Storage::CircuitBreaker.public_instance_methods + + # These methods are not part of the public API, but are public to allow the + # CircuitBreaker specs to operate. They should be made private over time. + exceptions = %i[ + cache_key + check_storage_accessible! + no_failures? + storage_available? + track_storage_accessible + track_storage_inaccessible + ] + + expect(theirs - ours).to contain_exactly(*exceptions) + end +end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index ec3abcb0953..1ef3e2e3a5d 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -51,6 +51,10 @@ describe Gitlab::GitalyClient::CommitService do expect(ret).to be_kind_of(Gitlab::GitalyClient::DiffStitcher) end + + it 'encodes paths correctly' do + expect { client.diff_from_parent(commit, paths: ['encoding/test.txt', 'encoding/ใในใ.txt']) }.not_to raise_error + end end describe '#commit_deltas' do diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index a9b861fcff2..9a84d6e6a67 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -38,6 +38,130 @@ describe Gitlab::GitalyClient, skip_gitaly_mock: true do end end + describe 'allow_n_plus_1_calls' do + context 'when RequestStore is enabled', :request_store do + it 'returns the result of the allow_n_plus_1_calls block' do + expect(described_class.allow_n_plus_1_calls { "result" }).to eq("result") + end + end + + context 'when RequestStore is not active' do + it 'returns the result of the allow_n_plus_1_calls block' do + expect(described_class.allow_n_plus_1_calls { "something" }).to eq("something") + end + end + end + + describe 'enforce_gitaly_request_limits?' do + def call_gitaly(count = 1) + (1..count).each do + described_class.enforce_gitaly_request_limits(:test) + end + end + + context 'when RequestStore is enabled', :request_store do + it 'allows up the maximum number of allowed calls' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error + end + + context 'when the maximum number of calls has been reached' do + before do + call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) + end + + it 'fails on the next call' do + expect { call_gitaly(1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError) + end + end + + it 'allows the maximum number of calls to be exceeded within an allow_n_plus_1_calls block' do + expect do + described_class.allow_n_plus_1_calls do + call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) + end + end.not_to raise_error + end + + context 'when the maximum number of calls has been reached within an allow_n_plus_1_calls block' do + before do + described_class.allow_n_plus_1_calls do + call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) + end + end + + it 'allows up to the maximum number of calls outside of an allow_n_plus_1_calls block' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error + end + + it 'does not allow the maximum number of calls to be exceeded outside of an allow_n_plus_1_calls block' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError) + end + end + end + + context 'when RequestStore is not active' do + it 'does not raise errors when the maximum number of allowed calls is exceeded' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 2) }.not_to raise_error + end + + it 'does not fail when the maximum number of calls is exceeded within an allow_n_plus_1_calls block' do + expect do + described_class.allow_n_plus_1_calls do + call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) + end + end.not_to raise_error + end + end + end + + describe 'get_request_count' do + context 'when RequestStore is enabled', :request_store do + context 'when enforce_gitaly_request_limits is called outside of allow_n_plus_1_calls blocks' do + before do + described_class.enforce_gitaly_request_limits(:call) + end + + it 'counts gitaly calls' do + expect(described_class.get_request_count).to eq(1) + end + end + + context 'when enforce_gitaly_request_limits is called inside and outside of allow_n_plus_1_calls blocks' do + before do + described_class.enforce_gitaly_request_limits(:call) + described_class.allow_n_plus_1_calls do + described_class.enforce_gitaly_request_limits(:call) + end + end + + it 'counts gitaly calls' do + expect(described_class.get_request_count).to eq(2) + end + end + + context 'when reset_counts is called' do + before do + described_class.enforce_gitaly_request_limits(:call) + described_class.reset_counts + end + + it 'resets counts' do + expect(described_class.get_request_count).to eq(0) + end + end + end + + context 'when RequestStore is not active' do + before do + described_class.enforce_gitaly_request_limits(:call) + end + + it 'returns zero' do + expect(described_class.get_request_count).to eq(0) + end + end + end + describe 'feature_enabled?' do let(:feature_name) { 'my_feature' } let(:real_feature_name) { "gitaly_#{feature_name}" } diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb index f5c9680bf59..73dd236a5c6 100644 --- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb +++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do let(:metric_class) { Gitlab::HealthChecks::Metric } let(:result_class) { Gitlab::HealthChecks::Result } - let(:repository_storages) { [:default] } + let(:repository_storages) { ['default'] } let(:tmp_dir) { Dir.mktmpdir } let(:storages_paths) do @@ -64,7 +64,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do allow(described_class).to receive(:storage_circuitbreaker_test) { true } end - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: :default)) } + it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } end context 'storage points to directory that has both read and write rights' do @@ -72,7 +72,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do FileUtils.chmod_R(0755, tmp_dir) end - it { is_expected.to include(result_class.new(true, nil, shard: :default)) } + it { is_expected.to include(result_class.new(true, nil, shard: 'default')) } it 'cleans up files used for testing' do expect(described_class).to receive(:storage_write_test).with(any_args).and_call_original @@ -85,7 +85,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do allow(described_class).to receive(:storage_read_test).with(any_args).and_return(false) end - it { is_expected.to include(result_class.new(false, 'cannot read from storage', shard: :default)) } + it { is_expected.to include(result_class.new(false, 'cannot read from storage', shard: 'default')) } end context 'write test fails' do @@ -93,7 +93,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do allow(described_class).to receive(:storage_write_test).with(any_args).and_return(false) end - it { is_expected.to include(result_class.new(false, 'cannot write to storage', shard: :default)) } + it { is_expected.to include(result_class.new(false, 'cannot write to storage', shard: 'default')) } end end end @@ -109,7 +109,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do it 'provides metrics' do metrics = described_class.metrics - expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to all(have_attributes(labels: { shard: 'default' })) expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) @@ -128,7 +128,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do it 'provides metrics' do metrics = described_class.metrics - expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to all(have_attributes(labels: { shard: 'default' })) expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) @@ -156,14 +156,14 @@ describe Gitlab::HealthChecks::FsShardsCheck do describe '#readiness' do subject { described_class.readiness } - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: :default)) } + it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } end describe '#metrics' do it 'provides metrics' do metrics = described_class.metrics - expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to all(have_attributes(labels: { shard: 'default' })) expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index c7d9f105f04..ee152872acc 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -26,6 +26,16 @@ describe Gitlab::UsageData do version uuid hostname + signup + ldap + gravatar + omniauth + reply_by_email + container_registry + gitlab_pages + gitlab_shared_runners + git + database )) end @@ -86,6 +96,32 @@ describe Gitlab::UsageData do end end + describe '#features_usage_data_ce' do + subject { described_class.features_usage_data_ce } + + it 'gathers feature usage data' do + expect(subject[:signup]).to eq(current_application_settings.signup_enabled?) + expect(subject[:ldap]).to eq(Gitlab.config.ldap.enabled) + expect(subject[:gravatar]).to eq(current_application_settings.gravatar_enabled?) + expect(subject[:omniauth]).to eq(Gitlab.config.omniauth.enabled) + expect(subject[:reply_by_email]).to eq(Gitlab::IncomingEmail.enabled?) + expect(subject[:container_registry]).to eq(Gitlab.config.registry.enabled) + expect(subject[:gitlab_shared_runners]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) + end + end + + describe '#components_usage_data' do + subject { described_class.components_usage_data } + + it 'gathers components usage data' do + expect(subject[:gitlab_pages][:enabled]).to eq(Gitlab.config.pages.enabled) + expect(subject[:gitlab_pages][:version]).to eq(Gitlab::Pages::VERSION) + expect(subject[:git][:version]).to eq(Gitlab::Git.version) + expect(subject[:database][:adapter]).to eq(Gitlab::Database.adapter_name) + expect(subject[:database][:version]).to eq(Gitlab::Database.version) + end + end + describe '#license_usage_data' do subject { described_class.license_usage_data } |