Loading feature_flags.go +8 −8 Changes for feature_flags.go: 8 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -114,14 +114,14 @@ func (s *FeaturesService) ListFeatureDefinitions(options ...RequestOptionFunc) ( // https://docs.gitlab.com/api/features/#set-or-create-a-feature type SetFeatureFlagOptions struct { Value any `url:"value" json:"value"` Key string `url:"key" json:"key"` FeatureGroup string `url:"feature_group" json:"feature_group"` User string `url:"user" json:"user"` Group string `url:"group" json:"group"` Namespace string `url:"namespace" json:"namespace"` Project string `url:"project" json:"project"` Repository string `url:"repository" json:"repository"` Force bool `url:"force" json:"force"` Key string `url:"key,omitempty" json:"key,omitempty"` FeatureGroup string `url:"feature_group,omitempty" json:"feature_group,omitempty"` User string `url:"user,omitempty" json:"user,omitempty"` Group string `url:"group,omitempty" json:"group,omitempty"` Namespace string `url:"namespace,omitempty" json:"namespace,omitempty"` Project string `url:"project,omitempty" json:"project,omitempty"` Repository string `url:"repository,omitempty" json:"repository,omitempty"` Force bool `url:"force,omitempty" json:"force,omitempty"` } // SetFeatureFlag sets or creates a feature flag gate Loading feature_flags_test.go +49 Changes for feature_flags_test.go: 49 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -147,6 +147,55 @@ func TestSetFeatureFlag(t *testing.T) { assert.Equal(t, want, feature) } func TestSetFeatureFlagOmitsUnsetGates(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/features/new_library", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) // THEN: The body carries the value alone. An empty key beside an // empty feature_group, user or group is refused by GitLab as // mutually exclusive, whatever the caller asked for testBodyJSON(t, r, map[string]any{ "value": false, }) fmt.Fprint(w, `{"name": "new_library", "state": "off", "gates": [{"key": "boolean", "value": false}]}`) }) // GIVEN: An instance-wide boolean gate and no other option opt := &SetFeatureFlagOptions{Value: false} // WHEN: The flag is set feature, _, err := client.Features.SetFeatureFlag("new_library", opt) assert.NoError(t, err) assert.Equal(t, "off", feature.State) } func TestSetFeatureFlagSendsTheOptionsItIsGiven(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/features/new_library", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) // THEN: Every option the caller set is in the body, so omitempty // drops only what was left unset testBodyJSON(t, r, map[string]any{ "value": float64(30), "key": "percentage_of_actors", "force": true, }) fmt.Fprint(w, `{"name": "new_library", "state": "conditional", "gates": [{"key": "percentage_of_actors", "value": 30}]}`) }) // GIVEN: A percentage of actors gate, forced past validation opt := &SetFeatureFlagOptions{Value: 30, Key: "percentage_of_actors", Force: true} // WHEN: The flag is set feature, _, err := client.Features.SetFeatureFlag("new_library", opt) assert.NoError(t, err) assert.Equal(t, "conditional", feature.State) } func TestDeleteFeatureFlag(t *testing.T) { t.Parallel() mux, client := setup(t) Loading gitlab_test/feature_flags_integration_test.go 0 → 100644 +99 Changes for gitlab_test/feature_flags_integration_test.go: 99 added lines, 0 removed lines. Original line number Diff line number Diff line //go:build integration package gitlab_test import ( "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" gitlab "gitlab.com/gitlab-org/api/client-go/v3" ) // Integration tests for the Features API. Setting a feature flag requires an // administrator token. // testFeatureFlagName returns the name of a feature flag that only the calling // test sets, and deletes the flag when the test finishes. func testFeatureFlagName(t *testing.T, client *gitlab.Client) string { t.Helper() name := fmt.Sprintf("client_go_integration_%d", time.Now().UnixNano()) t.Cleanup(func() { _, err := client.Features.DeleteFeatureFlag(name) require.NoError(t, err, "Failed to delete test feature flag") }) return name } func Test_FeaturesSetFeatureFlag_Boolean_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets name := testFeatureFlagName(t, client) // WHEN the flag is enabled for the whole instance, with no other option feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{Value: true}) // THEN GitLab accepts the request and the flag is on require.NoError(t, err, "Failed to enable the feature flag") assert.Equal(t, name, feature.Name) assert.Equal(t, "on", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "boolean", Value: true}) // AND WHEN it is disabled the same way feature, _, err = client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{Value: false}) // THEN the flag is off require.NoError(t, err, "Failed to disable the feature flag") assert.Equal(t, "off", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "boolean", Value: false}) } func Test_FeaturesSetFeatureFlag_PercentageOfActors_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets name := testFeatureFlagName(t, client) // WHEN it is enabled for a percentage of actors, which sets key and no gate feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{ Value: 30, Key: "percentage_of_actors", }) // THEN GitLab accepts the request and records the percentage require.NoError(t, err, "Failed to set the percentage of actors") assert.Equal(t, "conditional", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "percentage_of_actors", Value: float64(30)}) } func Test_FeaturesSetFeatureFlag_User_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets, and a user name := testFeatureFlagName(t, client) user, err := CreateTestUser(t, client) require.NoError(t, err) // WHEN it is enabled for that user, which sets a gate and no key feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{ Value: true, User: user.Username, }) // THEN GitLab accepts the request and enables the flag for the user require.NoError(t, err, "Failed to enable the feature flag for a user") assert.Equal(t, "conditional", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "actors", Value: []any{fmt.Sprintf("User:%d", user.ID)}}) } Loading
feature_flags.go +8 −8 Changes for feature_flags.go: 8 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -114,14 +114,14 @@ func (s *FeaturesService) ListFeatureDefinitions(options ...RequestOptionFunc) ( // https://docs.gitlab.com/api/features/#set-or-create-a-feature type SetFeatureFlagOptions struct { Value any `url:"value" json:"value"` Key string `url:"key" json:"key"` FeatureGroup string `url:"feature_group" json:"feature_group"` User string `url:"user" json:"user"` Group string `url:"group" json:"group"` Namespace string `url:"namespace" json:"namespace"` Project string `url:"project" json:"project"` Repository string `url:"repository" json:"repository"` Force bool `url:"force" json:"force"` Key string `url:"key,omitempty" json:"key,omitempty"` FeatureGroup string `url:"feature_group,omitempty" json:"feature_group,omitempty"` User string `url:"user,omitempty" json:"user,omitempty"` Group string `url:"group,omitempty" json:"group,omitempty"` Namespace string `url:"namespace,omitempty" json:"namespace,omitempty"` Project string `url:"project,omitempty" json:"project,omitempty"` Repository string `url:"repository,omitempty" json:"repository,omitempty"` Force bool `url:"force,omitempty" json:"force,omitempty"` } // SetFeatureFlag sets or creates a feature flag gate Loading
feature_flags_test.go +49 Changes for feature_flags_test.go: 49 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -147,6 +147,55 @@ func TestSetFeatureFlag(t *testing.T) { assert.Equal(t, want, feature) } func TestSetFeatureFlagOmitsUnsetGates(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/features/new_library", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) // THEN: The body carries the value alone. An empty key beside an // empty feature_group, user or group is refused by GitLab as // mutually exclusive, whatever the caller asked for testBodyJSON(t, r, map[string]any{ "value": false, }) fmt.Fprint(w, `{"name": "new_library", "state": "off", "gates": [{"key": "boolean", "value": false}]}`) }) // GIVEN: An instance-wide boolean gate and no other option opt := &SetFeatureFlagOptions{Value: false} // WHEN: The flag is set feature, _, err := client.Features.SetFeatureFlag("new_library", opt) assert.NoError(t, err) assert.Equal(t, "off", feature.State) } func TestSetFeatureFlagSendsTheOptionsItIsGiven(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/v4/features/new_library", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) // THEN: Every option the caller set is in the body, so omitempty // drops only what was left unset testBodyJSON(t, r, map[string]any{ "value": float64(30), "key": "percentage_of_actors", "force": true, }) fmt.Fprint(w, `{"name": "new_library", "state": "conditional", "gates": [{"key": "percentage_of_actors", "value": 30}]}`) }) // GIVEN: A percentage of actors gate, forced past validation opt := &SetFeatureFlagOptions{Value: 30, Key: "percentage_of_actors", Force: true} // WHEN: The flag is set feature, _, err := client.Features.SetFeatureFlag("new_library", opt) assert.NoError(t, err) assert.Equal(t, "conditional", feature.State) } func TestDeleteFeatureFlag(t *testing.T) { t.Parallel() mux, client := setup(t) Loading
gitlab_test/feature_flags_integration_test.go 0 → 100644 +99 Changes for gitlab_test/feature_flags_integration_test.go: 99 added lines, 0 removed lines. Original line number Diff line number Diff line //go:build integration package gitlab_test import ( "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" gitlab "gitlab.com/gitlab-org/api/client-go/v3" ) // Integration tests for the Features API. Setting a feature flag requires an // administrator token. // testFeatureFlagName returns the name of a feature flag that only the calling // test sets, and deletes the flag when the test finishes. func testFeatureFlagName(t *testing.T, client *gitlab.Client) string { t.Helper() name := fmt.Sprintf("client_go_integration_%d", time.Now().UnixNano()) t.Cleanup(func() { _, err := client.Features.DeleteFeatureFlag(name) require.NoError(t, err, "Failed to delete test feature flag") }) return name } func Test_FeaturesSetFeatureFlag_Boolean_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets name := testFeatureFlagName(t, client) // WHEN the flag is enabled for the whole instance, with no other option feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{Value: true}) // THEN GitLab accepts the request and the flag is on require.NoError(t, err, "Failed to enable the feature flag") assert.Equal(t, name, feature.Name) assert.Equal(t, "on", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "boolean", Value: true}) // AND WHEN it is disabled the same way feature, _, err = client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{Value: false}) // THEN the flag is off require.NoError(t, err, "Failed to disable the feature flag") assert.Equal(t, "off", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "boolean", Value: false}) } func Test_FeaturesSetFeatureFlag_PercentageOfActors_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets name := testFeatureFlagName(t, client) // WHEN it is enabled for a percentage of actors, which sets key and no gate feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{ Value: 30, Key: "percentage_of_actors", }) // THEN GitLab accepts the request and records the percentage require.NoError(t, err, "Failed to set the percentage of actors") assert.Equal(t, "conditional", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "percentage_of_actors", Value: float64(30)}) } func Test_FeaturesSetFeatureFlag_User_Integration(t *testing.T) { t.Parallel() client := SetupIntegrationClient(t) // GIVEN a feature flag no other test sets, and a user name := testFeatureFlagName(t, client) user, err := CreateTestUser(t, client) require.NoError(t, err) // WHEN it is enabled for that user, which sets a gate and no key feature, _, err := client.Features.SetFeatureFlag(name, &gitlab.SetFeatureFlagOptions{ Value: true, User: user.Username, }) // THEN GitLab accepts the request and enables the flag for the user require.NoError(t, err, "Failed to enable the feature flag for a user") assert.Equal(t, "conditional", feature.State) assert.Contains(t, feature.Gates, gitlab.Gate{Key: "actors", Value: []any{fmt.Sprintf("User:%d", user.ID)}}) }