Skip to content

Commit

Permalink
Introduce fake for openfeature.IClient
Browse files Browse the repository at this point in the history
  • Loading branch information
evankanderson committed Apr 25, 2024
1 parent b08806d commit 2014e7a
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 17 deletions.
2 changes: 1 addition & 1 deletion internal/auth/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ type StaticIDP struct {
human string
}

var _ IdentityProvider = &StaticIDP{}
var _ IdentityProvider = (*StaticIDP)(nil)

// Resolve implements IdentityProvider.
func (s *StaticIDP) Resolve(_ context.Context, id string) (*Identity, error) {
Expand Down
17 changes: 3 additions & 14 deletions internal/controlplane/handlers_authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lestrrat-go/jwx/v2/jwt/openid"
"github.com/open-feature/go-sdk/openfeature"
"github.com/open-feature/go-sdk/openfeature/memprovider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
Expand Down Expand Up @@ -293,9 +292,6 @@ func TestProjectAuthorizationInterceptor(t *testing.T) {
}
}

// see https://github.com/open-feature/go-sdk/issues/266
//
//nolint:tparallel,paralleltest // openfeature currently has global singleton state:
func TestRoleManagement(t *testing.T) {

Check failure on line 295 in internal/controlplane/handlers_authz_test.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

TestRoleManagement should use t.Cleanup instead of defer (tparallel)
t.Parallel()

Expand Down Expand Up @@ -393,6 +389,7 @@ func TestRoleManagement(t *testing.T) {

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
authzClient := &mock.SimpleClient{
Allowed: []uuid.UUID{},
}
Expand All @@ -418,16 +415,8 @@ func TestRoleManagement(t *testing.T) {
mockStore.EXPECT().GetUserBySubject(gomock.Any(), match).Return(db.User{ID: 1}, nil)
}

inMemFlag := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{
string(flags.IDPResolver): {
Key: string(flags.IDPResolver),
DefaultVariant: "Fixed",
Variants: map[string]any{"Fixed": tc.idpFlag},
},
})
assert.NoError(t, openfeature.SetProvider(inMemFlag))
featureClient := openfeature.NewClient(t.Name())
t.Cleanup(openfeature.Shutdown)
featureClient := &flags.FakeClient{}
featureClient.Data = map[string]any{"idp_resolver": tc.idpFlag}

server := Server{
store: mockStore,
Expand Down
2 changes: 1 addition & 1 deletion internal/controlplane/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ type Server struct {
idClient auth.Resolver
cryptoEngine crypto.Engine
restClientCache ratecache.RestClientCache
featureFlags *openfeature.Client
featureFlags openfeature.IClient
// We may want to start breaking up the server struct if we use it to
// inject more entity-specific interfaces. For example, we may want to
// consider having a struct per grpc service
Expand Down
2 changes: 1 addition & 1 deletion internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func FromContext(ctx context.Context) openfeature.EvaluationContext {
}

// Bool provides a simple wrapper around client.Boolean to normalize usage for Minder.
func Bool(ctx context.Context, client *openfeature.Client, feature Experiment) bool {
func Bool(ctx context.Context, client openfeature.IClient, feature Experiment) bool {
ret := client.Boolean(ctx, string(feature), false, FromContext(ctx))
// TODO: capture in telemetry records
return ret
Expand Down
182 changes: 182 additions & 0 deletions internal/flags/test_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//
// Copyright 2023 Stacklok, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package flags

import (
"context"
"errors"

"github.com/open-feature/go-sdk/openfeature"
)

// FakeClient implements a simple in-memory client for testing.
//
// see https://github.com/open-feature/go-sdk/issues/266 for the proper support.
type FakeClient struct {
Data map[string]any
}

var _ openfeature.IClient = (*FakeClient)(nil)

// AddHandler implements openfeature.IClient.
func (_ *FakeClient) AddHandler(_ openfeature.EventType, _ openfeature.EventCallback) {
panic("unimplemented")
}

// AddHooks implements openfeature.IClient.
func (_ *FakeClient) AddHooks(_ ...openfeature.Hook) {
panic("unimplemented")
}

// Boolean implements openfeature.IClient.
func (f *FakeClient) Boolean(_ context.Context, flag string, defaultValue bool,
_ openfeature.EvaluationContext, _ ...openfeature.Option) bool {
if v, ok := f.Data[flag]; ok {
return v.(bool)
}
return defaultValue
}

// BooleanValue implements openfeature.IClient.
func (f *FakeClient) BooleanValue(_ context.Context, flag string, defaultValue bool,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (bool, error) {
if v, ok := f.Data[flag]; ok {
return v.(bool), nil
}
return defaultValue, errors.New("Not found")
}

// BooleanValueDetails implements openfeature.IClient.
func (_ *FakeClient) BooleanValueDetails(_ context.Context, _ string, _ bool,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (openfeature.BooleanEvaluationDetails, error) {
panic("unimplemented")
}

// EvaluationContext implements openfeature.IClient.
func (_ *FakeClient) EvaluationContext() openfeature.EvaluationContext {
panic("unimplemented")
}

// Float implements openfeature.IClient.
func (f *FakeClient) Float(_ context.Context, flag string, defaultValue float64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) float64 {
if v, ok := f.Data[flag]; ok {
return v.(float64)
}
return defaultValue
}

// FloatValue implements openfeature.IClient.
func (f *FakeClient) FloatValue(_ context.Context, flag string, defaultValue float64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (float64, error) {
if v, ok := f.Data[flag]; ok {
return v.(float64), nil
}
return defaultValue, errors.New("Not found")
}

// FloatValueDetails implements openfeature.IClient.
func (_ *FakeClient) FloatValueDetails(_ context.Context, _ string, _ float64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (openfeature.FloatEvaluationDetails, error) {
panic("unimplemented")
}

// Int implements openfeature.IClient.
func (f *FakeClient) Int(_ context.Context, flag string, defaultValue int64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) int64 {
if v, ok := f.Data[flag]; ok {
return v.(int64)
}
return defaultValue
}

// IntValue implements openfeature.IClient.
func (f *FakeClient) IntValue(_ context.Context, flag string, defaultValue int64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (int64, error) {
if v, ok := f.Data[flag]; ok {
return v.(int64), nil
}
return defaultValue, errors.New("Not found")
}

// IntValueDetails implements openfeature.IClient.
func (_ *FakeClient) IntValueDetails(_ context.Context, _ string, _ int64,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (openfeature.IntEvaluationDetails, error) {
panic("unimplemented")
}

// Metadata implements openfeature.IClient.
func (_ *FakeClient) Metadata() openfeature.ClientMetadata {
panic("unimplemented")
}

// Object implements openfeature.IClient.
func (f *FakeClient) Object(_ context.Context, flag string, defaultValue interface{},
_ openfeature.EvaluationContext, _ ...openfeature.Option) interface{} {
if v, ok := f.Data[flag]; ok {
return v
}
return defaultValue
}

// ObjectValue implements openfeature.IClient.
func (f *FakeClient) ObjectValue(_ context.Context, flag string, defaultValue interface{},
_ openfeature.EvaluationContext, _ ...openfeature.Option) (interface{}, error) {
if v, ok := f.Data[flag]; ok {
return v, nil
}
return defaultValue, errors.New("Not found")
}

// ObjectValueDetails implements openfeature.IClient.
func (_ *FakeClient) ObjectValueDetails(_ context.Context, _ string, _ interface{},
_ openfeature.EvaluationContext, _ ...openfeature.Option) (openfeature.InterfaceEvaluationDetails, error) {
panic("unimplemented")
}

// RemoveHandler implements openfeature.IClient.
func (_ *FakeClient) RemoveHandler(_ openfeature.EventType, _ openfeature.EventCallback) {
panic("unimplemented")
}

// SetEvaluationContext implements openfeature.IClient.
func (_ *FakeClient) SetEvaluationContext(_ openfeature.EvaluationContext) {
panic("unimplemented")
}

// String implements openfeature.IClient.
func (f *FakeClient) String(_ context.Context, flag string, defaultValue string,
_ openfeature.EvaluationContext, _ ...openfeature.Option) string {
if v, ok := f.Data[flag]; ok {
return v.(string)
}
return defaultValue
}

// StringValue implements openfeature.IClient.
func (f *FakeClient) StringValue(_ context.Context, flag string, defaultValue string,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (string, error) {
if v, ok := f.Data[flag]; ok {
return v.(string), nil
}
return defaultValue, errors.New("Not found")
}

// StringValueDetails implements openfeature.IClient.
func (_ *FakeClient) StringValueDetails(_ context.Context, _ string, _ string,
_ openfeature.EvaluationContext, _ ...openfeature.Option) (openfeature.StringEvaluationDetails, error) {
panic("unimplemented")
}

0 comments on commit 2014e7a

Please sign in to comment.