diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-14 03:06:24 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-14 03:06:24 +0300 |
commit | eed996ac33a60d5fd8315a62fec8beaa8e907e69 (patch) | |
tree | d8077bee50b58a170ae1a950ae76e3011c78a415 /spec/lib/gitlab/ci | |
parent | b42f312df5aee0f1b832b69171e9d1cf92eb7416 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/cache_spec.rb | 77 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/files_spec.rb | 54 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/key_spec.rb | 94 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/prefix_spec.rb | 28 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb | 261 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/seed/build_spec.rb | 98 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/yaml_processor_spec.rb | 100 |
7 files changed, 624 insertions, 88 deletions
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 9aab3664e1c..4fa0a57dc82 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -12,22 +12,53 @@ describe Gitlab::Ci::Config::Entry::Cache do context 'when entry config value is correct' do let(:policy) { nil } + let(:key) { 'some key' } let(:config) do - { key: 'some key', + { key: key, untracked: true, paths: ['some/path/'], policy: policy } end describe '#value' do - it 'returns hash value' do - expect(entry.value).to eq(key: 'some key', untracked: true, paths: ['some/path/'], policy: 'pull-push') + shared_examples 'hash key value' do + it 'returns hash value' do + expect(entry.value).to eq(key: key, untracked: true, paths: ['some/path/'], policy: 'pull-push') + end + end + + it_behaves_like 'hash key value' + + context 'with files' do + let(:key) { { files: ['a-file', 'other-file'] } } + + it_behaves_like 'hash key value' + end + + context 'with files and prefix' do + let(:key) { { files: ['a-file', 'other-file'], prefix: 'prefix-value' } } + + it_behaves_like 'hash key value' + end + + context 'with prefix' do + let(:key) { { prefix: 'prefix-value' } } + + it 'key is nil' do + expect(entry.value).to match(a_hash_including(key: nil)) + end end end describe '#valid?' do it { is_expected.to be_valid } + + context 'with files' do + let(:key) { { files: ['a-file', 'other-file'] } } + + it { is_expected.to be_valid } + end end context 'policy is pull-push' do @@ -87,10 +118,44 @@ describe Gitlab::Ci::Config::Entry::Cache do end context 'when descendants are invalid' do - let(:config) { { key: 1 } } + context 'with invalid keys' do + let(:config) { { key: 1 } } - it 'reports error with descendants' do - is_expected.to include 'key config should be a string or symbol' + it 'reports error with descendants' do + is_expected.to include 'key should be a hash, a string or a symbol' + end + end + + context 'with empty key' do + let(:config) { { key: {} } } + + it 'reports error with descendants' do + is_expected.to include 'key config missing required keys: files' + end + end + + context 'with invalid files' do + let(:config) { { key: { files: 'a-file' } } } + + it 'reports error with descendants' do + is_expected.to include 'key:files config should be an array of strings' + end + end + + context 'with prefix without files' do + let(:config) { { key: { prefix: 'a-prefix' } } } + + it 'reports error with descendants' do + is_expected.to include 'key config missing required keys: files' + end + end + + context 'when there is an unknown key present' do + let(:config) { { key: { unknown: 'a-file' } } } + + it 'reports error with descendants' do + is_expected.to include 'key config contains unknown keys: unknown' + end end end diff --git a/spec/lib/gitlab/ci/config/entry/files_spec.rb b/spec/lib/gitlab/ci/config/entry/files_spec.rb new file mode 100644 index 00000000000..2bebbd7b198 --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/files_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Config::Entry::Files do + let(:entry) { described_class.new(config) } + + describe 'validations' do + context 'when entry config value is valid' do + let(:config) { ['some/file', 'some/path/'] } + + describe '#value' do + it 'returns key value' do + expect(entry.value).to eq config + end + end + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + end + + describe '#errors' do + context 'when entry value is not an array' do + let(:config) { 'string' } + + it 'saves errors' do + expect(entry.errors) + .to include 'files config should be an array of strings' + end + end + + context 'when entry value is not an array of strings' do + let(:config) { [1] } + + it 'saves errors' do + expect(entry.errors) + .to include 'files config should be an array of strings' + end + end + + context 'when entry value contains more than two values' do + let(:config) { %w[file1 file2 file3] } + + it 'saves errors' do + expect(entry.errors) + .to include 'files config has too many items (maximum is 2)' + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb index a7874447725..327607e2266 100644 --- a/spec/lib/gitlab/ci/config/entry/key_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb @@ -6,38 +6,38 @@ describe Gitlab::Ci::Config::Entry::Key do let(:entry) { described_class.new(config) } describe 'validations' do - shared_examples 'key with slash' do - it 'is invalid' do - expect(entry).not_to be_valid - end + it_behaves_like 'key entry validations', 'simple key' - it 'reports errors with config value' do - expect(entry.errors).to include 'key config cannot contain the "/" character' - end - end + context 'when entry config value is correct' do + context 'when key is a hash' do + let(:config) { { files: ['test'], prefix: 'something' } } - shared_examples 'key with only dots' do - it 'is invalid' do - expect(entry).not_to be_valid - end + describe '#value' do + it 'returns key value' do + expect(entry.value).to match(config) + end + end - it 'reports errors with config value' do - expect(entry.errors).to include 'key config cannot be "." or ".."' + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end end - end - context 'when entry config value is correct' do - let(:config) { 'test' } + context 'when key is a symbol' do + let(:config) { :key } - describe '#value' do - it 'returns key value' do - expect(entry.value).to eq 'test' + describe '#value' do + it 'returns key value' do + expect(entry.value).to eq(config.to_s) + end end - end - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end end end end @@ -47,53 +47,11 @@ describe Gitlab::Ci::Config::Entry::Key do describe '#errors' do it 'saves errors' do - expect(entry.errors) - .to include 'key config should be a string or symbol' + expect(entry.errors.first) + .to match /should be a hash, a string or a symbol/ end end end - - context 'when entry value contains slash' do - let(:config) { 'key/with/some/slashes' } - - it_behaves_like 'key with slash' - end - - context 'when entry value contains URI encoded slash (%2F)' do - let(:config) { 'key%2Fwith%2Fsome%2Fslashes' } - - it_behaves_like 'key with slash' - end - - context 'when entry value is a dot' do - let(:config) { '.' } - - it_behaves_like 'key with only dots' - end - - context 'when entry value is two dots' do - let(:config) { '..' } - - it_behaves_like 'key with only dots' - end - - context 'when entry value is a URI encoded dot (%2E)' do - let(:config) { '%2e' } - - it_behaves_like 'key with only dots' - end - - context 'when entry value is two URI encoded dots (%2E)' do - let(:config) { '%2E%2e' } - - it_behaves_like 'key with only dots' - end - - context 'when entry value is one dot and one URI encoded dot' do - let(:config) { '.%2e' } - - it_behaves_like 'key with only dots' - end end describe '.default' do diff --git a/spec/lib/gitlab/ci/config/entry/prefix_spec.rb b/spec/lib/gitlab/ci/config/entry/prefix_spec.rb new file mode 100644 index 00000000000..8132a674488 --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/prefix_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Config::Entry::Prefix do + let(:entry) { described_class.new(config) } + + describe 'validations' do + it_behaves_like 'key entry validations', :prefix + + context 'when entry value is not correct' do + let(:config) { ['incorrect'] } + + describe '#errors' do + it 'saves errors' do + expect(entry.errors) + .to include 'prefix config should be a string or symbol' + end + end + end + end + + describe '.default' do + it 'returns default key' do + expect(described_class.default).to be_nil + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb new file mode 100644 index 00000000000..6a8b804597c --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb @@ -0,0 +1,261 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Seed::Build::Cache do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:head_sha) { project.repository.head_commit.id } + let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: head_sha) } + + let(:processor) { described_class.new(pipeline, config) } + + describe '#build_attributes' do + subject { processor.build_attributes } + + context 'with cache:key' do + let(:config) do + { + key: 'a-key', + paths: ['vendor/ruby'] + } + end + + it { is_expected.to include(options: { cache: config }) } + end + + context 'with cache:key as a symbol' do + let(:config) do + { + key: :a_key, + paths: ['vendor/ruby'] + } + end + + it { is_expected.to include(options: { cache: config.merge(key: "a_key") }) } + end + + context 'with cache:key:files' do + shared_examples 'default key' do + let(:config) do + { key: { files: files } } + end + + it 'uses default key' do + expected = { options: { cache: { key: 'default' } } } + + is_expected.to include(expected) + end + end + + shared_examples 'version and gemfile files' do + let(:config) do + { + key: { + files: files + }, + paths: ['vendor/ruby'] + } + end + + it 'builds a string key' do + expected = { + options: { + cache: { + key: '703ecc8fef1635427a1f86a8a1a308831c122392', + paths: ['vendor/ruby'] + } + } + } + + is_expected.to include(expected) + end + end + + context 'with existing files' do + let(:files) { ['VERSION', 'Gemfile.zip'] } + + it_behaves_like 'version and gemfile files' + end + + context 'with files starting with ./' do + let(:files) { ['Gemfile.zip', './VERSION'] } + + it_behaves_like 'version and gemfile files' + end + + context 'with feature flag disabled' do + let(:files) { ['VERSION', 'Gemfile.zip'] } + + before do + stub_feature_flags(ci_file_based_cache: false) + end + + it_behaves_like 'default key' + end + + context 'with files ending with /' do + let(:files) { ['Gemfile.zip/'] } + + it_behaves_like 'default key' + end + + context 'with new line in filenames' do + let(:files) { ["Gemfile.zip\nVERSION"] } + + it_behaves_like 'default key' + end + + context 'with missing files' do + let(:files) { ['project-gemfile.lock', ''] } + + it_behaves_like 'default key' + end + + context 'with directories' do + shared_examples 'foo/bar directory key' do + let(:config) do + { + key: { + files: files + } + } + end + + it 'builds a string key' do + expected = { + options: { + cache: { key: '74bf43fb1090f161bdd4e265802775dbda2f03d1' } + } + } + + is_expected.to include(expected) + end + end + + context 'with directory' do + let(:files) { ['foo/bar'] } + + it_behaves_like 'foo/bar directory key' + end + + context 'with directory ending in slash' do + let(:files) { ['foo/bar/'] } + + it_behaves_like 'foo/bar directory key' + end + + context 'with directories ending in slash star' do + let(:files) { ['foo/bar/*'] } + + it_behaves_like 'foo/bar directory key' + end + end + end + + context 'with cache:key:prefix' do + context 'without files' do + let(:config) do + { + key: { + prefix: 'a-prefix' + }, + paths: ['vendor/ruby'] + } + end + + it 'adds prefix to default key' do + expected = { + options: { + cache: { + key: 'a-prefix-default', + paths: ['vendor/ruby'] + } + } + } + + is_expected.to include(expected) + end + end + + context 'with existing files' do + let(:config) do + { + key: { + files: ['VERSION', 'Gemfile.zip'], + prefix: 'a-prefix' + }, + paths: ['vendor/ruby'] + } + end + + it 'adds prefix key' do + expected = { + options: { + cache: { + key: 'a-prefix-703ecc8fef1635427a1f86a8a1a308831c122392', + paths: ['vendor/ruby'] + } + } + } + + is_expected.to include(expected) + end + end + + context 'with missing files' do + let(:config) do + { + key: { + files: ['project-gemfile.lock', ''], + prefix: 'a-prefix' + }, + paths: ['vendor/ruby'] + } + end + + it 'adds prefix to default key' do + expected = { + options: { + cache: { + key: 'a-prefix-default', + paths: ['vendor/ruby'] + } + } + } + + is_expected.to include(expected) + end + end + end + + context 'with all cache option keys' do + let(:config) do + { + key: 'a-key', + paths: ['vendor/ruby'], + untracked: true, + policy: 'push' + } + end + + it { is_expected.to include(options: { cache: config }) } + end + + context 'with unknown cache option keys' do + let(:config) do + { + key: 'a-key', + unknown_key: true + } + end + + it { expect { subject }.to raise_error(ArgumentError, /unknown_key/) } + end + + context 'with empty config' do + let(:config) { {} } + + it { is_expected.to include(options: {}) } + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 945baf47b7b..62e5fd566f7 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -4,7 +4,8 @@ require 'spec_helper' describe Gitlab::Ci::Pipeline::Seed::Build do let(:project) { create(:project, :repository) } - let(:pipeline) { create(:ci_empty_pipeline, project: project) } + let(:head_sha) { project.repository.head_commit.id } + let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: head_sha) } let(:attributes) { { name: 'rspec', ref: 'master' } } let(:previous_stages) { [] } @@ -69,6 +70,101 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to include(when: 'never') } end end + + context 'with cache:key' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + cache: { + key: 'a-value' + } + } + end + + it { is_expected.to include(options: { cache: { key: 'a-value' } }) } + end + + context 'with cache:key:files' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + cache: { + key: { + files: ['VERSION'] + } + } + } + end + + it 'includes cache options' do + cache_options = { + options: { + cache: { + key: 'f155568ad0933d8358f66b846133614f76dd0ca4' + } + } + } + + is_expected.to include(cache_options) + end + end + + context 'with cache:key:prefix' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + cache: { + key: { + prefix: 'something' + } + } + } + end + + it { is_expected.to include(options: { cache: { key: 'something-default' } }) } + end + + context 'with cache:key:files and prefix' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + cache: { + key: { + files: ['VERSION'], + prefix: 'something' + } + } + } + end + + it 'includes cache options' do + cache_options = { + options: { + cache: { + key: 'something-f155568ad0933d8358f66b846133614f76dd0ca4' + } + } + } + + is_expected.to include(cache_options) + end + end + + context 'with empty cache' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + cache: {} + } + end + + it { is_expected.to include(options: {}) } + end end describe '#bridge?' do diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 5a173470dfe..35a4749922e 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -950,7 +950,7 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) expect(config_processor.stage_builds_attributes("test").size).to eq(1) - expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq( + expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq( paths: ["logs/", "binaries/"], untracked: true, key: 'key', @@ -962,7 +962,7 @@ module Gitlab config = YAML.dump( { default: { - cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' } + cache: { paths: ["logs/", "binaries/"], untracked: true, key: { files: ['file'] } } }, rspec: { script: "rspec" @@ -972,33 +972,79 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) expect(config_processor.stage_builds_attributes("test").size).to eq(1) - expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq( + expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq( paths: ["logs/", "binaries/"], untracked: true, - key: 'key', + key: { files: ['file'] }, policy: 'pull-push' ) end - it "returns cache when defined in a job" do + it 'returns cache key when defined in a job' do config = YAML.dump({ rspec: { - cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' }, - script: "rspec" + cache: { paths: ['logs/', 'binaries/'], untracked: true, key: 'key' }, + script: 'rspec' } }) config_processor = Gitlab::Ci::YamlProcessor.new(config) - expect(config_processor.stage_builds_attributes("test").size).to eq(1) - expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq( - paths: ["logs/", "binaries/"], + expect(config_processor.stage_builds_attributes('test').size).to eq(1) + expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq( + paths: ['logs/', 'binaries/'], untracked: true, key: 'key', policy: 'pull-push' ) end + it 'returns cache files' do + config = YAML.dump( + rspec: { + cache: { + paths: ['logs/', 'binaries/'], + untracked: true, + key: { files: ['file'] } + }, + script: 'rspec' + } + ) + + config_processor = Gitlab::Ci::YamlProcessor.new(config) + + expect(config_processor.stage_builds_attributes('test').size).to eq(1) + expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq( + paths: ['logs/', 'binaries/'], + untracked: true, + key: { files: ['file'] }, + policy: 'pull-push' + ) + end + + it 'returns cache files with prefix' do + config = YAML.dump( + rspec: { + cache: { + paths: ['logs/', 'binaries/'], + untracked: true, + key: { files: ['file'], prefix: 'prefix' } + }, + script: 'rspec' + } + ) + + config_processor = Gitlab::Ci::YamlProcessor.new(config) + + expect(config_processor.stage_builds_attributes('test').size).to eq(1) + expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq( + paths: ['logs/', 'binaries/'], + untracked: true, + key: { files: ['file'], prefix: 'prefix' }, + policy: 'pull-push' + ) + end + it "overwrite cache when defined for a job and globally" do config = YAML.dump({ cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' }, @@ -1011,7 +1057,7 @@ module Gitlab config_processor = Gitlab::Ci::YamlProcessor.new(config) expect(config_processor.stage_builds_attributes("test").size).to eq(1) - expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq( + expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq( paths: ["test/"], untracked: false, key: 'local', @@ -1862,14 +1908,42 @@ module Gitlab config = YAML.dump({ cache: { key: 1 }, rspec: { script: "test" } }) expect do Gitlab::Ci::YamlProcessor.new(config) - end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "cache:key config should be a string or symbol") + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "cache:key should be a hash, a string or a symbol") end it "returns errors if job cache:key is not an a string" do config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: 1 } } }) expect do Gitlab::Ci::YamlProcessor.new(config) - end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol") + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:cache:key should be a hash, a string or a symbol") + end + + it 'returns errors if job cache:key:files is not an array of strings' do + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { files: [1] } } } }) + expect do + Gitlab::Ci::YamlProcessor.new(config) + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:files config should be an array of strings') + end + + it 'returns errors if job cache:key:files is an empty array' do + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { files: [] } } } }) + expect do + Gitlab::Ci::YamlProcessor.new(config) + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:files config requires at least 1 item') + end + + it 'returns errors if job defines only cache:key:prefix' do + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { prefix: 'prefix-key' } } } }) + expect do + Gitlab::Ci::YamlProcessor.new(config) + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key config missing required keys: files') + end + + it 'returns errors if job cache:key:prefix is not an a string' do + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { prefix: 1, files: ['file'] } } } }) + expect do + Gitlab::Ci::YamlProcessor.new(config) + end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:prefix config should be a string or symbol') end it "returns errors if job cache:untracked is not an array of strings" do |