2017-05-31 17:06:01 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2017-07-10 10:24:02 -04:00
|
|
|
describe Feature do
|
2018-08-20 14:52:56 -04:00
|
|
|
before do
|
|
|
|
# We mock all calls to .enabled? to return true in order to force all
|
|
|
|
# specs to run the feature flag gated behavior, but here we need a clean
|
|
|
|
# behavior from the class
|
|
|
|
allow(described_class).to receive(:enabled?).and_call_original
|
|
|
|
end
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
describe '.get' do
|
|
|
|
let(:feature) { double(:feature) }
|
|
|
|
let(:key) { 'my_feature' }
|
|
|
|
|
|
|
|
it 'returns the Flipper feature' do
|
2017-06-21 09:48:12 -04:00
|
|
|
expect_any_instance_of(Flipper::DSL).to receive(:feature).with(key)
|
|
|
|
.and_return(feature)
|
2017-05-31 17:06:01 -04:00
|
|
|
|
|
|
|
expect(described_class.get(key)).to be(feature)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-10-30 11:10:31 -04:00
|
|
|
describe '.persisted_names' do
|
|
|
|
it 'returns the names of the persisted features' do
|
|
|
|
Feature::FlipperFeature.create!(key: 'foo')
|
|
|
|
|
|
|
|
expect(described_class.persisted_names).to eq(%w[foo])
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns an empty Array when no features are presisted' do
|
|
|
|
expect(described_class.persisted_names).to be_empty
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'caches the feature names when request store is active', :request_store do
|
|
|
|
Feature::FlipperFeature.create!(key: 'foo')
|
|
|
|
|
|
|
|
expect(Feature::FlipperFeature)
|
|
|
|
.to receive(:feature_names)
|
|
|
|
.once
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
2.times do
|
|
|
|
expect(described_class.persisted_names).to eq(%w[foo])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.persisted?' do
|
2018-07-19 20:05:24 -04:00
|
|
|
context 'when the feature is persisted' do
|
|
|
|
it 'returns true when feature name is a string' do
|
|
|
|
Feature::FlipperFeature.create!(key: 'foo')
|
|
|
|
|
|
|
|
feature = double(:feature, name: 'foo')
|
|
|
|
|
|
|
|
expect(described_class.persisted?(feature)).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when feature name is a symbol' do
|
|
|
|
Feature::FlipperFeature.create!(key: 'foo')
|
2017-10-30 11:10:31 -04:00
|
|
|
|
2018-07-19 20:05:24 -04:00
|
|
|
feature = double(:feature, name: :foo)
|
2017-10-30 11:10:31 -04:00
|
|
|
|
2018-07-19 20:05:24 -04:00
|
|
|
expect(described_class.persisted?(feature)).to eq(true)
|
|
|
|
end
|
2017-10-30 11:10:31 -04:00
|
|
|
end
|
|
|
|
|
2018-07-19 20:05:24 -04:00
|
|
|
context 'when the feature is not persisted' do
|
|
|
|
it 'returns false when feature name is a string' do
|
|
|
|
feature = double(:feature, name: 'foo')
|
|
|
|
|
|
|
|
expect(described_class.persisted?(feature)).to eq(false)
|
|
|
|
end
|
2017-10-30 11:10:31 -04:00
|
|
|
|
2018-07-19 20:05:24 -04:00
|
|
|
it 'returns false when feature name is a symbol' do
|
|
|
|
feature = double(:feature, name: :bar)
|
|
|
|
|
|
|
|
expect(described_class.persisted?(feature)).to eq(false)
|
|
|
|
end
|
2017-10-30 11:10:31 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
describe '.all' do
|
|
|
|
let(:features) { Set.new }
|
|
|
|
|
|
|
|
it 'returns the Flipper features as an array' do
|
2017-06-21 09:48:12 -04:00
|
|
|
expect_any_instance_of(Flipper::DSL).to receive(:features)
|
|
|
|
.and_return(features)
|
2017-05-31 17:06:01 -04:00
|
|
|
|
|
|
|
expect(described_class.all).to eq(features.to_a)
|
|
|
|
end
|
|
|
|
end
|
2018-05-31 12:12:48 -04:00
|
|
|
|
|
|
|
describe '.flipper' do
|
|
|
|
shared_examples 'a memoized Flipper instance' do
|
|
|
|
it 'memoizes the Flipper instance' do
|
|
|
|
expect(Flipper).to receive(:new).once.and_call_original
|
|
|
|
|
|
|
|
2.times do
|
|
|
|
described_class.flipper
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when request store is inactive' do
|
|
|
|
before do
|
|
|
|
described_class.instance_variable_set(:@flipper, nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'a memoized Flipper instance'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when request store is inactive', :request_store do
|
|
|
|
it_behaves_like 'a memoized Flipper instance'
|
|
|
|
end
|
|
|
|
end
|
2018-08-20 14:52:56 -04:00
|
|
|
|
|
|
|
describe '.enabled?' do
|
|
|
|
it 'returns false for undefined feature' do
|
|
|
|
expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey
|
|
|
|
end
|
|
|
|
|
2018-09-04 14:34:37 -04:00
|
|
|
it 'returns true for undefined feature with default_enabled' do
|
|
|
|
expect(described_class.enabled?(:some_random_feature_flag, default_enabled: true)).to be_truthy
|
|
|
|
end
|
|
|
|
|
2018-08-20 14:52:56 -04:00
|
|
|
it 'returns false for existing disabled feature in the database' do
|
|
|
|
described_class.disable(:disabled_feature_flag)
|
|
|
|
|
|
|
|
expect(described_class.enabled?(:disabled_feature_flag)).to be_falsey
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true for existing enabled feature in the database' do
|
|
|
|
described_class.enable(:enabled_feature_flag)
|
|
|
|
|
|
|
|
expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with an individual actor' do
|
|
|
|
CustomActor = Struct.new(:flipper_id)
|
|
|
|
|
|
|
|
let(:actor) { CustomActor.new(flipper_id: 'CustomActor:5') }
|
|
|
|
let(:another_actor) { CustomActor.new(flipper_id: 'CustomActor:10') }
|
|
|
|
|
|
|
|
before do
|
|
|
|
described_class.enable(:enabled_feature_flag, actor)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when same actor is informed' do
|
|
|
|
expect(described_class.enabled?(:enabled_feature_flag, actor)).to be_truthy
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false when different actor is informed' do
|
|
|
|
expect(described_class.enabled?(:enabled_feature_flag, another_actor)).to be_falsey
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false when no actor is informed' do
|
|
|
|
expect(described_class.enabled?(:enabled_feature_flag)).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.disable?' do
|
|
|
|
it 'returns true for undefined feature' do
|
|
|
|
expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy
|
|
|
|
end
|
|
|
|
|
2018-09-04 14:34:37 -04:00
|
|
|
it 'returns false for undefined feature with default_enabled' do
|
|
|
|
expect(described_class.disabled?(:some_random_feature_flag, default_enabled: true)).to be_falsey
|
|
|
|
end
|
|
|
|
|
2018-08-20 14:52:56 -04:00
|
|
|
it 'returns true for existing disabled feature in the database' do
|
|
|
|
described_class.disable(:disabled_feature_flag)
|
|
|
|
|
|
|
|
expect(described_class.disabled?(:disabled_feature_flag)).to be_truthy
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false for existing enabled feature in the database' do
|
|
|
|
described_class.enable(:enabled_feature_flag)
|
|
|
|
|
|
|
|
expect(described_class.disabled?(:enabled_feature_flag)).to be_falsey
|
|
|
|
end
|
|
|
|
end
|
2017-05-31 17:06:01 -04:00
|
|
|
end
|