diff options
author | Micaël Bergeron <mbergeron@gitlab.com> | 2018-03-22 09:06:10 -0400 |
---|---|---|
committer | Micaël Bergeron <mbergeron@gitlab.com> | 2018-03-22 09:06:10 -0400 |
commit | 9c6663ea079128bb730ec2a168b43961cd9462ec (patch) | |
tree | 4f62c2e745c7f3e8571ee8c023abcce316c68275 /spec/lib | |
parent | 6801a93e5e7447199b091e44f33c96d22a1a1960 (diff) | |
parent | c01697539c3da4e72b1812662ff35d1f709d1dcc (diff) | |
download | gitlab-ce-9c6663ea079128bb730ec2a168b43961cd9462ec.tar.gz |
Merge remote-tracking branch 'origin/master' into 40781-os-to-ce
Diffstat (limited to 'spec/lib')
32 files changed, 966 insertions, 95 deletions
diff --git a/spec/lib/api/helpers/related_resources_helpers_spec.rb b/spec/lib/api/helpers/related_resources_helpers_spec.rb new file mode 100644 index 00000000000..b918301f1cb --- /dev/null +++ b/spec/lib/api/helpers/related_resources_helpers_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe API::Helpers::RelatedResourcesHelpers do + subject(:helpers) do + Class.new.include(described_class).new + end + + describe '#expose_url' do + let(:path) { '/api/v4/awesome_endpoint' } + subject(:url) { helpers.expose_url(path) } + + def stub_default_url_options(protocol: 'http', host: 'example.com', port: nil) + expect(Gitlab::Application.routes).to receive(:default_url_options) + .and_return(protocol: protocol, host: host, port: port) + end + + it 'respects the protocol if it is HTTP' do + stub_default_url_options(protocol: 'http') + + is_expected.to start_with('http://') + end + + it 'respects the protocol if it is HTTPS' do + stub_default_url_options(protocol: 'https') + + is_expected.to start_with('https://') + end + + it 'accepts port to be nil' do + stub_default_url_options(port: nil) + + is_expected.to start_with('http://example.com/') + end + + it 'includes port if provided' do + stub_default_url_options(port: 8080) + + is_expected.to start_with('http://example.com:8080/') + end + end +end diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 3ca4652f7cc..ba8dc68ceda 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -217,6 +217,23 @@ describe Banzai::Filter::RelativeLinkFilter do end end + context 'when ref name contains special chars' do + let(:ref) {'mark#\'@],+;-._/#@!$&()+down'} + + it 'correctly escapes the ref' do + # Adressable won't escape the '#', so we do this manually + ref_escaped = 'mark%23\'@%5D,+;-._/%23@!$&()+down' + + # Stub this method so the branch doesn't actually need to be in the repo + allow_any_instance_of(described_class).to receive(:uri_type).and_return(:raw) + + doc = filter(link('files/images/logo-black.png')) + + expect(doc.at_css('a')['href']) + .to eq "/#{project_path}/raw/#{ref_escaped}/files/images/logo-black.png" + end + end + context 'when requested path is a directory with space in the repo' do let(:ref) { 'master' } let(:commit) { project.commit('38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') } diff --git a/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb b/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb index 21a791f5695..c43ed72038e 100644 --- a/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb +++ b/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb @@ -37,6 +37,18 @@ describe Gitlab::BackgroundMigration::AddMergeRequestDiffCommitsCount, :migratio expect(diff.reload.commits_count).to eq(0) end + it 'skips diffs that have commits_count already set' do + timestamp = 2.days.ago + diff = merge_request_diffs_table.create!( + merge_request_id: merge_request.id, + commits_count: 0, + updated_at: timestamp) + + subject.perform(diff.id, diff.id) + + expect(diff.reload.updated_at).to be_within(1.second).of(timestamp) + end + it 'migrates multiple diffs to the correct values' do diffs = Array.new(3).map.with_index { |_, i| create_diff!(i, commits: 3) } diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb index 9f42cf1dfca..5cb1f4deb5f 100644 --- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb @@ -61,7 +61,7 @@ describe ::Gitlab::BareRepositoryImport::Repository do let(:wiki_path) { File.join(root_path, "#{hashed_path}.wiki.git") } before do - gitlab_shell.add_repository(repository_storage, hashed_path) + gitlab_shell.create_repository(repository_storage, hashed_path) repository = Rugged::Repository.new(repo_path) repository.config['gitlab.fullpath'] = 'to/repo' end diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb index 448c6fb57dd..3a9371ed2e8 100644 --- a/spec/lib/gitlab/ci/trace_spec.rb +++ b/spec/lib/gitlab/ci/trace_spec.rb @@ -510,6 +510,28 @@ describe Gitlab::Ci::Trace do it_behaves_like 'source trace in database stays intact', error: ActiveRecord::RecordInvalid end + + context 'when there is a validation error on Ci::Build' do + before do + allow_any_instance_of(Ci::Build).to receive(:save).and_return(false) + allow_any_instance_of(Ci::Build).to receive_message_chain(:errors, :full_messages) + .and_return(%w[Error Error]) + end + + context "when erase old trace with 'save'" do + before do + build.send(:write_attribute, :trace, nil) + build.save + end + + it 'old trace is not deleted' do + build.reload + expect(build.trace.raw).to eq(trace_content) + end + end + + it_behaves_like 'archive trace in database' + end end end diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb new file mode 100644 index 00000000000..cc1257484d2 --- /dev/null +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Gitlab::Ci::Variables::Collection::Item do + let(:variable) do + { key: 'VAR', value: 'something', public: true } + end + + describe '.fabricate' do + it 'supports using a hash' do + resource = described_class.fabricate(variable) + + expect(resource).to be_a(described_class) + expect(resource).to eq variable + end + + it 'supports using an active record resource' do + variable = create(:ci_variable, key: 'CI_VAR', value: '123') + resource = described_class.fabricate(variable) + + expect(resource).to be_a(described_class) + expect(resource).to eq(key: 'CI_VAR', value: '123', public: false) + end + + it 'supports using another collection item' do + item = described_class.new(**variable) + + resource = described_class.fabricate(item) + + expect(resource).to be_a(described_class) + expect(resource).to eq variable + expect(resource.object_id).not_to eq item.object_id + end + end + + describe '#==' do + it 'compares a hash representation of a variable' do + expect(described_class.new(**variable) == variable).to be true + end + end + + describe '#[]' do + it 'behaves like a hash accessor' do + item = described_class.new(**variable) + + expect(item[:key]).to eq 'VAR' + end + end + + describe '#to_hash' do + it 'returns a hash representation of a collection item' do + expect(described_class.new(**variable).to_hash).to eq variable + end + end +end diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb new file mode 100644 index 00000000000..90b6e178242 --- /dev/null +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -0,0 +1,99 @@ +require 'spec_helper' + +describe Gitlab::Ci::Variables::Collection do + describe '.new' do + it 'can be initialized with an array' do + variable = { key: 'VAR', value: 'value', public: true } + + collection = described_class.new([variable]) + + expect(collection.first.to_hash).to eq variable + end + + it 'can be initialized without an argument' do + expect(subject).to be_none + end + end + + describe '#append' do + it 'appends a hash' do + subject.append(key: 'VARIABLE', value: 'something') + + expect(subject).to be_one + end + + it 'appends a Ci::Variable' do + subject.append(build(:ci_variable)) + + expect(subject).to be_one + end + + it 'appends an internal resource' do + collection = described_class.new([{ key: 'TEST', value: 1 }]) + + subject.append(collection.first) + + expect(subject).to be_one + end + + it 'returns self' do + expect(subject.append(key: 'VAR', value: 'test')) + .to eq subject + end + end + + describe '#concat' do + it 'appends all elements from an array' do + collection = described_class.new([{ key: 'VAR_1', value: '1' }]) + variables = [{ key: 'VAR_2', value: '2' }, { key: 'VAR_3', value: '3' }] + + collection.concat(variables) + + expect(collection).to include(key: 'VAR_1', value: '1', public: true) + expect(collection).to include(key: 'VAR_2', value: '2', public: true) + expect(collection).to include(key: 'VAR_3', value: '3', public: true) + end + + it 'appends all elements from other collection' do + collection = described_class.new([{ key: 'VAR_1', value: '1' }]) + additional = described_class.new([{ key: 'VAR_2', value: '2' }, + { key: 'VAR_3', value: '3' }]) + + collection.concat(additional) + + expect(collection).to include(key: 'VAR_1', value: '1', public: true) + expect(collection).to include(key: 'VAR_2', value: '2', public: true) + expect(collection).to include(key: 'VAR_3', value: '3', public: true) + end + + it 'returns self' do + expect(subject.concat([key: 'VAR', value: 'test'])) + .to eq subject + end + end + + describe '#+' do + it 'makes it possible to combine with an array' do + collection = described_class.new([{ key: 'TEST', value: 1 }]) + variables = [{ key: 'TEST', value: 'something' }] + + expect((collection + variables).count).to eq 2 + end + + it 'makes it possible to combine with another collection' do + collection = described_class.new([{ key: 'TEST', value: 1 }]) + other = described_class.new([{ key: 'TEST', value: 2 }]) + + expect((collection + other).count).to eq 2 + end + end + + describe '#to_runner_variables' do + it 'creates an array of hashes in a runner-compatible format' do + collection = described_class.new([{ key: 'TEST', value: 1 }]) + + expect(collection.to_runner_variables) + .to eq [{ key: 'TEST', value: 1, public: true }] + end + end +end diff --git a/spec/lib/gitlab/conflict/file_collection_spec.rb b/spec/lib/gitlab/conflict/file_collection_spec.rb index 5944ce8049a..c93912db411 100644 --- a/spec/lib/gitlab/conflict/file_collection_spec.rb +++ b/spec/lib/gitlab/conflict/file_collection_spec.rb @@ -10,6 +10,38 @@ describe Gitlab::Conflict::FileCollection do end end + describe '#cache' do + it 'specifies a custom namespace with the merge request commit ids' do + our_commit = merge_request.source_branch_head.raw + their_commit = merge_request.target_branch_head.raw + custom_namespace = "#{our_commit.id}:#{their_commit.id}" + + expect(file_collection.send(:cache).namespace).to include(custom_namespace) + end + end + + describe '#can_be_resolved_in_ui?' do + it 'returns true if conflicts for this collection can be resolved in the UI' do + expect(file_collection.can_be_resolved_in_ui?).to be true + end + + it "returns false if conflicts for this collection can't be resolved in the UI" do + expect(file_collection).to receive(:files).and_raise(Gitlab::Git::Conflict::Resolver::ConflictSideMissing) + + expect(file_collection.can_be_resolved_in_ui?).to be false + end + + it 'caches the result' do + expect(file_collection).to receive(:files).twice.and_call_original + + expect(file_collection.can_be_resolved_in_ui?).to be true + + expect(file_collection).not_to receive(:files) + + expect(file_collection.can_be_resolved_in_ui?).to be true + end + end + describe '#default_commit_message' do it 'matches the format of the git CLI commit message' do expect(file_collection.default_commit_message).to eq(<<EOM.chomp) diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 1de3a14b809..a41b7f4e104 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -67,17 +67,35 @@ describe Gitlab::Database::MigrationHelpers do model.add_concurrent_index(:users, :foo, unique: true) end + + it 'does nothing if the index exists already' do + expect(model).to receive(:index_exists?) + .with(:users, :foo, { algorithm: :concurrently, unique: true }).and_return(true) + expect(model).not_to receive(:add_index) + + model.add_concurrent_index(:users, :foo, unique: true) + end end context 'using MySQL' do - it 'creates a regular index' do - expect(Gitlab::Database).to receive(:postgresql?).and_return(false) + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + end + it 'creates a regular index' do expect(model).to receive(:add_index) .with(:users, :foo, {}) model.add_concurrent_index(:users, :foo) end + + it 'does nothing if the index exists already' do + expect(model).to receive(:index_exists?) + .with(:users, :foo, { unique: true }).and_return(true) + expect(model).not_to receive(:add_index) + + model.add_concurrent_index(:users, :foo, unique: true) + end end end @@ -95,6 +113,7 @@ describe Gitlab::Database::MigrationHelpers do context 'outside a transaction' do before do allow(model).to receive(:transaction_open?).and_return(false) + allow(model).to receive(:index_exists?).and_return(true) end context 'using PostgreSQL' do @@ -103,18 +122,41 @@ describe Gitlab::Database::MigrationHelpers do allow(model).to receive(:disable_statement_timeout) end - it 'removes the index concurrently by column name' do - expect(model).to receive(:remove_index) - .with(:users, { algorithm: :concurrently, column: :foo }) + describe 'by column name' do + it 'removes the index concurrently' do + expect(model).to receive(:remove_index) + .with(:users, { algorithm: :concurrently, column: :foo }) - model.remove_concurrent_index(:users, :foo) + model.remove_concurrent_index(:users, :foo) + end + + it 'does nothing if the index does not exist' do + expect(model).to receive(:index_exists?) + .with(:users, :foo, { algorithm: :concurrently, unique: true }).and_return(false) + expect(model).not_to receive(:remove_index) + + model.remove_concurrent_index(:users, :foo, unique: true) + end end - it 'removes the index concurrently by index name' do - expect(model).to receive(:remove_index) - .with(:users, { algorithm: :concurrently, name: "index_x_by_y" }) + describe 'by index name' do + before do + allow(model).to receive(:index_exists_by_name?).with(:users, "index_x_by_y").and_return(true) + end + + it 'removes the index concurrently by index name' do + expect(model).to receive(:remove_index) + .with(:users, { algorithm: :concurrently, name: "index_x_by_y" }) + + model.remove_concurrent_index_by_name(:users, "index_x_by_y") + end + + it 'does nothing if the index does not exist' do + expect(model).to receive(:index_exists_by_name?).with(:users, "index_x_by_y").and_return(false) + expect(model).not_to receive(:remove_index) - model.remove_concurrent_index_by_name(:users, "index_x_by_y") + model.remove_concurrent_index_by_name(:users, "index_x_by_y") + end end end @@ -141,6 +183,10 @@ describe Gitlab::Database::MigrationHelpers do end describe '#add_concurrent_foreign_key' do + before do + allow(model).to receive(:foreign_key_exists?).and_return(false) + end + context 'inside a transaction' do it 'raises an error' do expect(model).to receive(:transaction_open?).and_return(true) @@ -157,14 +203,23 @@ describe Gitlab::Database::MigrationHelpers do end context 'using MySQL' do - it 'creates a regular foreign key' do + before do allow(Gitlab::Database).to receive(:mysql?).and_return(true) + end + it 'creates a regular foreign key' do expect(model).to receive(:add_foreign_key) .with(:projects, :users, column: :user_id, on_delete: :cascade) model.add_concurrent_foreign_key(:projects, :users, column: :user_id) end + + it 'does not create a foreign key if it exists already' do + expect(model).to receive(:foreign_key_exists?).with(:projects, :users, column: :user_id).and_return(true) + expect(model).not_to receive(:add_foreign_key) + + model.add_concurrent_foreign_key(:projects, :users, column: :user_id) + end end context 'using PostgreSQL' do @@ -189,6 +244,14 @@ describe Gitlab::Database::MigrationHelpers do column: :user_id, on_delete: :nullify) end + + it 'does not create a foreign key if it exists already' do + expect(model).to receive(:foreign_key_exists?).with(:projects, :users, column: :user_id).and_return(true) + expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/) + expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/) + + model.add_concurrent_foreign_key(:projects, :users, column: :user_id) + end end end end @@ -203,6 +266,29 @@ describe Gitlab::Database::MigrationHelpers do end end + describe '#foreign_key_exists?' do + before do + key = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(:projects, :users, { column: :non_standard_id }) + allow(model).to receive(:foreign_keys).with(:projects).and_return([key]) + end + + it 'finds existing foreign keys by column' do + expect(model.foreign_key_exists?(:projects, :users, column: :non_standard_id)).to be_truthy + end + + it 'finds existing foreign keys by target table only' do + expect(model.foreign_key_exists?(:projects, :users)).to be_truthy + end + + it 'compares by column name if given' do + expect(model.foreign_key_exists?(:projects, :users, column: :user_id)).to be_falsey + end + + it 'compares by target if no column given' do + expect(model.foreign_key_exists?(:projects, :other_table)).to be_falsey + end + end + describe '#disable_statement_timeout' do context 'using PostgreSQL' do it 'disables statement timeouts' do diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index b2f13fae73f..1fe1d3926ad 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -287,6 +287,29 @@ describe Gitlab::Database do end end + describe '.cached_column_exists?' do + it 'only retrieves data once' do + expect(ActiveRecord::Base.connection).to receive(:columns).once.and_call_original + + 2.times do + expect(described_class.cached_column_exists?(:projects, :id)).to be_truthy + expect(described_class.cached_column_exists?(:projects, :bogus_column)).to be_falsey + end + end + end + + describe '.cached_table_exists?' do + it 'only retrieves data once per table' do + expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original + expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original + + 2.times do + expect(described_class.cached_table_exists?(:projects)).to be_truthy + expect(described_class.cached_table_exists?(:bogus_table_name)).to be_falsey + end + end + end + describe '#true_value' do it 'returns correct value for PostgreSQL' do expect(described_class).to receive(:postgresql?).and_return(true) diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb index a067c42b75b..f48ee8924e8 100644 --- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb +++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb @@ -12,7 +12,7 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do diff_files end - it 'does not files marked as undiffable in .gitattributes' do + it 'does not highlight files marked as undiffable in .gitattributes' do allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(false) expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines) diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 9204ea37963..0c2e18c268a 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -455,5 +455,17 @@ describe Gitlab::Diff::File do expect(diff_file.size).to be_zero end end + + describe '#different_type?' do + it 'returns false' do + expect(diff_file).not_to be_different_type + end + end + + describe '#content_changed?' do + it 'returns false' do + expect(diff_file).not_to be_content_changed + end + end end end diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb index 6193e177668..aed7d8d81ce 100644 --- a/spec/lib/gitlab/exclusive_lease_spec.rb +++ b/spec/lib/gitlab/exclusive_lease_spec.rb @@ -88,4 +88,16 @@ describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do expect(lease.ttl).to be_nil end end + + describe '.reset_all!' do + it 'removes all existing lease keys from redis' do + uuid = described_class.new(unique_key, timeout: 3600).try_obtain + + expect(described_class.get_uuid(unique_key)).to eq(uuid) + + described_class.reset_all! + + expect(described_class.get_uuid(unique_key)).to be_falsey + end + end end diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb index 45bcd730332..dfccc15a4f3 100644 --- a/spec/lib/gitlab/git/gitlab_projects_spec.rb +++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb @@ -28,7 +28,7 @@ describe Gitlab::Git::GitlabProjects do describe '#push_branches' do let(:remote_name) { 'remote-name' } let(:branch_name) { 'master' } - let(:cmd) { %W(git push -- #{remote_name} #{branch_name}) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} push -- #{remote_name} #{branch_name}) } let(:force) { false } subject { gl_projects.push_branches(remote_name, 600, force, [branch_name]) } @@ -46,7 +46,7 @@ describe Gitlab::Git::GitlabProjects do end context 'with --force' do - let(:cmd) { %W(git push --force -- #{remote_name} #{branch_name}) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} push --force -- #{remote_name} #{branch_name}) } let(:force) { true } it 'executes the command' do @@ -65,7 +65,7 @@ describe Gitlab::Git::GitlabProjects do let(:tags) { true } let(:args) { { force: force, tags: tags, prune: prune }.merge(extra_args) } let(:extra_args) { {} } - let(:cmd) { %W(git fetch #{remote_name} --quiet --prune --tags) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --tags) } subject { gl_projects.fetch_remote(remote_name, 600, args) } @@ -98,7 +98,7 @@ describe Gitlab::Git::GitlabProjects do context 'with --force' do let(:force) { true } - let(:cmd) { %W(git fetch #{remote_name} --quiet --prune --force --tags) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --force --tags) } it 'executes the command with forced option' do stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) @@ -109,7 +109,7 @@ describe Gitlab::Git::GitlabProjects do context 'with --no-tags' do let(:tags) { false } - let(:cmd) { %W(git fetch #{remote_name} --quiet --prune --no-tags) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --prune --no-tags) } it 'executes the command' do stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) @@ -120,7 +120,7 @@ describe Gitlab::Git::GitlabProjects do context 'with no prune' do let(:prune) { false } - let(:cmd) { %W(git fetch #{remote_name} --quiet --tags) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} fetch #{remote_name} --quiet --tags) } it 'executes the command' do stub_spawn(cmd, 600, tmp_repo_path, {}, success: true) @@ -165,7 +165,7 @@ describe Gitlab::Git::GitlabProjects do describe '#import_project' do let(:project) { create(:project) } let(:import_url) { TestEnv.factory_repo_path_bare } - let(:cmd) { %W(git clone --bare -- #{import_url} #{tmp_repo_path}) } + let(:cmd) { %W(#{Gitlab.config.git.bin_path} clone --bare -- #{import_url} #{tmp_repo_path}) } let(:timeout) { 600 } subject { gl_projects.import_project(import_url, timeout) } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 52c9876cbb6..54ada3e423f 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -681,7 +681,7 @@ describe Gitlab::Git::Repository, seed_helper: true do subject { new_repository.fetch_repository_as_mirror(repository) } before do - Gitlab::Shell.new.add_repository('default', 'my_project') + Gitlab::Shell.new.create_repository('default', 'my_project') end after do diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index bece82e531a..a204a8f1ffe 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -279,6 +279,7 @@ project: - lfs_file_locks - project_badges - source_of_merge_requests +- internal_ids award_emoji: - awardable - user diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 62ef93f847a..4a51777ba9b 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -43,7 +43,6 @@ { "id": 40, "title": "Voluptatem", - "assignee_id": 1, "author_id": 22, "project_id": 5, "created_at": "2016-06-14T15:02:08.340Z", @@ -61,7 +60,23 @@ "issue_assignees": [ { "user_id": 1, - "issue_id": 1 + "issue_id": 40 + }, + { + "user_id": 15, + "issue_id": 40 + }, + { + "user_id": 16, + "issue_id": 40 + }, + { + "user_id": 16, + "issue_id": 40 + }, + { + "user_id": 6, + "issue_id": 40 } ], "milestone": { @@ -319,8 +334,7 @@ }, { "id": 39, - "title": "Delectus veniam ratione in eos culpa et natus molestiae earum aut.", - "assignee_id": 20, + "title": "Issue without assignees", "author_id": 22, "project_id": 5, "created_at": "2016-06-14T15:02:08.233Z", @@ -334,6 +348,7 @@ "confidential": false, "due_date": null, "moved_to_id": null, + "issue_assignees": [], "milestone": { "id": 1, "title": "test milestone", @@ -539,7 +554,6 @@ { "id": 38, "title": "Quasi adipisci non cupiditate dolorem quo qui earum sed.", - "assignee_id": 1, "author_id": 6, "project_id": 5, "created_at": "2016-06-14T15:02:08.154Z", @@ -756,7 +770,6 @@ { "id": 37, "title": "Cupiditate quo aut ducimus minima molestiae vero numquam possimus.", - "assignee_id": 15, "author_id": 20, "project_id": 5, "created_at": "2016-06-14T15:02:08.051Z", @@ -952,7 +965,6 @@ { "id": 36, "title": "Necessitatibus dolor est enim quia rem suscipit quidem voluptas ullam.", - "assignee_id": 20, "author_id": 16, "project_id": 5, "created_at": "2016-06-14T15:02:07.958Z", @@ -1148,7 +1160,6 @@ { "id": 35, "title": "Repellat praesentium deserunt maxime incidunt harum porro qui.", - "assignee_id": 6, "author_id": 20, "project_id": 5, "created_at": "2016-06-14T15:02:07.832Z", @@ -1344,7 +1355,6 @@ { "id": 34, "title": "Ullam expedita deserunt libero consequatur quia dolor harum perferendis facere quidem.", - "assignee_id": 20, "author_id": 1, "project_id": 5, "created_at": "2016-06-14T15:02:07.717Z", @@ -1540,7 +1550,6 @@ { "id": 33, "title": "Numquam accusamus eos iste exercitationem magni non inventore.", - "assignee_id": 15, "author_id": 26, "project_id": 5, "created_at": "2016-06-14T15:02:07.611Z", @@ -1736,7 +1745,6 @@ { "id": 32, "title": "Necessitatibus magnam qui at velit consequatur perspiciatis.", - "assignee_id": 22, "author_id": 15, "project_id": 5, "created_at": "2016-06-14T15:02:07.431Z", @@ -1932,7 +1940,6 @@ { "id": 31, "title": "Libero nam magnam incidunt eaque placeat error et.", - "assignee_id": 1, "author_id": 16, "project_id": 5, "created_at": "2016-06-14T15:02:07.280Z", diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index f4e466d1296..8e25cd26c2f 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -4,7 +4,12 @@ include ImportExport::CommonUtil describe Gitlab::ImportExport::ProjectTreeRestorer do describe 'restore project tree' do before(:context) do - @user = create(:user) + # Using an admin for import, so we can check assignment of existing members + @user = create(:admin) + @existing_members = [ + create(:user, username: 'bernard_willms'), + create(:user, username: 'saul_will') + ] RSpec::Mocks.with_temporary_scope do @project = create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') @@ -37,6 +42,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED) end + it 'has the project description' do + expect(Project.find_by_path('project').description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') + end + it 'has the project html description' do expect(Project.find_by_path('project').description_html).to eq('description') end @@ -63,8 +72,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC') end - it 'has issue assignees' do - expect(Issue.where(title: 'Voluptatem').first.issue_assignees).not_to be_empty + it 'has multiple issue assignees' do + expect(Issue.find_by(title: 'Voluptatem').assignees).to contain_exactly(@user, *@existing_members) + expect(Issue.find_by(title: 'Issue without assignees').assignees).to be_empty end it 'contains the merge access levels on a protected branch' do diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 3049491f0ae..0d20a551e2a 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -29,8 +29,17 @@ describe Gitlab::ImportExport::ProjectTreeSaver do project_json(project_tree_saver.full_path) end + context 'with description override' do + let(:params) { { description: 'Foo Bar' } } + let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared, params: params) } + + it 'overrides the project description' do + expect(saved_project_json).to include({ 'description' => params[:description] }) + end + end + it 'saves the correct json' do - expect(saved_project_json).to include({ "visibility_level" => 20 }) + expect(saved_project_json).to include({ 'description' => 'description', 'visibility_level' => 20 }) end it 'has milestones' do @@ -259,6 +268,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do :issues_disabled, :wiki_enabled, :builds_private, + description: 'description', issues: [issue], snippets: [snippet], releases: [release], diff --git a/spec/lib/gitlab/kubernetes/namespace_spec.rb b/spec/lib/gitlab/kubernetes/namespace_spec.rb index b3c987f9344..e098612f6fb 100644 --- a/spec/lib/gitlab/kubernetes/namespace_spec.rb +++ b/spec/lib/gitlab/kubernetes/namespace_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Kubernetes::Namespace do describe '#exists?' do context 'when namespace do not exits' do - let(:exception) { ::KubeException.new(404, "namespace #{name} not found", nil) } + let(:exception) { ::Kubeclient::HttpError.new(404, "namespace #{name} not found", nil) } it 'returns false' do expect(client).to receive(:get_namespace).with(name).once.and_raise(exception) diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb new file mode 100644 index 00000000000..d808b4d49e0 --- /dev/null +++ b/spec/lib/gitlab/omniauth_initializer_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab::OmniauthInitializer do + let(:devise_config) { class_double(Devise) } + + subject { described_class.new(devise_config) } + + describe '#execute' do + it 'configures providers from array' do + generic_config = { 'name' => 'generic' } + + expect(devise_config).to receive(:omniauth).with(:generic) + + subject.execute([generic_config]) + end + + it 'allows "args" array for app_id and app_secret' do + legacy_config = { 'name' => 'legacy', 'args' => %w(123 abc) } + + expect(devise_config).to receive(:omniauth).with(:legacy, '123', 'abc') + + subject.execute([legacy_config]) + end + + it 'passes app_id and app_secret as additional arguments' do + twitter_config = { 'name' => 'twitter', 'app_id' => '123', 'app_secret' => 'abc' } + + expect(devise_config).to receive(:omniauth).with(:twitter, '123', 'abc') + + subject.execute([twitter_config]) + end + + it 'passes "args" hash as symbolized hash argument' do + hash_config = { 'name' => 'hash', 'args' => { 'custom' => 'format' } } + + expect(devise_config).to receive(:omniauth).with(:hash, custom: 'format') + + subject.execute([hash_config]) + end + + it 'configures fail_with_empty_uid for shibboleth' do + shibboleth_config = { 'name' => 'shibboleth', 'args' => {} } + + expect(devise_config).to receive(:omniauth).with(:shibboleth, fail_with_empty_uid: true) + + subject.execute([shibboleth_config]) + end + + it 'configures remote_sign_out_handler proc for authentiq' do + authentiq_config = { 'name' => 'authentiq', 'args' => {} } + + expect(devise_config).to receive(:omniauth).with(:authentiq, remote_sign_out_handler: an_instance_of(Proc)) + + subject.execute([authentiq_config]) + end + + it 'configures on_single_sign_out proc for cas3' do + cas3_config = { 'name' => 'cas3', 'args' => {} } + + expect(devise_config).to receive(:omniauth).with(:cas3, on_single_sign_out: an_instance_of(Proc)) + + subject.execute([cas3_config]) + end + end +end diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb index f02b1cf55fb..548eb28fe4d 100644 --- a/spec/lib/gitlab/profiler_spec.rb +++ b/spec/lib/gitlab/profiler_spec.rb @@ -94,10 +94,12 @@ describe Gitlab::Profiler do it 'strips out the private token' do expect(custom_logger).to receive(:add) do |severity, _progname, message| + next if message.include?('spec/') + expect(severity).to eq(Logger::DEBUG) expect(message).to include('public').and include(described_class::FILTERED_STRING) expect(message).not_to include(private_token) - end + end.twice custom_logger.debug("public #{private_token}") end @@ -108,8 +110,8 @@ describe Gitlab::Profiler do custom_logger.debug('User Load (1.3ms)') custom_logger.debug('Project Load (10.4ms)') - expect(custom_logger.load_times_by_model).to eq('User' => 2.5, - 'Project' => 10.4) + expect(custom_logger.load_times_by_model).to eq('User' => [1.2, 1.3], + 'Project' => [10.4]) end it 'logs the backtrace, ignoring lines as appropriate' do @@ -162,4 +164,24 @@ describe Gitlab::Profiler do end end end + + describe '.log_load_times_by_model' do + it 'logs the model, query count, and time by slowest first' do + expect(null_logger).to receive(:load_times_by_model).and_return( + 'User' => [1.2, 1.3], + 'Project' => [10.4] + ) + + expect(null_logger).to receive(:info).with('Project total (1): 10.4ms') + expect(null_logger).to receive(:info).with('User total (2): 2.5ms') + + described_class.log_load_times_by_model(null_logger) + end + + it 'does nothing when called with a logger that does not have load times' do + expect(null_logger).not_to receive(:info) + + expect(described_class.log_load_times_by_model(null_logger)).to be_nil + end + end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index c46bb8edebf..8351b967133 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -83,19 +83,19 @@ describe Gitlab::ProjectSearchResults do end context 'when the matching filename contains a colon' do - let(:search_result) { "\nmaster:testdata/project::function1.yaml\x001\x00---\n" } + let(:search_result) { "master:testdata/project::function1.yaml\x001\x00---\n" } it 'returns a valid FoundBlob' do expect(subject.filename).to eq('testdata/project::function1.yaml') expect(subject.basename).to eq('testdata/project::function1') expect(subject.ref).to eq('master') expect(subject.startline).to eq(1) - expect(subject.data).to eq('---') + expect(subject.data).to eq("---\n") end end context 'when the matching content contains a number surrounded by colons' do - let(:search_result) { "\nmaster:testdata/foo.txt\x001\x00blah:9:blah" } + let(:search_result) { "master:testdata/foo.txt\x001\x00blah:9:blah" } it 'returns a valid FoundBlob' do expect(subject.filename).to eq('testdata/foo.txt') @@ -106,16 +106,40 @@ describe Gitlab::ProjectSearchResults do end end + context 'when the search result ends with an empty line' do + let(:results) { project.repository.search_files_by_content('Role models', 'master') } + + it 'returns a valid FoundBlob that ends with an empty line' do + expect(subject.filename).to eq('files/markdown/ruby-style-guide.md') + expect(subject.basename).to eq('files/markdown/ruby-style-guide') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(1) + expect(subject.data).to eq("# Prelude\n\n> Role models are important. <br/>\n> -- Officer Alex J. Murphy / RoboCop\n\n") + end + end + context 'when the search returns non-ASCII data' do context 'with UTF-8' do - let(:results) { project.repository.search_files_by_content("файл", 'master') } + let(:results) { project.repository.search_files_by_content('файл', 'master') } it 'returns results as UTF-8' do expect(subject.filename).to eq('encoding/russian.rb') expect(subject.basename).to eq('encoding/russian') expect(subject.ref).to eq('master') expect(subject.startline).to eq(1) - expect(subject.data).to eq("Хороший файл") + expect(subject.data).to eq("Хороший файл\n") + end + end + + context 'with UTF-8 in the filename' do + let(:results) { project.repository.search_files_by_content('webhook', 'master') } + + it 'returns results as UTF-8' do + expect(subject.filename).to eq('encoding/テスト.txt') + expect(subject.basename).to eq('encoding/テスト') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(3) + expect(subject.data).to include('WebHookの確認') end end @@ -127,7 +151,7 @@ describe Gitlab::ProjectSearchResults do expect(subject.basename).to eq('encoding/iso8859') expect(subject.ref).to eq('master') expect(subject.startline).to eq(1) - expect(subject.data).to eq("Äü\n\nfoo") + expect(subject.data).to eq("Äü\n\nfoo\n") end end end diff --git a/spec/lib/gitlab/project_transfer_spec.rb b/spec/lib/gitlab/project_transfer_spec.rb index 10c5fb148cd..0b9b1f537b5 100644 --- a/spec/lib/gitlab/project_transfer_spec.rb +++ b/spec/lib/gitlab/project_transfer_spec.rb @@ -21,30 +21,77 @@ describe Gitlab::ProjectTransfer do describe '#move_project' do it "moves project upload to another namespace" do - FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + path_to_be_moved = File.join(@root_dir, @namespace_path_was, @project_path) + expected_path = File.join(@root_dir, @namespace_path, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + @project_transfer.move_project(@project_path, @namespace_path_was, @namespace_path) - expected_path = File.join(@root_dir, @namespace_path, @project_path) expect(Dir.exist?(expected_path)).to be_truthy end end + describe '#move_namespace' do + context 'when moving namespace from root into another namespace' do + it "moves namespace projects' upload" do + child_namespace = 'test_child_namespace' + path_to_be_moved = File.join(@root_dir, child_namespace, @project_path) + expected_path = File.join(@root_dir, @namespace_path, child_namespace, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + + @project_transfer.move_namespace(child_namespace, nil, @namespace_path) + + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + context 'when moving namespace from one parent to another' do + it "moves namespace projects' upload" do + child_namespace = 'test_child_namespace' + path_to_be_moved = File.join(@root_dir, @namespace_path_was, child_namespace, @project_path) + expected_path = File.join(@root_dir, @namespace_path, child_namespace, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + + @project_transfer.move_namespace(child_namespace, @namespace_path_was, @namespace_path) + + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + context 'when moving namespace from having a parent to root' do + it "moves namespace projects' upload" do + child_namespace = 'test_child_namespace' + path_to_be_moved = File.join(@root_dir, @namespace_path_was, child_namespace, @project_path) + expected_path = File.join(@root_dir, child_namespace, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + + @project_transfer.move_namespace(child_namespace, @namespace_path_was, nil) + + expect(Dir.exist?(expected_path)).to be_truthy + end + end + end + describe '#rename_project' do it "renames project" do - FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was)) + path_to_be_moved = File.join(@root_dir, @namespace_path, @project_path_was) + expected_path = File.join(@root_dir, @namespace_path, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + @project_transfer.rename_project(@project_path_was, @project_path, @namespace_path) - expected_path = File.join(@root_dir, @namespace_path, @project_path) expect(Dir.exist?(expected_path)).to be_truthy end end describe '#rename_namespace' do it "renames namespace" do - FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + path_to_be_moved = File.join(@root_dir, @namespace_path_was, @project_path) + expected_path = File.join(@root_dir, @namespace_path, @project_path) + FileUtils.mkdir_p(path_to_be_moved) + @project_transfer.rename_namespace(@namespace_path_was, @namespace_path) - expected_path = File.join(@root_dir, @namespace_path, @project_path) expect(Dir.exist?(expected_path)).to be_truthy end end diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb new file mode 100644 index 00000000000..85971f2a7ef --- /dev/null +++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Gitlab::RepositoryCacheAdapter do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:cache) { repository.send(:cache) } + + describe '#cache_method_output', :use_clean_rails_memory_store_caching do + let(:fallback) { 10 } + + context 'with a non-existing repository' do + let(:project) { create(:project) } # No repository + + subject do + repository.cache_method_output(:cats, fallback: fallback) do + repository.cats_call_stub + end + end + + it 'returns the fallback value' do + expect(subject).to eq(fallback) + end + + it 'avoids calling the original method' do + expect(repository).not_to receive(:cats_call_stub) + + subject + end + end + + context 'with a method throwing a non-existing-repository error' do + subject do + repository.cache_method_output(:cats, fallback: fallback) do + raise Gitlab::Git::Repository::NoRepository + end + end + + it 'returns the fallback value' do + expect(subject).to eq(fallback) + end + + it 'does not cache the data' do + subject + + expect(repository.instance_variable_defined?(:@cats)).to eq(false) + expect(cache.exist?(:cats)).to eq(false) + end + end + + context 'with an existing repository' do + it 'caches the output' do + object = double + + expect(object).to receive(:number).once.and_return(10) + + 2.times do + val = repository.cache_method_output(:cats) { object.number } + + expect(val).to eq(10) + end + + expect(repository.send(:cache).exist?(:cats)).to eq(true) + expect(repository.instance_variable_get(:@cats)).to eq(10) + end + end + end + + describe '#expire_method_caches' do + it 'expires the caches of the given methods' do + expect(cache).to receive(:expire).with(:readme) + expect(cache).to receive(:expire).with(:gitignore) + + repository.expire_method_caches(%i(readme gitignore)) + end + end +end diff --git a/spec/lib/gitlab/repository_cache_spec.rb b/spec/lib/gitlab/repository_cache_spec.rb new file mode 100644 index 00000000000..fc259cf1208 --- /dev/null +++ b/spec/lib/gitlab/repository_cache_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::RepositoryCache do + let(:backend) { double('backend').as_null_object } + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:namespace) { "#{repository.full_path}:#{project.id}" } + let(:cache) { described_class.new(repository, backend: backend) } + + describe '#cache_key' do + subject { cache.cache_key(:foo) } + + it 'includes the namespace' do + expect(subject).to eq "foo:#{namespace}" + end + + context 'with a given namespace' do + let(:extra_namespace) { 'my:data' } + let(:cache) do + described_class.new(repository, extra_namespace: extra_namespace, + backend: backend) + end + + it 'includes the full namespace' do + expect(subject).to eq "foo:#{namespace}:#{extra_namespace}" + end + end + end + + describe '#expire' do + it 'expires the given key from the cache' do + cache.expire(:foo) + expect(backend).to have_received(:delete).with("foo:#{namespace}") + end + end + + describe '#fetch' do + it 'fetches the given key from the cache' do + cache.fetch(:bar) + expect(backend).to have_received(:fetch).with("bar:#{namespace}") + end + + it 'accepts a block' do + p = -> {} + + cache.fetch(:baz, &p) + expect(backend).to have_received(:fetch).with("baz:#{namespace}", &p) + end + end +end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 56b45d8da3c..14b59c5e945 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Shell do it { is_expected.to respond_to :add_key } it { is_expected.to respond_to :remove_key } - it { is_expected.to respond_to :add_repository } + it { is_expected.to respond_to :create_repository } it { is_expected.to respond_to :remove_repository } it { is_expected.to respond_to :fork_repository } @@ -402,8 +402,8 @@ describe Gitlab::Shell do allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) end - describe '#add_repository' do - shared_examples '#add_repository' do + describe '#create_repository' do + shared_examples '#create_repository' do let(:repository_storage) { 'default' } let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } let(:repo_name) { 'project/path' } @@ -414,7 +414,7 @@ describe Gitlab::Shell do end it 'creates a repository' do - expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_truthy + expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy expect(File.stat(created_path).mode & 0o777).to eq(0o770) @@ -426,19 +426,19 @@ describe Gitlab::Shell do it 'returns false when the command fails' do FileUtils.mkdir_p(File.dirname(created_path)) # This file will block the creation of the repo's .git directory. That - # should cause #add_repository to fail. + # should cause #create_repository to fail. FileUtils.touch(created_path) - expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_falsy + expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy end end context 'with gitaly' do - it_behaves_like '#add_repository' + it_behaves_like '#create_repository' end context 'without gitaly', :skip_gitaly_mock do - it_behaves_like '#add_repository' + it_behaves_like '#create_repository' end end diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb index e3447d974aa..194cae8c645 100644 --- a/spec/lib/gitlab/slash_commands/command_spec.rb +++ b/spec/lib/gitlab/slash_commands/command_spec.rb @@ -108,5 +108,10 @@ describe Gitlab::SlashCommands::Command do it { is_expected.to eq(Gitlab::SlashCommands::IssueSearch) } end + + context 'IssueMove is triggered' do + let(:params) { { text: 'issue move #78291 to gitlab/gitlab-ci' } } + it { is_expected.to eq(Gitlab::SlashCommands::IssueMove) } + end end end diff --git a/spec/lib/gitlab/slash_commands/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/issue_move_spec.rb new file mode 100644 index 00000000000..d41441c9472 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/issue_move_spec.rb @@ -0,0 +1,117 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::IssueMove, service: true do + describe '#match' do + shared_examples_for 'move command' do |text_command| + it 'can be parsed to extract the needed fields' do + match_data = described_class.match(text_command) + + expect(match_data['iid']).to eq('123456') + expect(match_data['project_path']).to eq('gitlab/gitlab-ci') + end + end + + it_behaves_like 'move command', 'issue move #123456 to gitlab/gitlab-ci' + it_behaves_like 'move command', 'issue move #123456 gitlab/gitlab-ci' + it_behaves_like 'move command', 'issue move #123456 gitlab/gitlab-ci ' + it_behaves_like 'move command', 'issue move 123456 to gitlab/gitlab-ci' + it_behaves_like 'move command', 'issue move 123456 gitlab/gitlab-ci' + it_behaves_like 'move command', 'issue move 123456 gitlab/gitlab-ci ' + end + + describe '#execute' do + set(:user) { create(:user) } + set(:issue) { create(:issue) } + set(:chat_name) { create(:chat_name, user: user) } + set(:project) { issue.project } + set(:other_project) { create(:project, namespace: project.namespace) } + + before do + [project, other_project].each { |prj| prj.add_master(user) } + end + + subject { described_class.new(project, chat_name) } + + def process_message(message) + subject.execute(described_class.match(message)) + end + + context 'when the user can move the issue' do + context 'when the move fails' do + it 'returns the error message' do + message = "issue move #{issue.iid} #{project.full_path}" + + expect(process_message(message)).to include(response_type: :ephemeral, + text: a_string_matching('Cannot move issue')) + end + end + + context 'when the move succeeds' do + let(:message) { "issue move #{issue.iid} #{other_project.full_path}" } + + it 'moves the issue to the new destination' do + expect { process_message(message) }.to change { Issue.count }.by(1) + + new_issue = issue.reload.moved_to + + expect(new_issue.state).to eq('opened') + expect(new_issue.project_id).to eq(other_project.id) + expect(new_issue.author_id).to eq(issue.author_id) + + expect(issue.state).to eq('closed') + expect(issue.project_id).to eq(project.id) + end + + it 'returns the new issue' do + expect(process_message(message)) + .to include(response_type: :in_channel, + attachments: [a_hash_including(title_link: a_string_including(other_project.full_path))]) + end + + it 'mentions the old issue' do + expect(process_message(message)) + .to include(attachments: [a_hash_including(pretext: a_string_including(project.full_path))]) + end + end + end + + context 'when the issue does not exist' do + it 'returns not found' do + message = "issue move #{issue.iid.succ} #{other_project.full_path}" + + expect(process_message(message)).to include(response_type: :ephemeral, + text: a_string_matching('not found')) + end + end + + context 'when the target project does not exist' do + it 'returns not found' do + message = "issue move #{issue.iid} #{other_project.full_path}/foo" + + expect(process_message(message)).to include(response_type: :ephemeral, + text: a_string_matching('not found')) + end + end + + context 'when the user cannot see the target project' do + it 'returns not found' do + message = "issue move #{issue.iid} #{other_project.full_path}" + other_project.team.truncate + + expect(process_message(message)).to include(response_type: :ephemeral, + text: a_string_matching('not found')) + end + end + + context 'when the user does not have the required permissions on the target project' do + it 'returns the error message' do + message = "issue move #{issue.iid} #{other_project.full_path}" + other_project.team.truncate + other_project.team.add_guest(user) + + expect(process_message(message)).to include(response_type: :ephemeral, + text: a_string_matching('Cannot move issue')) + end + end + end +end diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_move_spec.rb new file mode 100644 index 00000000000..58c341a284e --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/issue_move_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Gitlab::SlashCommands::Presenters::IssueMove do + set(:admin) { create(:admin) } + set(:project) { create(:project) } + set(:other_project) { create(:project) } + set(:old_issue) { create(:issue, project: project) } + set(:new_issue) { Issues::MoveService.new(project, admin).execute(old_issue, other_project) } + let(:attachment) { subject[:attachments].first } + + subject { described_class.new(new_issue).present(old_issue) } + + it { is_expected.to be_a(Hash) } + + it 'shows the new issue' do + expect(subject[:response_type]).to be(:in_channel) + expect(subject).to have_key(:attachments) + expect(attachment[:title]).to start_with(new_issue.title) + expect(attachment[:title_link]).to include(other_project.full_path) + end + + it 'mentions the old issue and the new issue in the pretext' do + expect(attachment[:pretext]).to include(project.full_path) + expect(attachment[:pretext]).to include(other_project.full_path) + end +end diff --git a/spec/lib/gitlab/verify/job_artifacts_spec.rb b/spec/lib/gitlab/verify/job_artifacts_spec.rb new file mode 100644 index 00000000000..ec490bdfde2 --- /dev/null +++ b/spec/lib/gitlab/verify/job_artifacts_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Gitlab::Verify::JobArtifacts do + include GitlabVerifyHelpers + + it_behaves_like 'Gitlab::Verify::BatchVerifier subclass' do + let!(:objects) { create_list(:ci_job_artifact, 3, :archive) } + end + + describe '#run_batches' do + let(:failures) { collect_failures } + let(:failure) { failures[artifact] } + + let!(:artifact) { create(:ci_job_artifact, :archive, :correct_checksum) } + + it 'passes artifacts with the correct file' do + expect(failures).to eq({}) + end + + it 'fails artifacts with a missing file' do + FileUtils.rm_f(artifact.file.path) + + expect(failures.keys).to contain_exactly(artifact) + expect(failure).to be_a(Errno::ENOENT) + expect(failure.to_s).to include(artifact.file.path) + end + + it 'fails artifacts with a mismatched checksum' do + File.truncate(artifact.file.path, 0) + + expect(failures.keys).to contain_exactly(artifact) + expect(failure.to_s).to include('Checksum mismatch') + end + end +end diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb deleted file mode 100644 index 8b0c7254b5e..00000000000 --- a/spec/lib/repository_cache_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -describe RepositoryCache do - let(:project) { create(:project) } - let(:backend) { double('backend').as_null_object } - let(:cache) { described_class.new('example', project.id, backend) } - - describe '#cache_key' do - it 'includes the namespace' do - expect(cache.cache_key(:foo)).to eq "foo:example:#{project.id}" - end - end - - describe '#expire' do - it 'expires the given key from the cache' do - cache.expire(:foo) - expect(backend).to have_received(:delete).with("foo:example:#{project.id}") - end - end - - describe '#fetch' do - it 'fetches the given key from the cache' do - cache.fetch(:bar) - expect(backend).to have_received(:fetch).with("bar:example:#{project.id}") - end - - it 'accepts a block' do - p = -> {} - - cache.fetch(:baz, &p) - expect(backend).to have_received(:fetch).with("baz:example:#{project.id}", &p) - end - end -end |