diff options
Diffstat (limited to 'spec/models/repository_spec.rb')
-rw-r--r-- | spec/models/repository_spec.rb | 597 |
1 files changed, 594 insertions, 3 deletions
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index afbf62035ac..a57229a4fdf 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -5,6 +5,15 @@ describe Repository, models: true do let(:repository) { create(:project).repository } let(:user) { create(:user) } + let(:commit_options) do + author = repository.user_to_committer(user) + { message: 'Test message', committer: author, author: author } + end + let(:merge_commit) do + source_sha = repository.find_branch('feature').target + merge_commit_id = repository.merge(user, source_sha, 'master', commit_options) + repository.commit(merge_commit_id) + end describe :branch_names_contains do subject { repository.branch_names_contains(sample_commit.id) } @@ -92,13 +101,29 @@ describe Repository, models: true do end describe 'parsing result' do - subject { repository.parse_search_result(results.first) } + subject { repository.parse_search_result(search_result) } + let(:search_result) { results.first } it { is_expected.to be_an OpenStruct } it { expect(subject.filename).to eq('CHANGELOG') } + it { expect(subject.basename).to eq('CHANGELOG') } it { expect(subject.ref).to eq('master') } it { expect(subject.startline).to eq(186) } it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } + + context "when filename has extension" do + let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" } + + it { expect(subject.filename).to eq('CONTRIBUTE.md') } + it { expect(subject.basename).to eq('CONTRIBUTE') } + end + + context "when file under directory" do + let(:search_result) { "master:a/b/c.md:5:a b c\n" } + + it { expect(subject.filename).to eq('a/b/c.md') } + it { expect(subject.basename).to eq('a/b/c') } + end end end @@ -139,6 +164,12 @@ describe Repository, models: true do expect(branch.name).to eq('new_feature') end + + it 'calls the after_create_branch hook' do + expect(repository).to receive(:after_create_branch) + + repository.add_branch(user, 'new_feature', 'master') + end end context 'when pre hooks failed' do @@ -200,13 +231,22 @@ describe Repository, models: true do describe :commit_with_hooks do context 'when pre hooks were successful' do - it 'should run without errors' do - expect_any_instance_of(GitHooksService).to receive(:execute).and_return(true) + before do + expect_any_instance_of(GitHooksService).to receive(:execute). + and_return(true) + end + it 'should run without errors' do expect do repository.commit_with_hooks(user, 'feature') { sample_commit.id } end.not_to raise_error end + + it 'should ensure the autocrlf Git option is set to :input' do + expect(repository).to receive(:update_autocrlf_option) + + repository.commit_with_hooks(user, 'feature') { sample_commit.id } + end end context 'when pre hooks failed' do @@ -219,4 +259,555 @@ describe Repository, models: true do end end end + + describe '#exists?' do + it 'returns true when a repository exists' do + expect(repository.exists?).to eq(true) + end + + it 'returns false when a repository does not exist' do + expect(repository.raw_repository).to receive(:rugged). + and_raise(Gitlab::Git::Repository::NoRepository) + + expect(repository.exists?).to eq(false) + end + + it 'returns false when there is no namespace' do + allow(repository).to receive(:path_with_namespace).and_return(nil) + + expect(repository.exists?).to eq(false) + end + end + + describe '#has_visible_content?' do + subject { repository.has_visible_content? } + + describe 'when there are no branches' do + before do + allow(repository.raw_repository).to receive(:branch_count).and_return(0) + end + + it { is_expected.to eq(false) } + end + + describe 'when there are branches' do + it 'returns true' do + expect(repository.raw_repository).to receive(:branch_count).and_return(3) + + expect(subject).to eq(true) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:branch_count). + once. + and_return(3) + + repository.has_visible_content? + repository.has_visible_content? + end + end + end + + describe '#update_autocrlf_option' do + describe 'when autocrlf is not already set to :input' do + before do + repository.raw_repository.autocrlf = true + end + + it 'sets autocrlf to :input' do + repository.update_autocrlf_option + + expect(repository.raw_repository.autocrlf).to eq(:input) + end + end + + describe 'when autocrlf is already set to :input' do + before do + repository.raw_repository.autocrlf = :input + end + + it 'does nothing' do + expect(repository.raw_repository).to_not receive(:autocrlf=). + with(:input) + + repository.update_autocrlf_option + end + end + end + + describe '#empty?' do + let(:empty_repository) { create(:project_empty_repo).repository } + + it 'returns true for an empty repository' do + expect(empty_repository.empty?).to eq(true) + end + + it 'returns false for a non-empty repository' do + expect(repository.empty?).to eq(false) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:empty?). + once. + and_return(false) + + repository.empty? + repository.empty? + end + end + + describe '#root_ref' do + it 'returns a branch name' do + expect(repository.root_ref).to be_an_instance_of(String) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:root_ref). + once. + and_return('master') + + repository.root_ref + repository.root_ref + end + end + + describe '#expire_cache' do + it 'expires all caches' do + expect(repository).to receive(:expire_branch_cache) + + repository.expire_cache + end + + it 'expires the caches for a specific branch' do + expect(repository).to receive(:expire_branch_cache).with('master') + + repository.expire_cache('master') + end + + it 'expires the emptiness caches for an empty repository' do + expect(repository).to receive(:empty?).and_return(true) + expect(repository).to receive(:expire_emptiness_caches) + + repository.expire_cache + end + + it 'does not expire the emptiness caches for a non-empty repository' do + expect(repository).to receive(:empty?).and_return(false) + expect(repository).to_not receive(:expire_emptiness_caches) + + repository.expire_cache + end + end + + describe '#expire_root_ref_cache' do + it 'expires the root reference cache' do + repository.root_ref + + expect(repository.raw_repository).to receive(:root_ref). + once. + and_return('foo') + + repository.expire_root_ref_cache + + expect(repository.root_ref).to eq('foo') + end + end + + describe '#expire_has_visible_content_cache' do + it 'expires the visible content cache' do + repository.has_visible_content? + + expect(repository.raw_repository).to receive(:branch_count). + once. + and_return(0) + + repository.expire_has_visible_content_cache + + expect(repository.has_visible_content?).to eq(false) + end + end + + describe '#expire_branch_cache' do + # This method is private but we need it for testing purposes. Sadly there's + # no other proper way of testing caching operations. + let(:cache) { repository.send(:cache) } + + it 'expires the cache for all branches' do + expect(cache).to receive(:expire). + at_least(repository.branches.length). + times + + repository.expire_branch_cache + end + + it 'expires the cache for all branches when the root branch is given' do + expect(cache).to receive(:expire). + at_least(repository.branches.length). + times + + repository.expire_branch_cache(repository.root_ref) + end + + it 'expires the cache for a specific branch' do + expect(cache).to receive(:expire).once + + repository.expire_branch_cache('foo') + end + end + + describe '#expire_emptiness_caches' do + let(:cache) { repository.send(:cache) } + + it 'expires the caches' do + expect(cache).to receive(:expire).with(:empty?) + expect(repository).to receive(:expire_has_visible_content_cache) + + repository.expire_emptiness_caches + end + end + + describe :skip_merged_commit do + subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } } + + it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') } + end + + describe '#merge' do + it 'should merge the code and return the commit id' do + expect(merge_commit).to be_present + expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present + end + end + + describe '#revert' do + let(:new_image_commit) { repository.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') } + let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + + context 'when there is a conflict' do + it 'should abort the operation' do + expect(repository.revert(user, new_image_commit, 'master')).to eq(false) + end + end + + context 'when commit was already reverted' do + it 'should abort the operation' do + repository.revert(user, update_image_commit, 'master') + + expect(repository.revert(user, update_image_commit, 'master')).to eq(false) + end + end + + context 'when commit can be reverted' do + it 'should revert the changes' do + expect(repository.revert(user, update_image_commit, 'master')).to be_truthy + end + end + + context 'reverting a merge commit' do + it 'should revert the changes' do + merge_commit + expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present + + repository.revert(user, merge_commit, 'master') + expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present + end + end + end + + describe '#before_delete' do + describe 'when a repository does not exist' do + before do + allow(repository).to receive(:exists?).and_return(false) + end + + it 'does not flush caches that depend on repository data' do + expect(repository).to_not receive(:expire_cache) + + repository.before_delete + end + + it 'flushes the root ref cache' do + expect(repository).to receive(:expire_root_ref_cache) + + repository.before_delete + end + + it 'flushes the emptiness caches' do + expect(repository).to receive(:expire_emptiness_caches) + + repository.before_delete + end + end + + describe 'when a repository exists' do + before do + allow(repository).to receive(:exists?).and_return(true) + end + + it 'flushes the caches that depend on repository data' do + expect(repository).to receive(:expire_cache) + + repository.before_delete + end + + it 'flushes the root ref cache' do + expect(repository).to receive(:expire_root_ref_cache) + + repository.before_delete + end + + it 'flushes the emptiness caches' do + expect(repository).to receive(:expire_emptiness_caches) + + repository.before_delete + end + end + end + + describe '#before_change_head' do + it 'flushes the branch cache' do + expect(repository).to receive(:expire_branch_cache) + + repository.before_change_head + end + + it 'flushes the root ref cache' do + expect(repository).to receive(:expire_root_ref_cache) + + repository.before_change_head + end + end + + describe '#before_push_tag' do + it 'flushes the cache' do + expect(repository).to receive(:expire_cache) + expect(repository).to receive(:expire_tag_count_cache) + + repository.before_push_tag + end + end + + describe '#after_import' do + it 'flushes the emptiness cachess' do + expect(repository).to receive(:expire_emptiness_caches) + + repository.after_import + end + end + + describe '#after_push_commit' do + it 'flushes the cache' do + expect(repository).to receive(:expire_cache).with('master', '123') + + repository.after_push_commit('master', '123') + end + end + + describe '#after_create_branch' do + it 'flushes the visible content cache' do + expect(repository).to receive(:expire_has_visible_content_cache) + + repository.after_create_branch + end + end + + describe '#after_remove_branch' do + it 'flushes the visible content cache' do + expect(repository).to receive(:expire_has_visible_content_cache) + + repository.after_remove_branch + end + end + + describe "#main_language" do + it 'shows the main language of the project' do + expect(repository.main_language).to eq("Ruby") + end + + it 'returns nil when the repository is empty' do + allow(repository).to receive(:empty?).and_return(true) + + expect(repository.main_language).to be_nil + end + end + + describe '#before_remove_tag' do + it 'flushes the tag cache' do + expect(repository).to receive(:expire_tag_count_cache) + + repository.before_remove_tag + end + end + + describe '#branch_count' do + it 'returns the number of branches' do + expect(repository.branch_count).to be_an_instance_of(Fixnum) + end + end + + describe '#tag_count' do + it 'returns the number of tags' do + expect(repository.tag_count).to be_an_instance_of(Fixnum) + end + end + + describe '#expire_branch_count_cache' do + let(:cache) { repository.send(:cache) } + + it 'expires the cache' do + expect(cache).to receive(:expire).with(:branch_count) + + repository.expire_branch_count_cache + end + end + + describe '#expire_tag_count_cache' do + let(:cache) { repository.send(:cache) } + + it 'expires the cache' do + expect(cache).to receive(:expire).with(:tag_count) + + repository.expire_tag_count_cache + end + end + + describe '#add_tag' do + it 'adds a tag' do + expect(repository).to receive(:before_push_tag) + + expect_any_instance_of(Gitlab::Shell).to receive(:add_tag). + with(repository.path_with_namespace, '8.5', 'master', 'foo') + + repository.add_tag('8.5', 'master', 'foo') + end + end + + describe '#rm_branch' do + let(:user) { create(:user) } + + it 'removes a branch' do + expect(repository).to receive(:before_remove_branch) + expect(repository).to receive(:after_remove_branch) + + repository.rm_branch(user, 'feature') + end + end + + describe '#rm_tag' do + it 'removes a tag' do + expect(repository).to receive(:before_remove_tag) + + expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag). + with(repository.path_with_namespace, '8.5') + + repository.rm_tag('8.5') + end + end + + describe '#avatar' do + it 'returns the first avatar file found in the repository' do + expect(repository).to receive(:blob_at_branch). + with('master', 'logo.png'). + and_return(true) + + expect(repository.avatar).to eq('logo.png') + end + + it 'caches the output' do + allow(repository).to receive(:blob_at_branch). + with('master', 'logo.png'). + and_return(true) + + expect(repository.avatar).to eq('logo.png') + + expect(repository).to_not receive(:blob_at_branch) + expect(repository.avatar).to eq('logo.png') + end + end + + describe '#expire_avatar_cache' do + let(:cache) { repository.send(:cache) } + + before do + allow(repository).to receive(:cache).and_return(cache) + end + + context 'without a branch or revision' do + it 'flushes the cache' do + expect(cache).to receive(:expire).with(:avatar) + + repository.expire_avatar_cache + end + end + + context 'with a branch' do + it 'does not flush the cache if the branch is not the default branch' do + expect(cache).not_to receive(:expire) + + repository.expire_avatar_cache('cats') + end + + it 'flushes the cache if the branch equals the default branch' do + expect(cache).to receive(:expire).with(:avatar) + + repository.expire_avatar_cache(repository.root_ref) + end + end + + context 'with a branch and revision' do + let(:commit) { double(:commit) } + + before do + allow(repository).to receive(:commit).and_return(commit) + end + + it 'does not flush the cache if the commit does not change any logos' do + diff = double(:diff, new_path: 'test.txt') + + expect(commit).to receive(:diffs).and_return([diff]) + expect(cache).not_to receive(:expire) + + repository.expire_avatar_cache(repository.root_ref, '123') + end + + it 'flushes the cache if the commit changes any of the logos' do + diff = double(:diff, new_path: Repository::AVATAR_FILES[0]) + + expect(commit).to receive(:diffs).and_return([diff]) + expect(cache).to receive(:expire).with(:avatar) + + repository.expire_avatar_cache(repository.root_ref, '123') + end + end + end + + describe '#build_cache' do + let(:cache) { repository.send(:cache) } + + it 'builds the caches if they do not already exist' do + expect(cache).to receive(:exist?). + exactly(repository.cache_keys.length). + times. + and_return(false) + + repository.cache_keys.each do |key| + expect(repository).to receive(key) + end + + repository.build_cache + end + + it 'does not build any caches that already exist' do + expect(cache).to receive(:exist?). + exactly(repository.cache_keys.length). + times. + and_return(true) + + repository.cache_keys.each do |key| + expect(repository).to_not receive(key) + end + + repository.build_cache + end + end end |