Commit 48a5bfc5 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'id-fix-self-approvals' into 'master'

Use fallback approval rule if no eligible rules exist

Closes gitlab-ce#61044

See merge request gitlab-org/gitlab-ee!14042
parents 5a3b3489 371b2f1c
...@@ -51,12 +51,12 @@ class ApprovalState ...@@ -51,12 +51,12 @@ class ApprovalState
end end
def has_non_fallback_rules? def has_non_fallback_rules?
regular_rules.present? || code_owner_rules.present? has_regular_rule_with_approvers? || code_owner_rules.present?
end end
# Use the fallback rule if regular rules are empty # Use the fallback rule if regular rules are empty
def use_fallback? def use_fallback?
regular_rules.empty? !has_regular_rule_with_approvers?
end end
def fallback_rule def fallback_rule
...@@ -65,7 +65,7 @@ class ApprovalState ...@@ -65,7 +65,7 @@ class ApprovalState
# Determines which set of rules to use (MR or project) # Determines which set of rules to use (MR or project)
def approval_rules_overwritten? def approval_rules_overwritten?
regular_merge_request_rules.any? || regular_merge_request_rules.any? { |rule| rule.approvers.present? } ||
(project.can_override_approvers? && merge_request.approvals_before_merge.present?) (project.can_override_approvers? && merge_request.approvals_before_merge.present?)
end end
alias_method :approvers_overwritten?, :approval_rules_overwritten? alias_method :approvers_overwritten?, :approval_rules_overwritten?
...@@ -83,7 +83,7 @@ class ApprovalState ...@@ -83,7 +83,7 @@ class ApprovalState
end end
def any_approver_allowed? def any_approver_allowed?
regular_rules.empty? || approved? !has_regular_rule_with_approvers? || approved?
end end
def approvals_required def approvals_required
...@@ -172,6 +172,10 @@ class ApprovalState ...@@ -172,6 +172,10 @@ class ApprovalState
private private
def has_regular_rule_with_approvers?
regular_rules.any? { |rule| rule.approvers.present? }
end
def regular_rules def regular_rules
strong_memoize(:regular_rules) do strong_memoize(:regular_rules) do
rules = approval_rules_overwritten? ? regular_merge_request_rules : regular_project_rules rules = approval_rules_overwritten? ? regular_merge_request_rules : regular_project_rules
......
---
title: Use fallback approval rule if no eligible rules exist
merge_request: 14042
author:
type: fixed
...@@ -30,8 +30,10 @@ describe 'Merge request > User edits MR with approval rules', :js do ...@@ -30,8 +30,10 @@ describe 'Merge request > User edits MR with approval rules', :js do
project.update_attribute(:disable_overriding_approvers_per_merge_request, false) project.update_attribute(:disable_overriding_approvers_per_merge_request, false)
stub_licensed_features(multiple_approval_rules: true) stub_licensed_features(multiple_approval_rules: true)
approver = create(:user)
mr_rule_names.each do |name| mr_rule_names.each do |name|
create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 1, name: name) create(:approval_merge_request_rule,
merge_request: merge_request, approvals_required: 1, name: name, users: [approver])
end end
sign_in(author) sign_in(author)
......
...@@ -4,7 +4,8 @@ require 'spec_helper' ...@@ -4,7 +4,8 @@ require 'spec_helper'
describe ApprovalState do describe ApprovalState do
def create_rule(additional_params = {}) def create_rule(additional_params = {})
params = additional_params.merge(merge_request: merge_request) default_approver = create(:user)
params = additional_params.reverse_merge(merge_request: merge_request, users: [default_approver])
factory = params.delete(:code_owner) ? :code_owner_rule : :approval_merge_request_rule factory = params.delete(:code_owner) ? :code_owner_rule : :approval_merge_request_rule
create(factory, params) create(factory, params)
...@@ -101,11 +102,23 @@ describe ApprovalState do ...@@ -101,11 +102,23 @@ describe ApprovalState do
context 'when approval rule on the merge request exists' do context 'when approval rule on the merge request exists' do
before do before do
create(:approval_merge_request_rule, merge_request: merge_request) create(:approval_merge_request_rule, merge_request: merge_request, users: approvers)
end end
it 'returns true' do context 'without approvers' do
expect(subject.approval_rules_overwritten?).to eq(true) let(:approvers) { [] }
it 'returns false' do
expect(subject.approval_rules_overwritten?).to eq(false)
end
end
context 'with approvers' do
let(:approvers) { [create(:user)] }
it 'returns true' do
expect(subject.approval_rules_overwritten?).to eq(true)
end
end end
end end
...@@ -618,10 +631,11 @@ describe ApprovalState do ...@@ -618,10 +631,11 @@ describe ApprovalState do
end end
context 'when there is one approver required' do context 'when there is one approver required' do
let!(:rule) { create_rule(approvals_required: 1) } let!(:rule) { create_rule(approvals_required: 1, users: []) }
context 'when that approver is the MR author' do context 'when that approver is the MR author' do
before do before do
project.update!(approvals_before_merge: 2)
rule.users << author rule.users << author
end end
...@@ -629,13 +643,45 @@ describe ApprovalState do ...@@ -629,13 +643,45 @@ describe ApprovalState do
it_behaves_like 'a MR that all members with write access can approve' it_behaves_like 'a MR that all members with write access can approve'
it 'requires one approval' do
expect(subject.approvals_left).to eq(1)
end
it 'does not allow a logged-out user to approve the MR' do it 'does not allow a logged-out user to approve the MR' do
expect(subject.can_approve?(nil)).to be_falsey expect(subject.can_approve?(nil)).to be_falsey
end end
it 'fallback rule is used' do
expect(subject.approvals_left).to eq(2)
end
it 'is not approved' do
expect(subject.approved?).to eq(false)
end
context 'with project approval rule' do
before do
create(:approval_project_rule, project: project, approvals_required: 1, users: [approver])
end
context 'with approvers' do
let(:approver) { create(:user) }
it 'requires one approval' do
rule = subject.wrapped_approval_rules.last.approval_rule
expect(rule).to be_a(ApprovalProjectRule)
expect(subject.approvers).to eq([approver])
end
end
context 'without approvers' do
let(:approver) { author }
it 'requires one approval' do
rule = subject.wrapped_approval_rules.last
expect(rule).to be_a(ApprovalMergeRequestFallback)
expect(subject.approvers).to eq([])
end
end
end
end end
context 'when that approver is not the MR author' do context 'when that approver is not the MR author' do
...@@ -1072,7 +1118,7 @@ describe ApprovalState do ...@@ -1072,7 +1118,7 @@ describe ApprovalState do
it 'includes approvers from first rule and code owner rule' do it 'includes approvers from first rule and code owner rule' do
create_rules create_rules
approvers = rule1.users + [group_approver1] approvers = rule1.users + code_owner_rule.users + [group_approver1]
expect(subject.approvers).to contain_exactly(*approvers) expect(subject.approvers).to contain_exactly(*approvers)
end end
...@@ -1252,10 +1298,11 @@ describe ApprovalState do ...@@ -1252,10 +1298,11 @@ describe ApprovalState do
end end
context 'when there is one approver required' do context 'when there is one approver required' do
let!(:rule) { create_rule(approvals_required: 1) } let!(:rule) { create_rule(approvals_required: 1, users: []) }
context 'when that approver is the MR author' do context 'when that approver is the MR author' do
before do before do
project.update!(approvals_before_merge: 1)
rule.users << author rule.users << author
end end
...@@ -1270,6 +1317,10 @@ describe ApprovalState do ...@@ -1270,6 +1317,10 @@ describe ApprovalState do
it 'does not allow a logged-out user to approve the MR' do it 'does not allow a logged-out user to approve the MR' do
expect(subject.can_approve?(nil)).to be_falsey expect(subject.can_approve?(nil)).to be_falsey
end end
it 'is not approved' do
expect(subject.approved?).to eq(false)
end
end end
context 'when that approver is not the MR author' do context 'when that approver is not the MR author' do
......
...@@ -60,6 +60,7 @@ describe API::MergeRequestApprovals do ...@@ -60,6 +60,7 @@ describe API::MergeRequestApprovals do
context 'when private group approver' do context 'when private group approver' do
before do before do
private_group = create(:group, :private) private_group = create(:group, :private)
private_group.add_developer(create(:user))
merge_request.approver_groups.create(group: private_group) merge_request.approver_groups.create(group: private_group)
end end
...@@ -138,7 +139,9 @@ describe API::MergeRequestApprovals do ...@@ -138,7 +139,9 @@ describe API::MergeRequestApprovals do
end end
it 'excludes private groups' do it 'excludes private groups' do
private_group = create :group, :private private_group = create(:group, :private)
rule.users << approver
rule.groups << private_group rule.groups << private_group
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings", user) get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings", user)
......
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