Commit e2843005 authored by Kamil Trzciński's avatar Kamil Trzciński

Improve `#stub_feature_flags` semantic

This changes the `#stub_feature_flags` to be:

- `stub_feature_flags(ci_live_trace: false)`
  Disable `ci_live_trace` feature flag globally

- `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

Which does represent an actual behaviour of `Flipper`:

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