debian-mirror-gitlab/spec/requests/api/features_spec.rb

552 lines
19 KiB
Ruby
Raw Normal View History

2019-12-26 22:10:19 +05:30
# frozen_string_literal: true
2017-09-10 17:25:29 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec.describe API::Features, stub_feature_flags: false do
2020-03-13 15:44:24 +05:30
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
2017-09-10 17:25:29 +05:30
2021-02-22 17:27:13 +05:30
# Find any `development` feature flag name
let(:known_feature_flag) do
Feature::Definition.definitions
.values.find(&:development?)
end
let(:known_feature_flag_definition_hash) do
a_hash_including(
'type' => 'development'
)
end
2017-09-10 17:25:29 +05:30
before do
2020-06-23 00:09:42 +05:30
Feature.reset
2017-09-10 17:25:29 +05:30
Flipper.unregister_groups
Flipper.register(:perf_team) do |actor|
actor.respond_to?(:admin) && actor.admin?
end
2021-01-03 14:25:43 +05:30
skip_feature_flags_yaml_validation
2022-07-16 23:28:13 +05:30
skip_default_enabled_yaml_check
2017-09-10 17:25:29 +05:30
end
describe 'GET /features' do
let(:expected_features) do
[
{
'name' => 'feature_1',
'state' => 'on',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => true }],
'definition' => nil
2017-09-10 17:25:29 +05:30
},
{
'name' => 'feature_2',
'state' => 'off',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => false }],
'definition' => nil
2017-09-10 17:25:29 +05:30
},
{
'name' => 'feature_3',
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
2021-02-22 17:27:13 +05:30
],
'definition' => nil
},
{
'name' => known_feature_flag.name,
'state' => 'on',
'gates' => [{ 'key' => 'boolean', 'value' => true }],
'definition' => known_feature_flag_definition_hash
2017-09-10 17:25:29 +05:30
}
]
end
before do
2020-06-23 00:09:42 +05:30
Feature.enable('feature_1')
Feature.disable('feature_2')
Feature.enable('feature_3', Feature.group(:perf_team))
2021-02-22 17:27:13 +05:30
Feature.enable(known_feature_flag.name)
2017-09-10 17:25:29 +05:30
end
it 'returns a 401 for anonymous users' do
get api('/features')
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:unauthorized)
2017-09-10 17:25:29 +05:30
end
it 'returns a 403 for users' do
get api('/features', user)
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:forbidden)
2017-09-10 17:25:29 +05:30
end
it 'returns the feature list for admins' do
get api('/features', admin)
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:ok)
2017-09-10 17:25:29 +05:30
expect(json_response).to match_array(expected_features)
end
end
describe 'POST /feature' do
2021-02-22 17:27:13 +05:30
let(:feature_name) { known_feature_flag.name }
2017-09-10 17:25:29 +05:30
context 'when the feature does not exist' do
it 'returns a 401 for anonymous users' do
post api("/features/#{feature_name}")
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:unauthorized)
2017-09-10 17:25:29 +05:30
end
it 'returns a 403 for users' do
post api("/features/#{feature_name}", user)
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:forbidden)
2017-09-10 17:25:29 +05:30
end
context 'when passed value=true' do
it 'creates an enabled feature' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'on',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => true }],
'definition' => known_feature_flag_definition_hash
)
end
it 'logs the event' do
expect(Feature.logger).to receive(:info).once
post api("/features/#{feature_name}", admin), params: { value: 'true' }
2017-09-10 17:25:29 +05:30
end
it 'creates an enabled feature for the given Flipper group when passed feature_group=perf_team' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true', feature_group: 'perf_team' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'creates an enabled feature for the given user when passed user=username' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'creates an enabled feature for the given user and feature group when passed user=username and feature_group=perf_team' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username, feature_group: 'perf_team' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response['name']).to eq(feature_name)
2019-12-26 22:10:19 +05:30
expect(json_response['state']).to eq('conditional')
expect(json_response['gates']).to contain_exactly(
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
)
2017-09-10 17:25:29 +05:30
end
end
2022-04-04 11:22:00 +05:30
shared_examples 'does not enable the flag' do |actor_type, actor_path|
it 'returns the current state of the flag without changes' do
post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor_path }
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to match(
"name" => feature_name,
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
],
'definition' => known_feature_flag_definition_hash
)
end
end
shared_examples 'enables the flag for the actor' do |actor_type|
it 'sets the feature gate' do
post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor.full_path }
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to match(
'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["#{actor.class}:#{actor.id}"] }
],
'definition' => known_feature_flag_definition_hash
)
end
end
2019-03-02 22:35:43 +05:30
context 'when enabling for a project by path' do
context 'when the project exists' do
2022-04-04 11:22:00 +05:30
it_behaves_like 'enables the flag for the actor', :project do
let(:actor) { create(:project) }
2019-03-02 22:35:43 +05:30
end
end
context 'when the project does not exist' do
2022-04-04 11:22:00 +05:30
it_behaves_like 'does not enable the flag', :project, 'mep/to/the/mep/mep'
2019-03-02 22:35:43 +05:30
end
end
2019-07-07 11:18:12 +05:30
context 'when enabling for a group by path' do
context 'when the group exists' do
2022-04-04 11:22:00 +05:30
it_behaves_like 'enables the flag for the actor', :group do
let(:actor) { create(:group) }
2019-07-07 11:18:12 +05:30
end
end
context 'when the group does not exist' do
2022-04-04 11:22:00 +05:30
it_behaves_like 'does not enable the flag', :group, 'not/a/group'
end
end
context 'when enabling for a namespace by path' do
context 'when the user namespace exists' do
it_behaves_like 'enables the flag for the actor', :namespace do
let(:actor) { create(:namespace) }
end
end
context 'when the group namespace exists' do
it_behaves_like 'enables the flag for the actor', :namespace do
let(:actor) { create(:group) }
end
end
context 'when the user namespace does not exist' do
it_behaves_like 'does not enable the flag', :namespace, 'not/a/group'
end
context 'when a project namespace exists' do
let(:project_namespace) { create(:project_namespace) }
it_behaves_like 'does not enable the flag', :namespace do
let(:actor_path) { project_namespace.full_path }
2019-07-07 11:18:12 +05:30
end
end
end
2020-05-24 23:13:21 +05:30
it 'creates a feature with the given percentage of time if passed an integer' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: '50' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_time', 'value' => 50 }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
2020-05-24 23:13:21 +05:30
2021-12-11 22:18:48 +05:30
it 'creates a feature with the given percentage of time if passed a float' do
post api("/features/#{feature_name}", admin), params: { value: '0.01' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to match(
'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_time', 'value' => 0.01 }
],
'definition' => known_feature_flag_definition_hash
)
end
2020-05-24 23:13:21 +05:30
it 'creates a feature with the given percentage of actors if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50', key: 'percentage_of_actors' }
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2020-05-24 23:13:21 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_actors', 'value' => 50 }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2020-05-24 23:13:21 +05:30
end
2021-12-11 22:18:48 +05:30
it 'creates a feature with the given percentage of actors if passed a float' do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to match(
'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_actors', 'value' => 0.01 }
],
'definition' => known_feature_flag_definition_hash
)
end
2022-07-16 23:28:13 +05:30
describe 'mutually exclusive parameters' do
shared_examples 'fails to set the feature flag' do
it 'returns an error' do
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to match(/key, \w+ are mutually exclusive/)
end
end
context 'when key and feature_group are provided' do
before do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors', feature_group: 'some-value' }
end
it_behaves_like 'fails to set the feature flag'
end
context 'when key and user are provided' do
before do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors', user: 'some-user' }
end
it_behaves_like 'fails to set the feature flag'
end
context 'when key and group are provided' do
before do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors', group: 'somepath' }
end
it_behaves_like 'fails to set the feature flag'
end
context 'when key and namespace are provided' do
before do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors', namespace: 'somepath' }
end
it_behaves_like 'fails to set the feature flag'
end
context 'when key and project are provided' do
before do
post api("/features/#{feature_name}", admin), params: { value: '0.01', key: 'percentage_of_actors', project: 'somepath' }
end
it_behaves_like 'fails to set the feature flag'
end
end
2017-09-10 17:25:29 +05:30
end
context 'when the feature exists' do
before do
2020-06-23 00:09:42 +05:30
Feature.disable(feature_name) # This also persists the feature on the DB
2017-09-10 17:25:29 +05:30
end
context 'when passed value=true' do
it 'enables the feature' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'on',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => true }],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'enables the feature for the given Flipper group when passed feature_group=perf_team' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true', feature_group: 'perf_team' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'enables the feature for the given user when passed user=username' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
end
context 'when feature is enabled and value=false is passed' do
it 'disables the feature' do
2020-06-23 00:09:42 +05:30
Feature.enable(feature_name)
expect(Feature.enabled?(feature_name)).to eq(true)
2017-09-10 17:25:29 +05:30
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'false' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'off',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => false }],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'disables the feature for the given Flipper group when passed feature_group=perf_team' do
2020-06-23 00:09:42 +05:30
Feature.enable(feature_name, Feature.group(:perf_team))
expect(Feature.enabled?(feature_name, admin)).to be_truthy
2017-09-10 17:25:29 +05:30
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'false', feature_group: 'perf_team' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'off',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => false }],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
it 'disables the feature for the given user when passed user=username' do
2020-06-23 00:09:42 +05:30
Feature.enable(feature_name, user)
expect(Feature.enabled?(feature_name, user)).to be_truthy
2017-09-10 17:25:29 +05:30
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: 'false', user: user.username }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'off',
2021-02-22 17:27:13 +05:30
'gates' => [{ 'key' => 'boolean', 'value' => false }],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
end
2020-05-24 23:13:21 +05:30
context 'with a pre-existing percentage of time value' do
2017-09-10 17:25:29 +05:30
before do
2020-06-23 00:09:42 +05:30
Feature.enable_percentage_of_time(feature_name, 50)
2017-09-10 17:25:29 +05:30
end
it 'updates the percentage of time if passed an integer' do
2019-02-15 15:39:39 +05:30
post api("/features/#{feature_name}", admin), params: { value: '30' }
2017-09-10 17:25:29 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2017-09-10 17:25:29 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_time', 'value' => 30 }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2017-09-10 17:25:29 +05:30
end
end
2020-05-24 23:13:21 +05:30
context 'with a pre-existing percentage of actors value' do
before do
2020-06-23 00:09:42 +05:30
Feature.enable_percentage_of_actors(feature_name, 42)
2020-05-24 23:13:21 +05:30
end
it 'updates the percentage of actors if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '74', key: 'percentage_of_actors' }
expect(response).to have_gitlab_http_status(:created)
2021-02-22 17:27:13 +05:30
expect(json_response).to match(
'name' => feature_name,
2020-05-24 23:13:21 +05:30
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_actors', 'value' => 74 }
2021-02-22 17:27:13 +05:30
],
'definition' => known_feature_flag_definition_hash
)
2020-05-24 23:13:21 +05:30
end
end
2017-09-10 17:25:29 +05:30
end
end
2018-05-09 12:01:36 +05:30
describe 'DELETE /feature/:name' do
let(:feature_name) { 'my_feature' }
context 'when the user has no access' do
it 'returns a 401 for anonymous users' do
delete api("/features/#{feature_name}")
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:unauthorized)
2018-05-09 12:01:36 +05:30
end
it 'returns a 403 for users' do
delete api("/features/#{feature_name}", user)
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:forbidden)
2018-05-09 12:01:36 +05:30
end
end
context 'when the user has access' do
it 'returns 204 when the value is not set' do
delete api("/features/#{feature_name}", admin)
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:no_content)
2018-05-09 12:01:36 +05:30
end
context 'when the gate value was set' do
before do
2020-06-23 00:09:42 +05:30
Feature.enable(feature_name)
2018-05-09 12:01:36 +05:30
end
it 'deletes an enabled feature' do
2020-06-23 00:09:42 +05:30
expect do
delete api("/features/#{feature_name}", admin)
Feature.reset
end.to change { Feature.persisted_name?(feature_name) }
.and change { Feature.enabled?(feature_name) }
2018-05-09 12:01:36 +05:30
2020-04-08 14:13:33 +05:30
expect(response).to have_gitlab_http_status(:no_content)
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
it 'logs the event' do
expect(Feature.logger).to receive(:info).once
delete api("/features/#{feature_name}", admin)
end
2018-05-09 12:01:36 +05:30
end
end
end
2017-09-10 17:25:29 +05:30
end