Commit 1f3f6579 authored by Patrick Steinhardt's avatar Patrick Steinhardt

checks: Add function to convert ChangesAccess to SingleChangeAccesses

There is only a single callsite right now which extracts changes from a
ChangesAccess and converts them into SingleChangeAccesses, which is
inside the ChangesAccess class itself. We're about to convert push rules
such that they can both drive single and bulk push rules, and there
we'll need the same functionality.

Extract the code into a standalone `#single_change_accesses()` function
which can be used to do the conversion.
parent 245ced26
......@@ -76,23 +76,33 @@ module Gitlab
result
end
def single_change_accesses
@single_changes_accesses ||=
changes.map do |change|
commits =
if change[:newrev].blank? || Gitlab::Git.blank_ref?(change[:newrev])
[]
else
Gitlab::Lazy.new { commits_for(change[:newrev]) }
end
Checks::SingleChangeAccess.new(
change,
user_access: user_access,
project: project,
protocol: protocol,
logger: logger,
commits: commits
)
end
end
protected
def single_access_checks!
# Iterate over all changes to find if user allowed all of them to be applied
changes.each do |change|
commits = Gitlab::Lazy.new { commits_for(change[:newrev]) }
# If user does not have access to make at least one change, cancel all
# push by allowing the exception to bubble up
Checks::SingleChangeAccess.new(
change,
user_access: user_access,
project: project,
protocol: protocol,
logger: logger,
commits: commits
).validate!
single_change_accesses.each do |single_change_access|
single_change_access.validate!
end
end
......
......@@ -174,6 +174,101 @@ RSpec.describe Gitlab::Checks::ChangesAccess do
end
end
describe '#single_change_accesses' do
let(:commits_for) { {} }
let(:expected_accesses) { [] }
shared_examples '#single_change_access' do
before do
commits_for.each do |id, commits|
expect(subject)
.to receive(:commits_for)
.with(id)
.and_return(commits)
end
end
it 'returns an array of SingleChangeAccess' do
# Commits are wrapped in a Gitlab::Lazy and thus need to be resolved
# first such that we can directly compare types.
actual_accesses = subject.single_change_accesses
.each { |access| access.instance_variable_set(:@commits, access.commits.to_a) }
expect(actual_accesses).to match_array(expected_accesses)
end
end
context 'with no changes' do
let(:changes) { [] }
it_behaves_like '#single_change_access'
end
context 'with a single change and no new commits' do
let(:commits_for) { { 'new' => [] } }
let(:changes) do
[
{ oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch' }
]
end
let(:expected_accesses) do
[
have_attributes(oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch', commits: [])
]
end
it_behaves_like '#single_change_access'
end
context 'with a single change and new commits' do
let(:commits_for) { { 'new' => [create_commit('new', [])] } }
let(:changes) do
[
{ oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch' }
]
end
let(:expected_accesses) do
[
have_attributes(oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch', commits: [create_commit('new', [])])
]
end
it_behaves_like '#single_change_access'
end
context 'with multiple changes' do
let(:commits_for) do
{
'a' => [create_commit('a', [])],
'c' => [create_commit('c', [])],
'd' => []
}
end
let(:changes) do
[
{ newrev: 'a', ref: 'refs/heads/a' },
{ oldrev: 'b', ref: 'refs/heads/b' },
{ oldrev: 'a', newrev: 'c', ref: 'refs/heads/c' },
{ newrev: 'd', ref: 'refs/heads/d' }
]
end
let(:expected_accesses) do
[
have_attributes(newrev: 'a', ref: 'refs/heads/a', commits: [create_commit('a', [])]),
have_attributes(oldrev: 'b', ref: 'refs/heads/b', commits: []),
have_attributes(oldrev: 'a', newrev: 'c', ref: 'refs/heads/c', commits: [create_commit('c', [])]),
have_attributes(newrev: 'd', ref: 'refs/heads/d', commits: [])
]
end
it_behaves_like '#single_change_access'
end
end
def create_commit(id, parent_ids)
Gitlab::Git::Commit.new(project.repository, {
id: id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment