debian-mirror-gitlab/spec/lib/gitlab/utils/override_spec.rb
2023-03-04 22:38:38 +05:30

304 lines
7.4 KiB
Ruby

# frozen_string_literal: true
require 'fast_spec_helper'
# Patching ActiveSupport::Concern
require_relative '../../../../config/initializers/0_as_concern'
RSpec.describe Gitlab::Utils::Override do
let(:base) do
Struct.new(:good) do
def self.good
0
end
end
end
let(:derived) { Class.new(base).tap { |m| m.extend described_class } }
let(:extension) { Module.new.tap { |m| m.extend described_class } }
let(:prepending_class) { base.tap { |m| m.prepend extension } }
let(:including_class) { base.tap { |m| m.include extension } }
let(:prepending_class_methods) do
base.tap { |m| m.singleton_class.prepend extension }
end
let(:extending_class_methods) do
base.tap { |m| m.extend extension }
end
let(:klass) { subject }
def good(mod, bad_arity: false, negative_arity: false)
mod.module_eval do
override :good
if bad_arity
def good(num); end
elsif negative_arity
def good(*args)
super.succ
end
else
def good
super.succ
end
end
end
mod
end
def bad(mod)
mod.module_eval do
override :bad
def bad
true
end
end
mod
end
shared_examples 'checking as intended' do
it 'checks ok for overriding method' do
good(subject)
result = instance.good
expect(result).to eq(1)
described_class.verify!
end
it 'checks ok for overriding method using negative arity' do
good(subject, negative_arity: true)
result = instance.good
expect(result).to eq(1)
described_class.verify!
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
instance.bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
it 'raises NotImplementedError when overriding a method with different arity' do
expect do
good(subject, bad_arity: true)
instance.good(1)
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
shared_examples 'checking as intended, nothing was overridden' do
it 'raises NotImplementedError because it is not overriding it' do
expect do
good(subject)
instance.good
described_class.verify!
end.to raise_error(NotImplementedError)
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
instance.bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
shared_examples 'nothing happened' do
it 'does not complain when it is overriding something' do
good(subject)
result = instance.good
expect(result).to eq(1)
described_class.verify!
end
it 'does not complain when it is not overriding anything' do
bad(subject)
result = instance.bad
expect(result).to eq(true)
described_class.verify!
end
end
before do
# Make sure we're not touching the internal cache
allow(described_class).to receive(:extensions).and_return({})
end
describe '#override' do
context 'when instance is klass.new(0)' do
let(:instance) { klass.new(0) }
context 'when STATIC_VERIFICATION is set' do
before do
stub_env('STATIC_VERIFICATION', 'true')
end
context 'when subject is a class' do
subject { derived }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is including it' do
subject { extension }
let(:klass) { including_class }
it_behaves_like 'checking as intended, nothing was overridden'
end
context 'when ActiveSupport::Concern and class_methods are used' do
# We need to give module names before using Override
let(:base) { stub_const('Base', Module.new) }
let(:extension) { stub_const('Extension', Module.new) }
def define_base(method_name:)
base.module_eval do
extend ActiveSupport::Concern
class_methods do
define_method(method_name) do
:f
end
end
end
end
def define_extension(method_name:)
extension.module_eval do
extend ActiveSupport::Concern
class_methods do
extend Gitlab::Utils::Override
override method_name
define_method(method_name) do
:g
end
end
end
end
context 'when it is defining a overriding method' do
before do
define_base(method_name: :f)
define_extension(method_name: :f)
base.prepend(extension)
end
it 'verifies' do
expect(base.f).to eq(:g)
described_class.verify!
end
end
context 'when it is not defining a overriding method' do
before do
define_base(method_name: :f)
define_extension(method_name: :g)
base.prepend(extension)
end
it 'raises NotImplementedError' do
expect(base.f).to eq(:f)
expect { described_class.verify! }
.to raise_error(NotImplementedError)
end
end
end
end
context 'when STATIC_VERIFICATION is not set' do
before do
stub_env('STATIC_VERIFICATION', nil)
end
context 'when subject is a class' do
subject { derived }
it_behaves_like 'nothing happened'
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class }
it_behaves_like 'nothing happened'
end
context 'when subject is a module, and class is including it' do
subject { extension }
let(:klass) { including_class }
it 'does not complain when it is overriding something' do
good(subject)
result = instance.good
expect(result).to eq(0)
described_class.verify!
end
it 'does not complain when it is not overriding anything' do
bad(subject)
result = instance.bad
expect(result).to eq(true)
described_class.verify!
end
end
end
end
context 'when instance is klass' do
let(:instance) { klass }
context 'when STATIC_VERIFICATION is set' do
before do
stub_env('STATIC_VERIFICATION', 'true')
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class_methods }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is extending it' do
subject { extension }
let(:klass) { extending_class_methods }
it_behaves_like 'checking as intended, nothing was overridden'
end
end
end
end
end