diff options
Diffstat (limited to 'spec/models/protected_branch_spec.rb')
-rw-r--r-- | spec/models/protected_branch_spec.rb | 169 |
1 files changed, 110 insertions, 59 deletions
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 71e22f848cc..d14a7dd1a7e 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ProtectedBranch do +RSpec.describe ProtectedBranch, feature_category: :source_code_management do subject { build_stubbed(:protected_branch) } describe 'Associations' do @@ -13,6 +13,30 @@ RSpec.describe ProtectedBranch do describe 'Validation' do it { is_expected.to validate_presence_of(:name) } + context 'uniqueness' do + let(:protected_branch) { build(:protected_branch) } + + subject { protected_branch } + + it { is_expected.to validate_uniqueness_of(:name).scoped_to([:project_id, :namespace_id]) } + + context 'when the protected_branch was saved previously' do + before do + protected_branch.save! + end + + it { is_expected.not_to validate_uniqueness_of(:name) } + + context 'and name is changed' do + before do + protected_branch.name = "#{protected_branch.name} + something else" + end + + it { is_expected.to validate_uniqueness_of(:name).scoped_to([:project_id, :namespace_id]) } + end + end + end + describe '#validate_either_project_or_top_group' do context 'when protected branch does not have project or group association' do it 'validate failed' do @@ -214,85 +238,52 @@ RSpec.describe ProtectedBranch do let_it_be(:project) { create(:project, :repository) } let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "“jawn”") } - let(:rely_on_new_cache) { true } - - shared_examples_for 'hash based cache implementation' do - it 'calls only hash based cache implementation' do - expect_next_instance_of(ProtectedBranches::CacheService) do |instance| - expect(instance).to receive(:fetch).with('missing-branch', anything).and_call_original - end - - expect(Rails.cache).not_to receive(:fetch) - - described_class.protected?(project, 'missing-branch') - end - end - before do - stub_feature_flags(rely_on_protected_branches_cache: rely_on_new_cache) allow(described_class).to receive(:matching).and_call_original # the original call works and warms the cache described_class.protected?(project, protected_branch.name) end - context 'Dry-run: true (rely_on_protected_branches_cache is off, new hash-based is used)' do - let(:rely_on_new_cache) { false } + it 'correctly invalidates a cache' do + expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).exactly(3).times.and_call_original - it 'recalculates a fresh value every time in order to check the cache is not returning stale data' do - expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).twice + create_params = { name: 'bar', merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }] } + branch = ProtectedBranches::CreateService.new(project, project.owner, create_params).execute + expect(described_class.protected?(project, protected_branch.name)).to eq(true) - 2.times { described_class.protected?(project, protected_branch.name) } - end + ProtectedBranches::UpdateService.new(project, project.owner, name: 'ber').execute(branch) + expect(described_class.protected?(project, protected_branch.name)).to eq(true) - it_behaves_like 'hash based cache implementation' + ProtectedBranches::DestroyService.new(project, project.owner).execute(branch) + expect(described_class.protected?(project, protected_branch.name)).to eq(true) end - context 'Dry-run: false (rely_on_protected_branches_cache is enabled, new hash-based cache is used)' do - let(:rely_on_new_cache) { true } - - it 'correctly invalidates a cache' do - expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).exactly(3).times.and_call_original - - create_params = { name: 'bar', merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }] } - branch = ProtectedBranches::CreateService.new(project, project.owner, create_params).execute - expect(described_class.protected?(project, protected_branch.name)).to eq(true) - - ProtectedBranches::UpdateService.new(project, project.owner, name: 'ber').execute(branch) - expect(described_class.protected?(project, protected_branch.name)).to eq(true) - - ProtectedBranches::DestroyService.new(project, project.owner).execute(branch) - expect(described_class.protected?(project, protected_branch.name)).to eq(true) - end - - it_behaves_like 'hash based cache implementation' - - context 'when project is updated' do - it 'does not invalidate a cache' do - expect(described_class).not_to receive(:matching).with(protected_branch.name, protected_refs: anything) + context 'when project is updated' do + it 'does not invalidate a cache' do + expect(described_class).not_to receive(:matching).with(protected_branch.name, protected_refs: anything) - project.touch + project.touch - described_class.protected?(project, protected_branch.name) - end + described_class.protected?(project, protected_branch.name) end + end - context 'when other project protected branch is updated' do - it 'does not invalidate the current project cache' do - expect(described_class).not_to receive(:matching).with(protected_branch.name, protected_refs: anything) + context 'when other project protected branch is updated' do + it 'does not invalidate the current project cache' do + expect(described_class).not_to receive(:matching).with(protected_branch.name, protected_refs: anything) - another_project = create(:project) - ProtectedBranches::CreateService.new(another_project, another_project.owner, name: 'bar').execute + another_project = create(:project) + ProtectedBranches::CreateService.new(another_project, another_project.owner, name: 'bar').execute - described_class.protected?(project, protected_branch.name) - end + described_class.protected?(project, protected_branch.name) end + end - it 'correctly uses the cached version' do - expect(described_class).not_to receive(:matching) + it 'correctly uses the cached version' do + expect(described_class).not_to receive(:matching) - expect(described_class.protected?(project, protected_branch.name)).to eq(true) - end + expect(described_class.protected?(project, protected_branch.name)).to eq(true) end end end @@ -344,6 +335,7 @@ RSpec.describe ProtectedBranch do context "when feature flag disabled" do before do stub_feature_flags(group_protected_branches: false) + stub_feature_flags(allow_protected_branches_for_group: false) end let(:subject_branch) { create(:protected_branch, allow_force_push: allow_force_push, name: "foo") } @@ -383,6 +375,7 @@ RSpec.describe ProtectedBranch do with_them do before do stub_feature_flags(group_protected_branches: true) + stub_feature_flags(allow_protected_branches_for_group: true) unless group_level_value.nil? create(:protected_branch, allow_force_push: group_level_value, name: "foo", project: nil, group: group) @@ -436,6 +429,7 @@ RSpec.describe ProtectedBranch do context 'when feature flag enabled' do before do stub_feature_flags(group_protected_branches: true) + stub_feature_flags(allow_protected_branches_for_group: true) end it 'call `all_protected_branches`' do @@ -448,6 +442,7 @@ RSpec.describe ProtectedBranch do context 'when feature flag disabled' do before do stub_feature_flags(group_protected_branches: false) + stub_feature_flags(allow_protected_branches_for_group: false) end it 'call `protected_branches`' do @@ -458,6 +453,62 @@ RSpec.describe ProtectedBranch do end end + describe '.protected_ref_accessible_to?' do + let_it_be(:project) { create(:project) } + let_it_be(:guest) { create(:user) } + let_it_be(:reporter) { create(:user) } + let_it_be(:developer) { create(:user) } + let_it_be(:maintainer) { create(:user) } + let_it_be(:owner) { create(:user) } + let_it_be(:admin) { create(:user, :admin) } + + before do + project.add_guest(guest) + project.add_reporter(reporter) + project.add_developer(developer) + project.add_maintainer(maintainer) + project.add_owner(owner) + end + + subject { described_class.protected_ref_accessible_to?(anything, current_user, project: project, action: :push) } + + context 'with guest' do + let(:current_user) { guest } + + it { is_expected.to eq(false) } + end + + context 'with reporter' do + let(:current_user) { reporter } + + it { is_expected.to eq(false) } + end + + context 'with developer' do + let(:current_user) { developer } + + it { is_expected.to eq(false) } + end + + context 'with maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to eq(true) } + end + + context 'with owner' do + let(:current_user) { owner } + + it { is_expected.to eq(true) } + end + + context 'with admin' do + let(:current_user) { admin } + + it { is_expected.to eq(true) } + end + end + describe '.by_name' do let!(:protected_branch) { create(:protected_branch, name: 'master') } let!(:another_protected_branch) { create(:protected_branch, name: 'stable') } |