Commit f06c83ba authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'improve-stub-semantic' into 'master'

Improve `#stub_feature_flags` semantic

See merge request gitlab-org/gitlab!31654
parents b933db38 e2843005
......@@ -318,19 +318,41 @@ stub_feature_flags(ci_live_trace: false)
Feature.enabled?(:ci_live_trace) # => false
```
If you wish to set up a test where a feature flag is disabled for some
actors and not others, you can specify this in options passed to the
helper. For example, to disable the `ci_live_trace` feature flag for a
specifc project:
If you wish to set up a test where a feature flag is enabled only
for some actors and not others, you can specify this in options
passed to the helper. For example, to enable the `ci_live_trace`
feature flag for a specifc project:
```ruby
project1, project2 = build_list(:project, 2)
# Feature will only be disabled for project1
stub_feature_flags(ci_live_trace: { enabled: false, thing: project1 })
# Feature will only be enabled for project1
stub_feature_flags(ci_live_trace: project1)
Feature.enabled?(:ci_live_trace, project1) # => false
Feature.enabled?(:ci_live_trace, project2) # => true
Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, project1) # => true
Feature.enabled?(:ci_live_trace, project2) # => false
```
This represents an actual behavior of FlipperGate:
1. You can enable an override for a specified actor to be enabled
1. You can disable (remove) an override for a specified actor,
fallbacking to default state
1. There's no way to model that you explicitly disable a specified actor
```ruby
Feature.enable(:my_feature)
Feature.disable(:my_feature, project1)
Feature.enabled?(:my_feature) # => true
Feature.enabled?(:my_feature, project1) # => true
```
```ruby
Feature.disable(:my_feature2)
Feature.enable(:my_feature2, project1)
Feature.enabled?(:my_feature2) # => false
Feature.enabled?(:my_feature2, project1) # => true
```
### Pristine test environments
......
......@@ -97,7 +97,7 @@ describe 'Project navbar' do
context 'when requirements is available' do
before do
stub_licensed_features(requirements: true)
stub_feature_flags(requirements_management: { enabled: true, thing: project })
stub_feature_flags(requirements_management: true)
insert_after_nav_item(
_('Merge Requests'),
......
......@@ -26,7 +26,7 @@ describe 'Requirements list', :js do
before do
stub_licensed_features(requirements: true)
stub_feature_flags(requirements_management: { enabled: true, thing: project })
stub_feature_flags(requirements_management: [project])
project.add_maintainer(user)
project.add_guest(user_guest)
......
......@@ -143,9 +143,7 @@ describe GroupsHelper do
describe '#show_group_activity_analytics?' do
before do
allow(Feature).to receive(:enabled?).with(:group_activity_analytics, group).and_return(false)
allow(Feature).to receive(:enabled?).with(:group_activity_analytics).and_return(true)
stub_feature_flags(group_activity_analytics: feature_available)
stub_licensed_features(group_activity_analytics: feature_available)
allow(helper).to receive(:current_user) { current_user }
......@@ -238,9 +236,11 @@ describe GroupsHelper do
expect(helper.show_administration_nav?(subgroup)).to be false
end
context 'when `group_administration_nav_item` feature flag is disabled for the group' do
context 'when `group_administration_nav_item` feature flag is enabled for another group' do
let(:another_group) { create(:group) }
before do
stub_feature_flags(group_administration_nav_item: { enabled: false, thing: group })
stub_feature_flags(group_administration_nav_item: another_group)
end
it 'returns false' do
......
......@@ -45,7 +45,7 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do
describe 'enforced sso expiry' do
before do
stub_feature_flags(enforced_sso_requires_session: { enabled: true, thing: saml_provider.group })
stub_feature_flags(enforced_sso_requires_session: saml_provider.group)
end
it 'returns true if a sign in is recently recorded' do
......@@ -89,7 +89,7 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do
end
it 'allows access when the sso enforcement feature is disabled' do
stub_feature_flags(enforced_sso: { enabled: false, thing: saml_provider.group })
stub_feature_flags(enforced_sso: false)
expect(subject).not_to be_access_restricted
end
......@@ -110,7 +110,7 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do
context 'is restricted' do
before do
stub_feature_flags(enforced_sso_requires_session: { enabled: true, thing: root_group })
stub_feature_flags(enforced_sso_requires_session: root_group)
end
it 'for a group' do
......@@ -133,13 +133,13 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do
context 'is not restricted' do
it 'for the group without configured saml_provider' do
group = create(:group)
stub_feature_flags(enforced_sso_requires_session: { enabled: true, thing: group })
stub_feature_flags(enforced_sso_requires_session: group)
expect(described_class).not_to be_group_access_restricted(group)
end
it 'for the group without the feature flag' do
stub_feature_flags(enforced_sso_requires_session: { enabled: false, thing: root_group })
stub_feature_flags(enforced_sso_requires_session: false)
expect(described_class).not_to be_group_access_restricted(root_group)
end
......
......@@ -83,7 +83,7 @@ describe Ci::Build do
context 'and :ci_minutes_track_for_public_projects FF is disabled' do
before do
stub_feature_flags(ci_minutes_track_for_public_projects: { enabled: false, thing: project.shared_runners_limit_namespace })
stub_feature_flags(ci_minutes_track_for_public_projects: false)
end
it_behaves_like 'depends on runner presence and type'
......
......@@ -997,7 +997,7 @@ describe Project do
context 'and :ci_minutes_track_for_public_projects FF is disabled' do
before do
stub_feature_flags(ci_minutes_track_for_public_projects: { enabled: false, thing: project.shared_runners_limit_namespace })
stub_feature_flags(ci_minutes_track_for_public_projects: false)
end
it { is_expected.to be_falsey }
......
......@@ -134,8 +134,7 @@ describe GroupPolicy do
let(:current_user) { developer }
before do
allow(Feature).to receive(:enabled?).with(:group_activity_analytics, group).and_return(false)
stub_feature_flags(group_activity_analytics: true)
stub_licensed_features(group_activity_analytics: true)
end
......@@ -146,9 +145,7 @@ describe GroupPolicy do
let(:current_user) { developer }
before do
allow(Feature).to receive(:enabled?).with(:group_activity_analytics, group).and_return(false)
allow(Feature).to receive(:enabled?).with(:group_activity_analytics).and_return(true)
stub_feature_flags(group_activity_analytics: false)
stub_licensed_features(group_activity_analytics: false)
end
......
......@@ -13,8 +13,7 @@ describe API::Analytics::GroupActivityAnalytics do
shared_examples 'GET group_activity' do |activity, count|
let(:feature_available) { true }
let(:feature_enabled_globally) { true }
let(:feature_enabled_for_group) { true }
let(:feature_enabled_for) { group }
let(:params) { { group_path: group.full_path } }
let(:current_user) { reporter }
let(:request) do
......@@ -22,25 +21,25 @@ describe API::Analytics::GroupActivityAnalytics do
end
before do
allow(Feature).to receive(:enabled?).with(:group_activity_analytics, group).and_return(feature_enabled_for_group)
allow(Feature).to receive(:enabled?).with(:group_activity_analytics).and_return(feature_enabled_globally)
stub_feature_flags(group_activity_analytics: feature_enabled_for)
stub_licensed_features(group_activity_analytics: feature_available)
request
end
it 'is successful' do
expect(response).to have_gitlab_http_status(:ok)
end
context 'when feature is enabled for a group' do
it 'is successful' do
expect(response).to have_gitlab_http_status(:ok)
end
it 'is returns a count' do
expect(response.parsed_body).to eq({ "#{activity}_count" => count })
it 'is returns a count' do
expect(response.parsed_body).to eq({ "#{activity}_count" => count })
end
end
context 'when feature is not available in plan' do
let(:feature_available) { false }
let(:feature_enabled_for_group) { false }
let(:feature_enabled_for) { false }
it 'is returns `forbidden`' do
expect(response).to have_gitlab_http_status(:forbidden)
......@@ -48,8 +47,7 @@ describe API::Analytics::GroupActivityAnalytics do
end
context 'when feature is disabled globally' do
let(:feature_enabled_globally) { false }
let(:feature_enabled_for_group) { false }
let(:feature_enabled_for) { false }
it 'is returns `forbidden`' do
expect(response).to have_gitlab_http_status(:forbidden)
......
......@@ -36,7 +36,7 @@ describe EE::MergeRequestMetricsService do
context 'when `store_merge_request_line_metrics` feature flag is disabled' do
before do
stub_feature_flags(store_merge_request_line_metrics: { enabled: false, thing: merge_request.target_project })
stub_feature_flags(store_merge_request_line_metrics: false)
end
it 'does not update line counts' do
......
......@@ -80,7 +80,7 @@ RSpec.shared_examples 'Insights page' do
context 'without correct license' do
before do
stub_feature_flags(insights: { enabled: false, thing: entity })
stub_feature_flags(insights: false)
stub_licensed_features(insights: false)
end
......
......@@ -29,7 +29,7 @@ RSpec.shared_examples 'an entity with alpha/beta feature support' do
context 'and enabled for the entity' do
before do
stub_feature_flags(insights: { enabled: true, thing: entity })
stub_feature_flags(insights: entity)
end
it { expect(entity.public_send(method_name, :insights)).to be_truthy }
......@@ -59,7 +59,7 @@ RSpec.shared_examples 'an entity with alpha/beta feature support' do
context 'and enabled for the entity' do
before do
stub_feature_flags(insights: { enabled: true, thing: entity })
stub_feature_flags(insights: entity)
end
it { expect(entity.public_send(method_name, :insights)).to be_truthy }
......
......@@ -27,7 +27,7 @@ describe Groups::Settings::IntegrationsController do
context 'when group_level_integrations not enabled' do
it 'returns not_found' do
stub_feature_flags(group_level_integrations: { enabled: false, thing: group })
stub_feature_flags(group_level_integrations: false)
get :index, params: { group_id: group }
......@@ -60,7 +60,7 @@ describe Groups::Settings::IntegrationsController do
context 'when group_level_integrations not enabled' do
it 'returns not_found' do
stub_feature_flags(group_level_integrations: { enabled: false, thing: group })
stub_feature_flags(group_level_integrations: false)
get :edit, params: { group_id: group, id: Service.available_services_names.sample }
......
......@@ -15,7 +15,7 @@ describe Groups::Settings::RepositoryController do
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: group })
stub_feature_flags(ajax_new_deploy_token: false)
entity.add_owner(user)
end
......
......@@ -878,7 +878,7 @@ describe Projects::PipelinesController do
context 'when junit_pipeline_screenshots_view is enabled' do
before do
stub_feature_flags(junit_pipeline_screenshots_view: { enabled: true, thing: project })
stub_feature_flags(junit_pipeline_screenshots_view: project)
end
context 'when test_report contains attachment and scope is with_attachment as a URL param' do
......@@ -907,7 +907,7 @@ describe Projects::PipelinesController do
context 'when junit_pipeline_screenshots_view is disabled' do
before do
stub_feature_flags(junit_pipeline_screenshots_view: { enabled: false, thing: project })
stub_feature_flags(junit_pipeline_screenshots_view: false)
end
context 'when test_report contains attachment and scope is with_attachment as a URL param' do
......
......@@ -36,7 +36,7 @@ describe Projects::Settings::RepositoryController do
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
stub_feature_flags(ajax_new_deploy_token: false)
end
it_behaves_like 'a created deploy token' do
......
......@@ -27,7 +27,7 @@ describe 'Explore Groups', :js do
end
before do
stub_feature_flags({ vue_issuables_list: { enabled: false, thing: group } })
stub_feature_flags(vue_issuables_list: false)
end
shared_examples 'renders public and internal projects' do
......
......@@ -12,7 +12,7 @@ describe 'Group issues page' do
let(:path) { issues_group_path(group) }
before do
stub_feature_flags({ vue_issuables_list: { enabled: false, thing: group } })
stub_feature_flags(vue_issuables_list: false)
end
context 'with shared examples' do
......
......@@ -20,7 +20,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js
end
before do
stub_feature_flags(web_ide_default: false, single_mr_diff_view: { enabled: false, thing: target_project }, code_navigation: false)
stub_feature_flags(web_ide_default: false, single_mr_diff_view: false, code_navigation: false)
target_project.add_maintainer(user)
sign_in(user)
......
......@@ -10,7 +10,7 @@ describe 'Batch diffs', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
before do
stub_feature_flags(single_mr_diff_view: { enabled: true, thing: project })
stub_feature_flags(single_mr_diff_view: project)
stub_feature_flags(diffs_batch_load: true)
sign_in(project.owner)
......
......@@ -30,7 +30,7 @@ describe 'Projects > Settings > Repository settings' do
before do
stub_container_registry_config(enabled: true)
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
stub_feature_flags(ajax_new_deploy_token: project)
visit project_settings_repository_path(project)
end
......@@ -225,7 +225,7 @@ describe 'Projects > Settings > Repository settings' do
# Removal: https://gitlab.com/gitlab-org/gitlab/-/issues/208828
context 'with the `keep_divergent_refs` feature flag disabled' do
before do
stub_feature_flags(keep_divergent_refs: { enabled: false, thing: project })
stub_feature_flags(keep_divergent_refs: false)
end
it 'hides the "Keep divergent refs" option' do
......
......@@ -11,7 +11,7 @@ describe 'Repository Settings > User sees revoke deploy token modal', :js do
before do
project.add_role(user, role)
sign_in(user)
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
stub_feature_flags(ajax_new_deploy_token: project)
visit(project_settings_repository_path(project))
click_link('Revoke')
end
......
......@@ -14,7 +14,7 @@ describe ForkProjectsFinder do
let(:private_fork_member) { create(:user) }
before do
stub_feature_flags(object_pools: { enabled: false, thing: source_project })
stub_feature_flags(object_pools: source_project)
private_fork.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
private_fork.add_developer(private_fork_member)
......
......@@ -64,7 +64,7 @@ describe Gitlab::ImportExport::RelationTreeRestorer do
shared_examples 'logging of relations creation' do
context 'when log_import_export_relation_creation feature flag is enabled' do
before do
stub_feature_flags(log_import_export_relation_creation: { enabled: true, thing: group })
stub_feature_flags(log_import_export_relation_creation: group)
end
it 'logs top-level relation creation' do
......@@ -79,7 +79,7 @@ describe Gitlab::ImportExport::RelationTreeRestorer do
context 'when log_import_export_relation_creation feature flag is disabled' do
before do
stub_feature_flags(log_import_export_relation_creation: { enabled: false, thing: group })
stub_feature_flags(log_import_export_relation_creation: false)
end
it 'does not log top-level relation creation' do
......
......@@ -28,17 +28,13 @@ describe Gitlab::Tracking do
end
it 'enables features using feature flags' do
stub_feature_flags(additional_snowplow_tracking: true)
allow(Feature).to receive(:enabled?).with(
:additional_snowplow_tracking,
'_group_'
).and_return(false)
stub_feature_flags(additional_snowplow_tracking: :__group__)
addition_feature_fields = {
formTracking: false,
linkClickTracking: false
}
expect(subject.snowplow_options('_group_')).to include(addition_feature_fields)
expect(subject.snowplow_options(:_group_)).to include(addition_feature_fields)
end
end
......
......@@ -76,11 +76,7 @@ describe Gitlab::WikiPages::FrontMatterParser do
let(:raw_content) { with_front_matter }
before do
stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => false)
stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => {
enabled: true,
thing: gate
})
stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => gate)
end
it do
......
......@@ -16,10 +16,7 @@ describe WikiPage do
end
def enable_front_matter_for(thing)
stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => {
thing: thing,
enabled: true
})
stub_feature_flags(Gitlab::WikiPages::FrontMatterParser::FEATURE_FLAG => thing)
end
describe '.group_by_directory' do
......
......@@ -452,7 +452,7 @@ describe API::Internal::Base do
context 'when gitaly_upload_pack_filter feature flag is disabled' do
before do
stub_feature_flags(gitaly_upload_pack_filter: { enabled: false, thing: project })
stub_feature_flags(gitaly_upload_pack_filter: false)
end
it 'returns only maxInputSize and not partial clone git config' do
......@@ -481,7 +481,7 @@ describe API::Internal::Base do
context 'when gitaly_upload_pack_filter feature flag is disabled' do
before do
stub_feature_flags(gitaly_upload_pack_filter: { enabled: false, thing: project })
stub_feature_flags(gitaly_upload_pack_filter: false)
end
it 'returns an empty git config' do
......
......@@ -107,7 +107,7 @@ describe API::RemoteMirrors do
context 'with the `keep_divergent_refs` feature enabled' do
before do
stub_feature_flags(keep_divergent_refs: { enabled: true, project: project })
stub_feature_flags(keep_divergent_refs: project)
end
it 'updates the `keep_divergent_refs` attribute' do
......
......@@ -163,7 +163,7 @@ describe API::Search do
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
stub_feature_flags(users_search: false)
get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
......@@ -336,7 +336,7 @@ describe API::Search do
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
stub_feature_flags(users_search: false)
get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
......@@ -501,7 +501,7 @@ describe API::Search do
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
stub_feature_flags(users_search: false)
get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
......
......@@ -264,7 +264,7 @@ describe Git::WikiPushService, services: true do
context 'but is enabled for a given project' do
before do
stub_feature_flags(wiki_events_on_git_push: { enabled: true, thing: project })
stub_feature_flags(wiki_events_on_git_push: project)
end
it 'creates events' do
......
......@@ -30,21 +30,14 @@ describe Namespaces::CheckStorageSizeService, '#execute' do
expect(subject).to be_error
end
it 'is successful when disabled for the current group' do
stub_feature_flags(namespace_storage_limit: { enabled: false, thing: root_group })
expect(subject).to be_success
end
it 'is successful when feature flag is activated for another group' do
stub_feature_flags(namespace_storage_limit: false)
stub_feature_flags(namespace_storage_limit: { enabled: true, thing: create(:group) })
stub_feature_flags(namespace_storage_limit: create(:group))
expect(subject).to be_success
end
it 'errors when feature flag is activated for the current group' do
stub_feature_flags(namespace_storage_limit: { enabled: true, thing: root_group })
stub_feature_flags(namespace_storage_limit: root_group)
expect(subject).to be_error
end
......
......@@ -173,21 +173,19 @@ RSpec.configure do |config|
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
enabled = example.metadata[:enable_rugged].present?
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag|
allow(Feature).to receive(:enabled?).with(flag).and_return(enabled)
stub_feature_flags(flag => enable_rugged)
end
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enabled)
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
# The following can be removed when we remove the staged rollout strategy
# and we can just enable it using instance wide settings
# (ie. ApplicationSetting#auto_devops_enabled)
allow(Feature).to receive(:enabled?)
.with(:force_autodevops_on_by_default, anything)
.and_return(false)
stub_feature_flags(force_autodevops_on_by_default: false)
# Enable Marginalia feature for all specs in the test suite.
allow(Gitlab::Marginalia).to receive(:cached_feature_enabled?).and_return(true)
......@@ -196,12 +194,8 @@ RSpec.configure do |config|
# is feature-complete and can be made default in place
# of older sidebar.
# See https://gitlab.com/groups/gitlab-org/-/epics/1863
allow(Feature).to receive(:enabled?)
.with(:vue_issuable_sidebar, anything)
.and_return(false)
allow(Feature).to receive(:enabled?)
.with(:vue_issuable_epic_sidebar, anything)
.and_return(false)
stub_feature_flags(vue_issuable_sidebar: false)
stub_feature_flags(vue_issuable_epic_sidebar: false)
# Stub these calls due to being expensive operations
# It can be reenabled for specific tests via:
......
......@@ -9,23 +9,27 @@ module StubFeatureFlags
# Examples
# - `stub_feature_flags(ci_live_trace: false)` ... Disable `ci_live_trace`
# feature flag globally.
# - `stub_feature_flags(ci_live_trace: { enabled: false, thing: project })` ...
# Disable `ci_live_trace` feature flag on the specified project.
# - `stub_feature_flags(ci_live_trace: project)` ...
# - `stub_feature_flags(ci_live_trace: [project1, project2])` ...
# Enable `ci_live_trace` feature flag only on the specified projects.
def stub_feature_flags(features)
features.each do |feature_name, option|
if option.is_a?(Hash)
enabled, thing = option.values_at(:enabled, :thing)
else
enabled = option
thing = nil
end
features.each do |feature_name, actors|
allow(Feature).to receive(:enabled?).with(feature_name, any_args).and_return(false)
allow(Feature).to receive(:enabled?).with(feature_name.to_s, any_args).and_return(false)
Array(actors).each do |actor|
raise ArgumentError, "actor cannot be Hash" if actor.is_a?(Hash)
if thing
allow(Feature).to receive(:enabled?).with(feature_name, thing, any_args) { enabled }
allow(Feature).to receive(:enabled?).with(feature_name.to_s, thing, any_args) { enabled }
else
allow(Feature).to receive(:enabled?).with(feature_name, any_args) { enabled }
allow(Feature).to receive(:enabled?).with(feature_name.to_s, any_args) { enabled }
case actor
when false, true
allow(Feature).to receive(:enabled?).with(feature_name, any_args).and_return(actor)
allow(Feature).to receive(:enabled?).with(feature_name.to_s, any_args).and_return(actor)
when nil, ActiveRecord::Base, Symbol, RSpec::Mocks::Double
allow(Feature).to receive(:enabled?).with(feature_name, actor, any_args).and_return(true)
allow(Feature).to receive(:enabled?).with(feature_name.to_s, actor, any_args).and_return(true)
else
raise ArgumentError, "#stub_feature_flags accepts only `nil`, `true`, `false`, `ActiveRecord::Base` or `Symbol` as actors"
end
end
end
end
......
......@@ -60,7 +60,7 @@ RSpec.shared_examples 'diff file entity' do
context 'when the `single_mr_diff_view` feature is disabled' do
before do
stub_feature_flags(single_mr_diff_view: { enabled: false, thing: project })
stub_feature_flags(single_mr_diff_view: false)
end
it 'contains both kinds of diffs' do
......
# frozen_string_literal: true
require 'spec_helper'
describe StubFeatureFlags do
before do
# reset stub introduced by `stub_feature_flags`
allow(Feature).to receive(:enabled?).and_call_original
end
context 'if not stubbed' do
it 'features are disabled by default' do
expect(Feature.enabled?(:test_feature)).to eq(false)
end
end
describe '#stub_feature_flags' do
using RSpec::Parameterized::TableSyntax
let(:feature_name) { :test_feature }
context 'when checking global state' do
where(:feature_actors, :expected_result) do
false | false
true | true
:A | false
%i[A] | false
%i[A B] | false
end
with_them do
before do
stub_feature_flags(feature_name => feature_actors)
end
it { expect(Feature.enabled?(feature_name)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name)).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
it { expect(Feature.enabled?(feature_name, default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, default_enabled: true)).not_to eq(expected_result) }
end
end
end
context 'when checking scoped state' do
where(:feature_actors, :tested_actor, :expected_result) do
false | nil | false
true | nil | true
false | :A | false
true | :A | true
:A | nil | false
:A | :A | true
:A | :B | false
%i[A] | nil | false
%i[A] | :A | true
%i[A] | :B | false
%i[A B] | nil | false
%i[A B] | :A | true
%i[A B] | :B | true
end
with_them do
before do
stub_feature_flags(feature_name => feature_actors)
end
it { expect(Feature.enabled?(feature_name, tested_actor)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, tested_actor)).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
it { expect(Feature.enabled?(feature_name, tested_actor, default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, tested_actor, default_enabled: true)).not_to eq(expected_result) }
end
end
end
context 'type handling' do
context 'raises error' do
where(:feature_actors) do
['string', 1, 1.0, OpenStruct.new]
end
with_them do
subject { stub_feature_flags(feature_name => feature_actors) }
it { expect { subject }.to raise_error(ArgumentError, /accepts only/) }
end
end
context 'does not raise error' do
where(:feature_actors) do
[true, false, nil, :symbol, double, User.new]
end
with_them do
subject { stub_feature_flags(feature_name => feature_actors) }
it { expect { subject }.not_to raise_error }
end
end
end
it 'subsquent run changes state' do
# enable FF only on A
stub_feature_flags(test_feature: %i[A])
expect(Feature.enabled?(:test_feature)).to eq(false)
expect(Feature.enabled?(:test_feature, :A)).to eq(true)
expect(Feature.enabled?(:test_feature, :B)).to eq(false)
# enable FF only on B
stub_feature_flags(test_feature: %i[B])
expect(Feature.enabled?(:test_feature)).to eq(false)
expect(Feature.enabled?(:test_feature, :A)).to eq(false)
expect(Feature.enabled?(:test_feature, :B)).to eq(true)
# enable FF on all
stub_feature_flags(test_feature: true)
expect(Feature.enabled?(:test_feature)).to eq(true)
expect(Feature.enabled?(:test_feature, :A)).to eq(true)
expect(Feature.enabled?(:test_feature, :B)).to eq(true)
# disable FF on all
stub_feature_flags(test_feature: false)
expect(Feature.enabled?(:test_feature)).to eq(false)
expect(Feature.enabled?(:test_feature, :A)).to eq(false)
expect(Feature.enabled?(:test_feature, :B)).to eq(false)
end
end
end
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