diff options
Diffstat (limited to 'spec/models')
-rw-r--r-- | spec/models/blob_spec.rb | 543 | ||||
-rw-r--r-- | spec/models/commit_spec.rb | 229 | ||||
-rw-r--r-- | spec/models/concerns/redis_cacheable_spec.rb | 4 | ||||
-rw-r--r-- | spec/models/personal_snippet_spec.rb | 9 | ||||
-rw-r--r-- | spec/models/project_snippet_spec.rb | 9 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 19 | ||||
-rw-r--r-- | spec/models/repository_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/snippet_repository_spec.rb | 23 | ||||
-rw-r--r-- | spec/models/snippet_spec.rb | 106 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 2 |
10 files changed, 800 insertions, 146 deletions
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb index 7099d000d4c..a0193b29bb3 100644 --- a/spec/models/blob_spec.rb +++ b/spec/models/blob_spec.rb @@ -6,6 +6,8 @@ describe Blob do include FakeBlobHelpers let(:project) { build(:project, lfs_enabled: true) } + let(:personal_snippet) { build(:personal_snippet) } + let(:project_snippet) { build(:project_snippet, project: project) } before do allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) @@ -18,77 +20,146 @@ describe Blob do end describe '.lazy' do - let(:project) { create(:project, :repository) } - let(:same_project) { Project.find(project.id) } - let(:other_project) { create(:project, :repository) } let(:commit_id) { 'e63f41fe459e62e1228fcef60d7189127aeba95a' } let(:blob_size_limit) { 10 * 1024 * 1024 } - it 'does not fetch blobs when none are accessed' do - expect(project.repository).not_to receive(:blobs_at) + shared_examples '.lazy checks' do + it 'does not fetch blobs when none are accessed' do + expect(container.repository).not_to receive(:blobs_at) - described_class.lazy(project, commit_id, 'CHANGELOG') - end + described_class.lazy(container, commit_id, 'CHANGELOG') + end + + it 'fetches all blobs for the same repository when one is accessed' do + expect(container.repository).to receive(:blobs_at) + .with([[commit_id, 'CHANGELOG'], [commit_id, 'CONTRIBUTING.md']], blob_size_limit: blob_size_limit) + .once.and_call_original + expect(other_container.repository).not_to receive(:blobs_at) + + changelog = described_class.lazy(container, commit_id, 'CHANGELOG') + contributing = described_class.lazy(same_container, commit_id, 'CONTRIBUTING.md') + + described_class.lazy(other_container, commit_id, 'CHANGELOG') + + # Access property so the values are loaded + changelog.id + contributing.id + end + + it 'does not include blobs from previous requests in later requests' do + changelog = described_class.lazy(container, commit_id, 'CHANGELOG') + contributing = described_class.lazy(same_container, commit_id, 'CONTRIBUTING.md') - it 'fetches all blobs for the same repository when one is accessed' do - expect(project.repository).to receive(:blobs_at) - .with([[commit_id, 'CHANGELOG'], [commit_id, 'CONTRIBUTING.md']], blob_size_limit: blob_size_limit) - .once.and_call_original - expect(other_project.repository).not_to receive(:blobs_at) + # Access property so the values are loaded + changelog.id + contributing.id - changelog = described_class.lazy(project, commit_id, 'CHANGELOG') - contributing = described_class.lazy(same_project, commit_id, 'CONTRIBUTING.md') + readme = described_class.lazy(container, commit_id, 'README.md') - described_class.lazy(other_project, commit_id, 'CHANGELOG') + expect(container.repository).to receive(:blobs_at) + .with([[commit_id, 'README.md']], blob_size_limit: blob_size_limit).once.and_call_original - # Access property so the values are loaded - changelog.id - contributing.id + readme.id + end end - it 'does not include blobs from previous requests in later requests' do - changelog = described_class.lazy(project, commit_id, 'CHANGELOG') - contributing = described_class.lazy(same_project, commit_id, 'CONTRIBUTING.md') + context 'with project' do + let(:container) { create(:project, :repository) } + let(:same_container) { Project.find(container.id) } + let(:other_container) { create(:project, :repository) } - # Access property so the values are loaded - changelog.id - contributing.id + it_behaves_like '.lazy checks' + end + + context 'with personal snippet' do + let(:container) { create(:personal_snippet, :repository) } + let(:same_container) { PersonalSnippet.find(container.id) } + let(:other_container) { create(:personal_snippet, :repository) } - readme = described_class.lazy(project, commit_id, 'README.md') + it_behaves_like '.lazy checks' + end - expect(project.repository).to receive(:blobs_at) - .with([[commit_id, 'README.md']], blob_size_limit: blob_size_limit).once.and_call_original + context 'with project snippet' do + let(:container) { create(:project_snippet, :repository) } + let(:same_container) { ProjectSnippet.find(container.id) } + let(:other_container) { create(:project_snippet, :repository) } - readme.id + it_behaves_like '.lazy checks' end end describe '#data' do - context 'using a binary blob' do - it 'returns the data as-is' do - data = "\n\xFF\xB9\xC3" - blob = fake_blob(binary: true, data: data) + shared_examples '#data checks' do + context 'using a binary blob' do + it 'returns the data as-is' do + data = "\n\xFF\xB9\xC3" + blob = fake_blob(binary: true, data: data, container: container) - expect(blob.data).to eq(data) + expect(blob.data).to eq(data) + end end - end - context 'using a text blob' do - it 'converts the data to UTF-8' do - blob = fake_blob(binary: false, data: "\n\xFF\xB9\xC3") + context 'using a text blob' do + it 'converts the data to UTF-8' do + blob = fake_blob(binary: false, data: "\n\xFF\xB9\xC3", container: container) - expect(blob.data).to eq("\n���") + expect(blob.data).to eq("\n���") + end end end + + context 'with project' do + let(:container) { project } + + it_behaves_like '#data checks' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like '#data checks' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like '#data checks' + end end describe '#external_storage_error?' do + shared_examples 'no error' do + it do + expect(blob.external_storage_error?).to be_falsey + end + end + + shared_examples 'returns error' do + it do + expect(blob.external_storage_error?).to be_truthy + end + end + context 'if the blob is stored in LFS' do - let(:blob) { fake_blob(path: 'file.pdf', lfs: true) } + let(:blob) { fake_blob(path: 'file.pdf', lfs: true, container: container) } context 'when the project has LFS enabled' do - it 'returns false' do - expect(blob.external_storage_error?).to be_falsey + context 'with project' do + let(:container) { project } + + it_behaves_like 'no error' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns error' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'no error' end end @@ -97,17 +168,39 @@ describe Blob do project.lfs_enabled = false end - it 'returns true' do - expect(blob.external_storage_error?).to be_truthy + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns error' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns error' end end end context 'if the blob is not stored in LFS' do - let(:blob) { fake_blob(path: 'file.md') } + let(:blob) { fake_blob(path: 'file.md', container: container) } - it 'returns false' do - expect(blob.external_storage_error?).to be_falsey + context 'with project' do + let(:container) { project } + + it_behaves_like 'no error' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'no error' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'no error' end end end @@ -116,19 +209,59 @@ describe Blob do context 'if the blob is stored in LFS' do let(:blob) { fake_blob(path: 'file.pdf', lfs: true) } - context 'when the project has LFS enabled' do - it 'returns true' do + shared_examples 'returns true' do + it do expect(blob.stored_externally?).to be_truthy end end + shared_examples 'returns false' do + it do + expect(blob.stored_externally?).to be_falsey + end + end + + context 'when the project has LFS enabled' do + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' + end + end + context 'when the project does not have LFS enabled' do before do project.lfs_enabled = false end - it 'returns false' do - expect(blob.stored_externally?).to be_falsey + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns false' end end end @@ -143,21 +276,63 @@ describe Blob do end describe '#binary?' do + shared_examples 'returns true' do + it do + expect(blob.binary?).to be_truthy + end + end + + shared_examples 'returns false' do + it do + expect(blob.binary?).to be_falsey + end + end + context 'if the blob is stored externally' do + let(:blob) { fake_blob(path: file, lfs: true) } + context 'if the extension has a rich viewer' do context 'if the viewer is binary' do - it 'returns true' do - blob = fake_blob(path: 'file.pdf', lfs: true) + let(:file) { 'file.pdf' } - expect(blob.binary?).to be_truthy + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' end end context 'if the viewer is text-based' do - it 'return false' do - blob = fake_blob(path: 'file.md', lfs: true) + let(:file) { 'file.md' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } - expect(blob.binary?).to be_falsey + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns false' end end end @@ -165,54 +340,138 @@ describe Blob do context "if the extension doesn't have a rich viewer" do context 'if the extension has a text mime type' do context 'if the extension is for a programming language' do - it 'returns false' do - blob = fake_blob(path: 'file.txt', lfs: true) + let(:file) { 'file.txt' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end - expect(blob.binary?).to be_falsey + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns false' end end context 'if the extension is not for a programming language' do - it 'returns false' do - blob = fake_blob(path: 'file.ics', lfs: true) + let(:file) { 'file.ics' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.binary?).to be_falsey + it_behaves_like 'returns false' end end end context 'if the extension has a binary mime type' do context 'if the extension is for a programming language' do - it 'returns false' do - blob = fake_blob(path: 'file.rb', lfs: true) + let(:file) { 'file.rb' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.binary?).to be_falsey + it_behaves_like 'returns false' end end context 'if the extension is not for a programming language' do - it 'returns true' do - blob = fake_blob(path: 'file.exe', lfs: true) + let(:file) { 'file.exe' } - expect(blob.binary?).to be_truthy + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' end end end context 'if the extension has an unknown mime type' do context 'if the extension is for a programming language' do - it 'returns false' do - blob = fake_blob(path: 'file.ini', lfs: true) + let(:file) { 'file.ini' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.binary?).to be_falsey + it_behaves_like 'returns false' end end context 'if the extension is not for a programming language' do - it 'returns true' do - blob = fake_blob(path: 'file.wtf', lfs: true) + let(:file) { 'file.wtf' } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } - expect(blob.binary?).to be_truthy + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' end end end @@ -221,18 +480,46 @@ describe Blob do context 'if the blob is not stored externally' do context 'if the blob is binary' do - it 'returns true' do - blob = fake_blob(path: 'file.pdf', binary: true) + let(:blob) { fake_blob(path: 'file.pdf', binary: true, container: container) } - expect(blob.binary?).to be_truthy + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' end end context 'if the blob is text-based' do - it 'return false' do - blob = fake_blob(path: 'file.md') + let(:blob) { fake_blob(path: 'file.md', container: container) } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.binary?).to be_falsey + it_behaves_like 'returns false' end end end @@ -389,38 +676,110 @@ describe Blob do end describe '#rendered_as_text?' do + shared_examples 'returns true' do + it do + expect(blob.rendered_as_text?(ignore_errors: ignore_errors)).to be_truthy + end + end + + shared_examples 'returns false' do + it do + expect(blob.rendered_as_text?(ignore_errors: ignore_errors)).to be_falsey + end + end + context 'when ignoring errors' do + let(:ignore_errors) { true } + context 'when the simple viewer is text-based' do - it 'returns true' do - blob = fake_blob(path: 'file.md', size: 100.megabytes) + let(:blob) { fake_blob(path: 'file.md', size: 100.megabytes, container: container) } - expect(blob.rendered_as_text?).to be_truthy + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns true' end end context 'when the simple viewer is binary' do - it 'returns false' do - blob = fake_blob(path: 'file.pdf', binary: true, size: 100.megabytes) + let(:blob) { fake_blob(path: 'file.pdf', binary: true, size: 100.megabytes, container: container) } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.rendered_as_text?).to be_falsey + it_behaves_like 'returns false' end end end context 'when not ignoring errors' do + let(:ignore_errors) { false } + context 'when the viewer has render errors' do - it 'returns false' do - blob = fake_blob(path: 'file.md', size: 100.megabytes) + let(:blob) { fake_blob(path: 'file.md', size: 100.megabytes, container: container) } - expect(blob.rendered_as_text?(ignore_errors: false)).to be_falsey + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns false' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns false' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns false' end end context "when the viewer doesn't have render errors" do - it 'returns true' do - blob = fake_blob(path: 'file.md') + let(:blob) { fake_blob(path: 'file.md', container: container) } + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns true' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns true' + end + + context 'with project snippet' do + let(:container) { project_snippet } - expect(blob.rendered_as_text?(ignore_errors: false)).to be_truthy + it_behaves_like 'returns true' end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index ada25005064..26cc68eb58c 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -3,8 +3,10 @@ require 'spec_helper' describe Commit do - let(:project) { create(:project, :public, :repository) } - let(:commit) { project.commit } + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:personal_snippet) { create(:personal_snippet, :repository) } + let_it_be(:project_snippet) { create(:project_snippet, :repository) } + let(:commit) { project.commit } describe 'modules' do subject { described_class } @@ -17,49 +19,67 @@ describe Commit do end describe '.lazy' do - let_it_be(:project) { create(:project, :repository) } + shared_examples '.lazy checks' do + context 'when the commits are found' do + let(:oids) do + %w( + 498214de67004b1da3d820901307bed2a68a8ef6 + c642fe9b8b9f28f9225d7ea953fe14e74748d53b + 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9 + 048721d90c449b244b7b4c53a9186b04330174ec + 281d3a76f31c812dbf48abce82ccf6860adedd81 + ) + end - context 'when the commits are found' do - let(:oids) do - %w( - 498214de67004b1da3d820901307bed2a68a8ef6 - c642fe9b8b9f28f9225d7ea953fe14e74748d53b - 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9 - 048721d90c449b244b7b4c53a9186b04330174ec - 281d3a76f31c812dbf48abce82ccf6860adedd81 - ) - end + subject { oids.map { |oid| described_class.lazy(container, oid) } } - subject { oids.map { |oid| described_class.lazy(project, oid) } } + it 'batches requests for commits' do + expect(container.repository).to receive(:commits_by).once.and_call_original - it 'batches requests for commits' do - expect(project.repository).to receive(:commits_by).once.and_call_original + subject.first.title + subject.last.title + end - subject.first.title - subject.last.title - end + it 'maintains ordering' do + subject.each_with_index do |commit, i| + expect(commit.id).to eq(oids[i]) + end + end - it 'maintains ordering' do - subject.each_with_index do |commit, i| - expect(commit.id).to eq(oids[i]) + it 'does not attempt to replace methods via BatchLoader' do + subject.each do |commit| + expect(commit).to receive(:method_missing).and_call_original + + commit.id + end end end - it 'does not attempt to replace methods via BatchLoader' do - subject.each do |commit| - expect(commit).to receive(:method_missing).and_call_original + context 'when not found' do + it 'returns nil as commit' do + commit = described_class.lazy(container, 'deadbeef').__sync - commit.id + expect(commit).to be_nil end end end - context 'when not found' do - it 'returns nil as commit' do - commit = described_class.lazy(project, 'deadbeef').__sync + context 'with project' do + let(:container) { project } - expect(commit).to be_nil - end + it_behaves_like '.lazy checks' + end + + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like '.lazy checks' + end + + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like '.lazy checks' end end @@ -231,15 +251,43 @@ describe Commit do end describe '#to_reference' do - let(:project) { create(:project, :repository, path: 'sample-project') } + context 'with project' do + let(:project) { create(:project, :repository, path: 'sample-project') } + + it 'returns a String reference to the object' do + expect(commit.to_reference).to eq commit.id + end - it 'returns a String reference to the object' do - expect(commit.to_reference).to eq commit.id + it 'supports a cross-project reference' do + another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace) + expect(commit.to_reference(another_project)).to eq "sample-project@#{commit.id}" + end end - it 'supports a cross-project reference' do - another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace) - expect(commit.to_reference(another_project)).to eq "sample-project@#{commit.id}" + context 'with personal snippet' do + let(:commit) { personal_snippet.commit } + + it 'returns a String reference to the object' do + expect(commit.to_reference).to eq "$#{personal_snippet.id}@#{commit.id}" + end + + it 'supports a cross-snippet reference' do + another_snippet = build(:personal_snippet) + expect(commit.to_reference(another_snippet)).to eq "$#{personal_snippet.id}@#{commit.id}" + end + end + + context 'with project snippet' do + let(:commit) { project_snippet.commit } + + it 'returns a String reference to the object' do + expect(commit.to_reference).to eq "$#{project_snippet.id}@#{commit.id}" + end + + it 'supports a cross-snippet project reference' do + another_snippet = build(:personal_snippet) + expect(commit.to_reference(another_snippet)).to eq "#{project_snippet.project.path}$#{project_snippet.id}@#{commit.id}" + end end end @@ -264,13 +312,41 @@ describe Commit do describe '#reference_link_text' do let(:project) { create(:project, :repository, path: 'sample-project') } - it 'returns a String reference to the object' do - expect(commit.reference_link_text).to eq commit.short_id + context 'with project' do + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq commit.short_id + end + + it 'supports a cross-project reference' do + another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace) + expect(commit.reference_link_text(another_project)).to eq "sample-project@#{commit.short_id}" + end + end + + context 'with personal snippet' do + let(:commit) { personal_snippet.commit } + + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq "$#{personal_snippet.id}@#{commit.short_id}" + end + + it 'supports a cross-snippet reference' do + another_snippet = build(:personal_snippet, :repository) + expect(commit.reference_link_text(another_snippet)).to eq "$#{personal_snippet.id}@#{commit.short_id}" + end end - it 'supports a cross-project reference' do - another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace) - expect(commit.reference_link_text(another_project)).to eq "sample-project@#{commit.short_id}" + context 'with project snippet' do + let(:commit) { project_snippet.commit } + + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq "$#{project_snippet.id}@#{commit.short_id}" + end + + it 'supports a cross-snippet project reference' do + another_snippet = build(:project_snippet, :repository) + expect(commit.reference_link_text(another_snippet)).to eq "#{project_snippet.project.path}$#{project_snippet.id}@#{commit.short_id}" + end end end @@ -401,6 +477,26 @@ eos expect(commit.closes_issues).to be_empty end + + context 'with personal snippet' do + let(:commit) { personal_snippet.commit } + + it 'does not call Gitlab::ClosingIssueExtractor' do + expect(Gitlab::ClosingIssueExtractor).not_to receive(:new) + + commit.closes_issues + end + end + + context 'with project snippet' do + let(:commit) { project_snippet.commit } + + it 'does not call Gitlab::ClosingIssueExtractor' do + expect(Gitlab::ClosingIssueExtractor).not_to receive(:new) + + commit.closes_issues + end + end end it_behaves_like 'a mentionable' do @@ -597,19 +693,39 @@ eos end describe '.from_hash' do - let(:new_commit) { described_class.from_hash(commit.to_hash, project) } + subject { described_class.from_hash(commit.to_hash, container) } - it 'returns a Commit' do - expect(new_commit).to be_an_instance_of(described_class) + shared_examples 'returns Commit' do + it 'returns a Commit' do + expect(subject).to be_an_instance_of(described_class) + end + + it 'wraps a Gitlab::Git::Commit' do + expect(subject.raw).to be_an_instance_of(Gitlab::Git::Commit) + end + + it 'stores the correct commit fields' do + expect(subject.id).to eq(commit.id) + expect(subject.message).to eq(commit.message) + end + end + + context 'with project' do + let(:container) { project } + + it_behaves_like 'returns Commit' end - it 'wraps a Gitlab::Git::Commit' do - expect(new_commit.raw).to be_an_instance_of(Gitlab::Git::Commit) + context 'with personal snippet' do + let(:container) { personal_snippet } + + it_behaves_like 'returns Commit' end - it 'stores the correct commit fields' do - expect(new_commit.id).to eq(commit.id) - expect(new_commit.message).to eq(commit.message) + context 'with project snippet' do + let(:container) { project_snippet } + + it_behaves_like 'returns Commit' end end @@ -670,6 +786,19 @@ eos expect(commit1.merge_requests).to contain_exactly(merge_request1, merge_request2) expect(commit2.merge_requests).to contain_exactly(merge_request1) end + + context 'with personal snippet' do + it 'returns empty relation' do + expect(personal_snippet.repository.commit.merge_requests).to eq MergeRequest.none + end + end + + context 'with project snippet' do + it 'returns empty relation' do + expect(project_snippet.project).not_to receive(:merge_requests) + expect(project_snippet.repository.commit.merge_requests).to eq MergeRequest.none + end + end end describe 'signed commits' do diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index a9dca27f258..f88d64e2013 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -28,7 +28,7 @@ describe RedisCacheable do end describe '#cached_attribute' do - subject { instance.cached_attribute(payload.keys.first) } + subject { instance.cached_attribute(payload.each_key.first) } it 'gets the cache attribute' do Gitlab::Redis::SharedState.with do |redis| @@ -36,7 +36,7 @@ describe RedisCacheable do .and_return(payload.to_json) end - expect(subject).to eq(payload.values.first) + expect(subject).to eq(payload.each_value.first) end end diff --git a/spec/models/personal_snippet_spec.rb b/spec/models/personal_snippet_spec.rb index 276c8e22731..4a949a75cbd 100644 --- a/spec/models/personal_snippet_spec.rb +++ b/spec/models/personal_snippet_spec.rb @@ -16,4 +16,13 @@ describe PersonalSnippet do end end end + + it_behaves_like 'model with repository' do + let_it_be(:container) { create(:personal_snippet, :repository) } + let(:stubbed_container) { build_stubbed(:personal_snippet) } + let(:expected_full_path) { "@snippets/#{container.id}" } + let(:expected_repository_klass) { Repository } + let(:expected_storage_klass) { Storage::Hashed } + let(:expected_web_url_path) { "snippets/#{container.id}" } + end end diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index 903671afb13..09b4ec3677c 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -32,4 +32,13 @@ describe ProjectSnippet do end end end + + it_behaves_like 'model with repository' do + let_it_be(:container) { create(:project_snippet, :repository) } + let(:stubbed_container) { build_stubbed(:project_snippet) } + let(:expected_full_path) { "#{container.project.full_path}/@snippets/#{container.id}" } + let(:expected_repository_klass) { Repository } + let(:expected_storage_klass) { Storage::Hashed } + let(:expected_web_url_path) { "#{container.project.full_path}/snippets/#{container.id}" } + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9dc362594dd..5c56d1aa757 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -113,6 +113,7 @@ describe Project do let(:expected_full_path) { "#{container.namespace.full_path}/somewhere" } let(:expected_repository_klass) { Repository } let(:expected_storage_klass) { Storage::Hashed } + let(:expected_web_url_path) { "#{container.namespace.full_path}/somewhere" } end it 'has an inverse relationship with merge requests' do @@ -5592,6 +5593,24 @@ describe Project do it { is_expected.to be_falsey } end + describe '#self_monitoring?' do + let_it_be(:project) { create(:project) } + + subject { project.self_monitoring? } + + context 'when the project is instance self monitoring' do + before do + stub_application_setting(self_monitoring_project_id: project.id) + end + + it { is_expected.to be true } + end + + context 'when the project is not self monitoring' do + it { is_expected.to be false } + end + end + def rugged_config rugged_repo(project.repository).config end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 77114696fd2..0adf3fc8e85 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -372,7 +372,7 @@ describe Repository do context 'when some commits are not found ' do let(:oids) do - ['deadbeef'] + TestEnv::BRANCH_SHA.values.first(10) + ['deadbeef'] + TestEnv::BRANCH_SHA.each_value.first(10) end it 'returns only found commits' do diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb new file mode 100644 index 00000000000..9befbb02b17 --- /dev/null +++ b/spec/models/snippet_repository_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SnippetRepository do + describe 'associations' do + it { is_expected.to belong_to(:shard) } + it { is_expected.to belong_to(:snippet) } + end + + describe '.find_snippet' do + it 'finds snippet by disk path' do + snippet = create(:snippet) + snippet.track_snippet_repository + + expect(described_class.find_snippet(snippet.disk_path)).to eq(snippet) + end + + it 'returns nil when it does not find the snippet' do + expect(described_class.find_snippet('@@unexisting/path/to/snippet')).to be_nil + end + end +end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index ae43c0d585a..93bc42c144d 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -19,6 +19,7 @@ describe Snippet do it { is_expected.to have_many(:notes).dependent(:destroy) } it { is_expected.to have_many(:award_emoji).dependent(:destroy) } it { is_expected.to have_many(:user_mentions).class_name("SnippetUserMention") } + it { is_expected.to have_one(:snippet_repository) } end describe 'validation' do @@ -525,4 +526,109 @@ describe Snippet do snippet.to_json(params) end end + + describe '#storage' do + let(:snippet) { create(:snippet) } + + it "stores snippet in #{Storage::Hashed::SNIPPET_REPOSITORY_PATH_PREFIX} dir" do + expect(snippet.storage.disk_path).to start_with Storage::Hashed::SNIPPET_REPOSITORY_PATH_PREFIX + end + end + + describe '#track_snippet_repository' do + let(:snippet) { create(:snippet, :repository) } + + context 'when a snippet repository entry does not exist' do + it 'creates a new entry' do + expect { snippet.track_snippet_repository }.to change(snippet, :snippet_repository) + end + + it 'tracks the snippet storage location' do + snippet.track_snippet_repository + + expect(snippet.snippet_repository).to have_attributes( + disk_path: snippet.disk_path, + shard_name: snippet.repository_storage + ) + end + end + + context 'when a tracking entry exists' do + let!(:snippet_repository) { create(:snippet_repository, snippet: snippet) } + let!(:shard) { create(:shard, name: 'foo') } + + it 'does not create a new entry in the database' do + expect { snippet.track_snippet_repository }.not_to change(snippet, :snippet_repository) + end + + it 'updates the snippet storage location' do + allow(snippet).to receive(:disk_path).and_return('fancy/new/path') + allow(snippet).to receive(:repository_storage).and_return('foo') + + snippet.track_snippet_repository + + expect(snippet.snippet_repository).to have_attributes( + disk_path: 'fancy/new/path', + shard_name: 'foo' + ) + end + end + end + + describe '#create_repository' do + let(:snippet) { create(:snippet) } + + it 'creates the repository' do + expect(snippet.repository).to receive(:after_create).and_call_original + + expect(snippet.create_repository).to be_truthy + expect(snippet.repository.exists?).to be_truthy + end + + it 'tracks snippet repository' do + expect do + snippet.create_repository + end.to change(SnippetRepository, :count).by(1) + end + + context 'when repository exists' do + let(:snippet) { create(:snippet, :repository) } + + it 'does not try to create repository' do + expect(snippet.repository).not_to receive(:after_create) + + expect(snippet.create_repository).to be_nil + end + + it 'does not track snippet repository' do + expect do + snippet.create_repository + end.not_to change(SnippetRepository, :count) + end + end + end + + describe '#repository_storage' do + let(:snippet) { create(:snippet) } + + it 'returns default repository storage' do + expect(Gitlab::CurrentSettings).to receive(:pick_repository_storage) + + snippet.repository_storage + end + + context 'when snippet_project is already created' do + let!(:snippet_repository) { create(:snippet_repository, snippet: snippet) } + + before do + allow(snippet_repository).to receive(:shard_name).and_return('foo') + end + + it 'returns repository_storage from snippet_project' do + expect(Gitlab::CurrentSettings).not_to receive(:pick_repository_storage) + + expect(snippet.repository_storage).to eq 'foo' + end + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ac001ec3118..90795e0a8e4 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4160,7 +4160,7 @@ describe User, :do_not_mock_admin_mode do describe '#dismissed_callout?' do subject(:user) { create(:user) } - let(:feature_name) { UserCallout.feature_names.keys.first } + let(:feature_name) { UserCallout.feature_names.each_key.first } context 'when no callout dismissal record exists' do it 'returns false when no ignore_dismissal_earlier_than provided' do |