Commit d3bc16c1 authored by Nick Thomas's avatar Nick Thomas

Introduce namespace license checks for Push Rules (EES)

parent 814de769
class Admin::PushRulesController < Admin::ApplicationController class Admin::PushRulesController < Admin::ApplicationController
before_action :check_push_rules_available!
before_action :push_rule before_action :push_rule
respond_to :html respond_to :html
...@@ -18,6 +19,10 @@ class Admin::PushRulesController < Admin::ApplicationController ...@@ -18,6 +19,10 @@ class Admin::PushRulesController < Admin::ApplicationController
private private
def check_push_rules_available!
render_404 unless License.feature_available?(:push_rules)
end
def push_rule_params def push_rule_params
params.require(:push_rule).permit(:deny_delete_tag, :delete_branch_regex, params.require(:push_rule).permit(:deny_delete_tag, :delete_branch_regex,
:commit_message_regex, :force_push_regex, :author_email_regex, :member_check, :commit_message_regex, :force_push_regex, :author_email_regex, :member_check,
......
...@@ -5,18 +5,19 @@ module EE ...@@ -5,18 +5,19 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
before_action :push_rule, only: [:show]
before_action :remote_mirror, only: [:show] before_action :remote_mirror, only: [:show]
end end
def show private
super
def push_rule
return unless project.feature_available?(:push_rules)
project.create_push_rule unless project.push_rule project.create_push_rule unless project.push_rule
@push_rule = project.push_rule @push_rule = project.push_rule
end end
private
def remote_mirror def remote_mirror
@remote_mirror = @project.remote_mirrors.first_or_initialize @remote_mirror = @project.remote_mirrors.first_or_initialize
end end
......
...@@ -3,6 +3,7 @@ class Projects::PushRulesController < Projects::ApplicationController ...@@ -3,6 +3,7 @@ class Projects::PushRulesController < Projects::ApplicationController
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :check_push_rules_available!
respond_to :html respond_to :html
......
...@@ -25,7 +25,7 @@ module EE ...@@ -25,7 +25,7 @@ module EE
belongs_to :mirror_user, foreign_key: 'mirror_user_id', class_name: 'User' belongs_to :mirror_user, foreign_key: 'mirror_user_id', class_name: 'User'
has_one :mirror_data, dependent: :delete, autosave: true, class_name: 'ProjectMirrorData' has_one :mirror_data, dependent: :delete, autosave: true, class_name: 'ProjectMirrorData'
has_one :push_rule, dependent: :destroy has_one :push_rule, ->(project) { project&.feature_available?(:push_rules) ? all : none }, dependent: :destroy
has_one :index_status, dependent: :destroy has_one :index_status, dependent: :destroy
has_one :jenkins_service, dependent: :destroy has_one :jenkins_service, dependent: :destroy
has_one :jenkins_deprecated_service, dependent: :destroy has_one :jenkins_deprecated_service, dependent: :destroy
......
...@@ -13,6 +13,7 @@ class License < ActiveRecord::Base ...@@ -13,6 +13,7 @@ class License < ActiveRecord::Base
MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze
MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze
OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze
PUSH_RULES_FEATURE = 'GitLab_PushRules'.freeze
RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
...@@ -32,7 +33,8 @@ class License < ActiveRecord::Base ...@@ -32,7 +33,8 @@ class License < ActiveRecord::Base
file_lock: FILE_LOCK_FEATURE, file_lock: FILE_LOCK_FEATURE,
issue_weights: ISSUE_WEIGHTS_FEATURE, issue_weights: ISSUE_WEIGHTS_FEATURE,
merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE, merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE,
merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE,
push_rules: PUSH_RULES_FEATURE
}.freeze }.freeze
STARTER_PLAN = 'starter'.freeze STARTER_PLAN = 'starter'.freeze
...@@ -48,6 +50,7 @@ class License < ActiveRecord::Base ...@@ -48,6 +50,7 @@ class License < ActiveRecord::Base
{ ISSUE_WEIGHTS_FEATURE => 1 }, { ISSUE_WEIGHTS_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 }, { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }, { MERGE_REQUEST_SQUASH_FEATURE => 1 },
{ PUSH_RULES_FEATURE => 1 },
{ RELATED_ISSUES_FEATURE => 1 } { RELATED_ISSUES_FEATURE => 1 }
].freeze ].freeze
...@@ -86,6 +89,7 @@ class License < ActiveRecord::Base ...@@ -86,6 +89,7 @@ class License < ActiveRecord::Base
{ MERGE_REQUEST_REBASE_FEATURE => 1 }, { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }, { MERGE_REQUEST_SQUASH_FEATURE => 1 },
{ OBJECT_STORAGE_FEATURE => 1 }, { OBJECT_STORAGE_FEATURE => 1 },
{ PUSH_RULES_FEATURE => 1 },
{ SERVICE_DESK_FEATURE => 1 } { SERVICE_DESK_FEATURE => 1 }
].freeze ].freeze
......
...@@ -44,6 +44,7 @@ module MergeRequests ...@@ -44,6 +44,7 @@ module MergeRequests
@merge_request = merge_request @merge_request = merge_request
return true if project.merge_requests_ff_only_enabled return true if project.merge_requests_ff_only_enabled
return true unless project.feature_available?(:push_rules)
push_rule = merge_request.project.push_rule push_rule = merge_request.project.push_rule
return true unless push_rule return true unless push_rule
......
...@@ -164,6 +164,8 @@ module Projects ...@@ -164,6 +164,8 @@ module Projects
end end
def create_predefined_push_rule def create_predefined_push_rule
return unless project.feature_available?(:push_rules)
predefined_push_rule = PushRule.find_by(is_sample: true) predefined_push_rule = PushRule.find_by(is_sample: true)
if predefined_push_rule if predefined_push_rule
......
...@@ -34,23 +34,10 @@ ...@@ -34,23 +34,10 @@
Abuse Reports Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all)) %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License' do
%span
License
- if akismet_enabled? - if akismet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
%span %span
Spam Logs Spam Logs
= nav_link(controller: :push_rules) do = render 'layouts/nav/admin_ee'
= link_to admin_push_rule_path, title: 'Push Rules' do
%span
Push Rules
= nav_link(controller: :geo_nodes) do
= link_to admin_geo_nodes_path, title: 'Geo Nodes' do
%span
Geo Nodes
= nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License' do
%span
License
- if License.feature_available?(:push_rules)
= nav_link(controller: :push_rules) do
= link_to admin_push_rule_path, title: 'Push Rules' do
%span
Push Rules
= nav_link(controller: :geo_nodes) do
= link_to admin_geo_nodes_path, title: 'Geo Nodes' do
%span
Geo Nodes
- return unless @project.feature_available?(:push_rules)
- expanded = Rails.env.test? - expanded = Rails.env.test?
%section.settings %section.settings
.settings-header .settings-header
......
---
title: Introduce namespace license checks for Push Rules (EES)
merge_request: 2335
author:
...@@ -2,6 +2,7 @@ module API ...@@ -2,6 +2,7 @@ module API
class ProjectPushRule < Grape::API class ProjectPushRule < Grape::API
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
before { check_project_feature_available!(:push_rules) }
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
......
...@@ -3,6 +3,7 @@ module API ...@@ -3,6 +3,7 @@ module API
class ProjectGitHook < Grape::API class ProjectGitHook < Grape::API
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
before { check_project_feature_available!(:push_rules) }
DEPRECATION_MESSAGE = 'This endpoint is deprecated, replaced with push_rules, and will be removed in GitLab 9.0.'.freeze DEPRECATION_MESSAGE = 'This endpoint is deprecated, replaced with push_rules, and will be removed in GitLab 9.0.'.freeze
......
...@@ -3,6 +3,7 @@ module API ...@@ -3,6 +3,7 @@ module API
class ProjectPushRule < Grape::API class ProjectPushRule < Grape::API
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
before { check_project_feature_available!(:push_rules) }
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
......
...@@ -9,6 +9,10 @@ module EE ...@@ -9,6 +9,10 @@ module EE
user user
end end
def check_project_feature_available!(feature)
not_found! unless user_project.feature_available?(feature)
end
end end
end end
end end
...@@ -142,7 +142,7 @@ module Gitlab ...@@ -142,7 +142,7 @@ module Gitlab
end end
def push_rule_check def push_rule_check
return unless @newrev && @oldrev return unless @newrev && @oldrev && project.feature_available?(:push_rules)
push_rule = project.push_rule push_rule = project.push_rule
......
...@@ -8,17 +8,52 @@ describe Admin::PushRulesController do ...@@ -8,17 +8,52 @@ describe Admin::PushRulesController do
end end
describe '#update' do describe '#update' do
it 'updates sample push rule' do let(:params) do
params = {
{ deny_delete_tag: true, delete_branch_regex: "any", commit_message_regex: "any", deny_delete_tag: true, delete_branch_regex: "any", commit_message_regex: "any",
force_push_regex: "any", author_email_regex: "any", member_check: true, file_name_regex: "any", force_push_regex: "any", author_email_regex: "any", member_check: true, file_name_regex: "any",
max_file_size: "0", prevent_secrets: true } max_file_size: "0", prevent_secrets: true
}
end
it 'updates sample push rule' do
expect_any_instance_of(PushRule).to receive(:update_attributes).with(params) expect_any_instance_of(PushRule).to receive(:update_attributes).with(params)
patch :update, push_rule: params patch :update, push_rule: params
expect(response).to redirect_to(admin_push_rule_path) expect(response).to redirect_to(admin_push_rule_path)
end end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404' do
patch :update, push_rule: params
expect(response).to have_http_status(404)
end
end
end
describe '#show' do
it 'returns 200' do
get :show
expect(response).to have_http_status(200)
end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404' do
get :show
expect(response).to have_http_status(404)
end
end
end end
end end
require 'spec_helper'
describe Projects::PushRulesController do
let(:project) { create(:empty_project, push_rule: create(:push_rule, prevent_secrets: false)) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
end
describe '#update' do
def do_update
patch :update, namespace_id: project.namespace, project_id: project, id: 1, push_rule: { prevent_secrets: true }
end
it 'updates the push rule' do
do_update
expect(response).to have_http_status(302)
expect(project.push_rule(true).prevent_secrets).to be_truthy
end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404' do
do_update
expect(response).to have_http_status(404)
end
end
end
end
require 'spec_helper'
describe Projects::Settings::RepositoryController do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
end
describe 'GET show' do
context 'push rule' do
subject(:push_rule) { assigns(:push_rule) }
it 'is created' do
get :show, namespace_id: project.namespace, project_id: project
is_expected.to be_persisted
end
context 'unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'is not created' do
get :show, namespace_id: project.namespace, project_id: project
is_expected.to be_nil
end
end
end
end
end
...@@ -15,19 +15,33 @@ describe 'Project settings > [EE] repository', feature: true do ...@@ -15,19 +15,33 @@ describe 'Project settings > [EE] repository', feature: true do
let(:commit_message) { 'Required part of every message' } let(:commit_message) { 'Required part of every message' }
let(:input_id) { 'push_rule_commit_message_regex' } let(:input_id) { 'push_rule_commit_message_regex' }
before do context 'push rules licensed' do
visit namespace_project_settings_repository_path(project.namespace, project) before do
visit namespace_project_settings_repository_path(project.namespace, project)
fill_in input_id, with: commit_message fill_in input_id, with: commit_message
click_button 'Save Push Rules' click_button 'Save Push Rules'
end end
it 'displays the new value in the form' do
expect(find("##{input_id}").value).to eq commit_message
end
it 'displays the new value in the form' do it 'saves the new value' do
expect(find("##{input_id}").value).to eq commit_message expect(project.push_rule.commit_message_regex).to eq commit_message
end
end end
it 'saves the new value' do context 'push rules unlicensed' do
expect(project.push_rule.commit_message_regex).to eq commit_message before do
stub_licensed_features(push_rules: false)
visit namespace_project_settings_repository_path(project.namespace, project)
end
it 'hides push rule settings' do
expect(page).not_to have_content('Push Rules')
end
end end
end end
......
...@@ -165,6 +165,14 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -165,6 +165,14 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
end end
context 'push rules checks' do context 'push rules checks' do
shared_examples 'check ignored when push rule unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it { is_expected.to be_truthy }
end
let(:project) { create(:project, :public, push_rule: push_rule) } let(:project) { create(:project, :public, push_rule: push_rule) }
before do before do
...@@ -183,6 +191,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -183,6 +191,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
project.add_master(user) project.add_master(user)
end end
it_behaves_like 'check ignored when push rule unlicensed'
it 'returns an error if the rule denies tag deletion' do it 'returns an error if the rule denies tag deletion' do
expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You cannot delete a tag') expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You cannot delete a tag')
end end
...@@ -199,6 +209,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -199,6 +209,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
context 'commit message rules' do context 'commit message rules' do
let(:push_rule) { create(:push_rule, :commit_message) } let(:push_rule) { create(:push_rule, :commit_message) }
it_behaves_like 'check ignored when push rule unlicensed'
it 'returns an error if the rule fails' do it 'returns an error if the rule fails' do
expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "Commit message does not follow the pattern '#{push_rule.commit_message_regex}'") expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "Commit message does not follow the pattern '#{push_rule.commit_message_regex}'")
end end
...@@ -212,6 +224,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -212,6 +224,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
allow_any_instance_of(Commit).to receive(:author_email).and_return('mike@valid.com') allow_any_instance_of(Commit).to receive(:author_email).and_return('mike@valid.com')
end end
it_behaves_like 'check ignored when push rule unlicensed'
it 'returns an error if the rule fails for the committer' do it 'returns an error if the rule fails for the committer' do
allow_any_instance_of(Commit).to receive(:committer_email).and_return('ana@invalid.com') allow_any_instance_of(Commit).to receive(:committer_email).and_return('ana@invalid.com')
...@@ -233,6 +247,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -233,6 +247,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
allow_any_instance_of(Commit).to receive(:author_email).and_return('some@mail.com') allow_any_instance_of(Commit).to receive(:author_email).and_return('some@mail.com')
end end
it_behaves_like 'check ignored when push rule unlicensed'
it 'returns an error if the commit author is not a GitLab member' do it 'returns an error if the commit author is not a GitLab member' do
expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "Author 'some@mail.com' is not a member of team") expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "Author 'some@mail.com' is not a member of team")
end end
...@@ -243,6 +259,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -243,6 +259,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
context 'file name regex check' do context 'file name regex check' do
let(:push_rule) { create(:push_rule, file_name_regex: 'READ*') } let(:push_rule) { create(:push_rule, file_name_regex: 'READ*') }
it_behaves_like 'check ignored when push rule unlicensed'
it "returns an error if a new or renamed filed doesn't match the file name regex" do it "returns an error if a new or renamed filed doesn't match the file name regex" do
expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "File name README was blacklisted by the pattern READ*.") expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "File name README was blacklisted by the pattern READ*.")
end end
...@@ -251,6 +269,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -251,6 +269,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
context 'blacklisted files check' do context 'blacklisted files check' do
let(:push_rule) { create(:push_rule, prevent_secrets: true) } let(:push_rule) { create(:push_rule, prevent_secrets: true) }
it_behaves_like 'check ignored when push rule unlicensed'
it "returns true if there is no blacklisted files" do it "returns true if there is no blacklisted files" do
new_rev = nil new_rev = nil
...@@ -305,6 +325,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -305,6 +325,8 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
allow_any_instance_of(Blob).to receive(:size).and_return(2.megabytes) allow_any_instance_of(Blob).to receive(:size).and_return(2.megabytes)
end end
it_behaves_like 'check ignored when push rule unlicensed'
it 'returns an error if file exceeds the maximum file size' do it 'returns an error if file exceeds the maximum file size' do
expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "File \"README\" is larger than the allowed size of 1 MB") expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, "File \"README\" is larger than the allowed size of 1 MB")
end end
......
...@@ -11,6 +11,22 @@ describe Project, models: true do ...@@ -11,6 +11,22 @@ describe Project, models: true do
it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:namespace) } it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:namespace) }
end end
describe '#push_rule' do
let(:project) { create(:project, push_rule: create(:push_rule)) }
subject(:push_rule) { project.push_rule(true) }
it { is_expected.not_to be_nil }
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it { is_expected.to be_nil }
end
end
describe '#feature_available?' do describe '#feature_available?' do
let(:namespace) { build_stubbed(:namespace) } let(:namespace) { build_stubbed(:namespace) }
let(:project) { build_stubbed(:project, namespace: namespace) } let(:project) { build_stubbed(:project, namespace: namespace) }
......
...@@ -34,13 +34,26 @@ describe Projects::CreateService, '#execute', services: true do ...@@ -34,13 +34,26 @@ describe Projects::CreateService, '#execute', services: true do
end end
context 'git hook sample' do context 'git hook sample' do
let!(:sample) { create(:push_rule_sample) }
subject(:push_rule) { create_project(user, opts).push_rule }
it 'creates git hook from sample' do it 'creates git hook from sample' do
push_rule_sample = create(:push_rule_sample) is_expected.to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
push_rule = create_project(user, opts).push_rule context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
[:force_push_regex, :deny_delete_tag, :delete_branch_regex, :commit_message_regex].each do |attr_name| it 'ignores the push rule sample' do
expect(push_rule.send(attr_name)).to eq push_rule_sample.send(attr_name) is_expected.to be_nil
end end
end end
end end
......
...@@ -242,6 +242,16 @@ describe MergeRequests::MergeService, services: true do ...@@ -242,6 +242,16 @@ describe MergeRequests::MergeService, services: true do
end end
describe '#hooks_validation_pass?' do describe '#hooks_validation_pass?' do
shared_examples 'hook validations are skipped when push rules unlicensed' do
subject { service.hooks_validation_pass?(merge_request) }
before do
stub_licensed_features(push_rules: false)
end
it { is_expected.to be_truthy }
end
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
it 'returns true when valid' do it 'returns true when valid' do
...@@ -253,6 +263,8 @@ describe MergeRequests::MergeService, services: true do ...@@ -253,6 +263,8 @@ describe MergeRequests::MergeService, services: true do
allow(project).to receive(:push_rule) { build(:push_rule, commit_message_regex: 'unmatched pattern .*') } allow(project).to receive(:push_rule) { build(:push_rule, commit_message_regex: 'unmatched pattern .*') }
end end
it_behaves_like 'hook validations are skipped when push rules unlicensed'
it 'returns false and saves error when invalid' do it 'returns false and saves error when invalid' do
expect(service.hooks_validation_pass?(merge_request)).to be_falsey expect(service.hooks_validation_pass?(merge_request)).to be_falsey
expect(merge_request.merge_error).not_to be_empty expect(merge_request.merge_error).not_to be_empty
...@@ -264,6 +276,8 @@ describe MergeRequests::MergeService, services: true do ...@@ -264,6 +276,8 @@ describe MergeRequests::MergeService, services: true do
allow(project).to receive(:push_rule) { build(:push_rule, author_email_regex: '.*@unmatchedemaildomain.com') } allow(project).to receive(:push_rule) { build(:push_rule, author_email_regex: '.*@unmatchedemaildomain.com') }
end end
it_behaves_like 'hook validations are skipped when push rules unlicensed'
it 'returns false and saves error when invalid' do it 'returns false and saves error when invalid' do
expect(service.hooks_validation_pass?(merge_request)).to be_falsey expect(service.hooks_validation_pass?(merge_request)).to be_falsey
expect(merge_request.merge_error).not_to be_empty expect(merge_request.merge_error).not_to be_empty
......
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