Skip to content

Commit

Permalink
feat: added code to handle short circuit
Browse files Browse the repository at this point in the history
When triggering an evaluation while the provider is in "not ready" or "fatal state" the sdk shall
return an appropriate error.

Signed-off-by: Bernd Warmuth <[email protected]>
  • Loading branch information
warber committed Nov 6, 2024
1 parent 5469e36 commit 9695ff6
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 126 deletions.
9 changes: 4 additions & 5 deletions e2e/evaluation_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@ package e2e_test

import (
"context"
"strings"
"testing"

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

func setupFuzzClient(f *testing.F) *openfeature.Client {
f.Helper()

memoryProvider := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{})
err := openfeature.SetProvider(memoryProvider)
err := openfeature.SetNamedProviderAndWait(f.Name(), memoryProvider)
if err != nil {
f.Errorf("error setting up provider %v", err)
}

return openfeature.NewClient("fuzzing")
return openfeature.GetApiInstance().GetNamedClient(f.Name()).(*openfeature.Client)
}

func FuzzBooleanEvaluation(f *testing.F) {
Expand Down
32 changes: 15 additions & 17 deletions e2e/evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
"github.com/open-feature/go-sdk/openfeature/memprovider"
)

var client = openfeature.NewClient("evaluation tests")

// ctxStorageKey is the key used to pass test data across context.Context
type ctxStorageKey struct{}

Expand Down Expand Up @@ -95,7 +93,7 @@ func initializeEvaluationScenario(ctx *godog.ScenarioContext) {
func aProviderIsRegisteredWithCacheDisabled(ctx context.Context) error {
memoryProvider := memprovider.NewInMemoryProvider(memoryFlags)

err := openfeature.SetProvider(memoryProvider)
err := openfeature.SetNamedProvider("evaluation-test", memoryProvider)
if err != nil {
return err
}
Expand All @@ -111,7 +109,7 @@ func aBooleanFlagWithKeyIsEvaluatedWithDefaultValue(
return ctx, errors.New("default value must be of type bool")
}

got, err := client.BooleanValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").BooleanValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -140,7 +138,7 @@ func theResolvedBooleanValueShouldBe(ctx context.Context, expectedValueStr strin
func aStringFlagWithKeyIsEvaluatedWithDefaultValue(
ctx context.Context, flagKey, defaultValue string,
) (context.Context, error) {
got, err := client.StringValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").StringValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand All @@ -164,7 +162,7 @@ func theResolvedStringValueShouldBe(ctx context.Context, expectedValue string) e
func anIntegerFlagWithKeyIsEvaluatedWithDefaultValue(
ctx context.Context, flagKey string, defaultValue int64,
) (context.Context, error) {
got, err := client.IntValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").IntValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand All @@ -188,7 +186,7 @@ func theResolvedIntegerValueShouldBe(ctx context.Context, expectedValue int64) e
func aFloatFlagWithKeyIsEvaluatedWithDefaultValue(
ctx context.Context, flagKey string, defaultValue float64,
) (context.Context, error) {
got, err := client.FloatValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").FloatValue(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand All @@ -210,7 +208,7 @@ func theResolvedFloatValueShouldBe(ctx context.Context, expectedValue float64) e
}

func anObjectFlagWithKeyIsEvaluatedWithANullDefaultValue(ctx context.Context, flagKey string) (context.Context, error) {
got, err := client.ObjectValue(ctx, flagKey, nil, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").ObjectValue(ctx, flagKey, nil, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -272,7 +270,7 @@ func aBooleanFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(
return ctx, errors.New("default value must be of type bool")
}

got, err := client.BooleanValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").BooleanValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -316,7 +314,7 @@ func theResolvedBooleanDetailsValueShouldBeTheVariantShouldBeAndTheReasonShouldB
func aStringFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(
ctx context.Context, flagKey, defaultValue string,
) (context.Context, error) {
got, err := client.StringValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").StringValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -355,7 +353,7 @@ func theResolvedStringDetailsValueShouldBeTheVariantShouldBeAndTheReasonShouldBe
func anIntegerFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(
ctx context.Context, flagKey string, defaultValue int64,
) (context.Context, error) {
got, err := client.IntValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").IntValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -394,7 +392,7 @@ func theResolvedIntegerDetailsValueShouldBeTheVariantShouldBeAndTheReasonShouldB
func aFloatFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(
ctx context.Context, flagKey string, defaultValue float64,
) (context.Context, error) {
got, err := client.FloatValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").FloatValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -433,7 +431,7 @@ func theResolvedFloatDetailsValueShouldBeTheVariantShouldBeAndTheReasonShouldBe(
func anObjectFlagWithKeyIsEvaluatedWithDetailsAndANullDefaultValue(
ctx context.Context, flagKey string,
) (context.Context, error) {
got, err := client.ObjectValueDetails(ctx, flagKey, nil, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").ObjectValueDetails(ctx, flagKey, nil, openfeature.EvaluationContext{})
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -540,7 +538,7 @@ func aFlagWithKeyIsEvaluatedWithDefaultValue(
return ctx, errors.New("no contextAwareEvaluationData found")
}

got, err := client.StringValue(ctx, flagKey, defaultValue, ctxAwareEvalData.evaluationContext)
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").StringValue(ctx, flagKey, defaultValue, ctxAwareEvalData.evaluationContext)
if err != nil {
return ctx, fmt.Errorf("openfeature client: %w", err)
}
Expand Down Expand Up @@ -570,7 +568,7 @@ func theResolvedFlagValueIsWhenTheContextIsEmpty(ctx context.Context, expectedRe
return errors.New("no contextAwareEvaluationData found")
}

got, err := client.StringValue(
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").StringValue(
ctx, ctxAwareEvalData.flagKey, ctxAwareEvalData.defaultValue, openfeature.EvaluationContext{},
)
if err != nil {
Expand All @@ -587,7 +585,7 @@ func theResolvedFlagValueIsWhenTheContextIsEmpty(ctx context.Context, expectedRe
func aNonexistentStringFlagWithKeyIsEvaluatedWithDetailsAndADefaultValue(
ctx context.Context, flagKey, defaultValue string,
) (context.Context, error) {
got, err := client.StringValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").StringValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})

return context.WithValue(ctx, ctxStorageKey{}, stringFlagNotFoundData{
evalDetails: got,
Expand Down Expand Up @@ -644,7 +642,7 @@ func theReasonShouldIndicateAnErrorAndTheErrorCodeShouldIndicateAMissingFlagWith
func aStringFlagWithKeyIsEvaluatedAsAnIntegerWithDetailsAndADefaultValue(
ctx context.Context, flagKey string, defaultValue int64,
) (context.Context, error) {
got, err := client.IntValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})
got, err := openfeature.GetApiInstance().GetNamedClient("evaluation-test").IntValueDetails(ctx, flagKey, defaultValue, openfeature.EvaluationContext{})

return context.WithValue(ctx, ctxStorageKey{}, typeErrorData{
evalDetails: got,
Expand Down
8 changes: 8 additions & 0 deletions openfeature/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,14 @@ func (c *Client) evaluate(
},
}

if c.State() == NotReadyState {
return evalDetails, ProviderNotReadyError
}

if c.State() == FatalState {
return evalDetails, ProviderFatalError
}

if !utf8.Valid([]byte(flag)) {
return evalDetails, NewParseErrorResolutionError("flag key is not a UTF-8 encoded string")
}
Expand Down
30 changes: 30 additions & 0 deletions openfeature/client_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ func ExampleNewClient() {
}

func ExampleClient_BooleanValue() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
client := openfeature.NewClient("example-client")
value, err := client.BooleanValue(
context.Background(), "test-flag", true, openfeature.EvaluationContext{},
Expand All @@ -29,6 +32,9 @@ func ExampleClient_BooleanValue() {
}

func ExampleClient_StringValue() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
client := openfeature.NewClient("example-client")
value, err := client.StringValue(
context.Background(), "test-flag", "openfeature", openfeature.EvaluationContext{},
Expand All @@ -42,6 +48,9 @@ func ExampleClient_StringValue() {
}

func ExampleClient_FloatValue() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
client := openfeature.NewClient("example-client")
value, err := client.FloatValue(
context.Background(), "test-flag", 0.55, openfeature.EvaluationContext{},
Expand All @@ -55,6 +64,9 @@ func ExampleClient_FloatValue() {
}

func ExampleClient_IntValue() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
client := openfeature.NewClient("example-client")
value, err := client.IntValue(
context.Background(), "test-flag", 3, openfeature.EvaluationContext{},
Expand All @@ -68,6 +80,9 @@ func ExampleClient_IntValue() {
}

func ExampleClient_ObjectValue() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
client := openfeature.NewClient("example-client")
value, err := client.ObjectValue(
context.Background(), "test-flag", map[string]string{"foo": "bar"}, openfeature.EvaluationContext{},
Expand All @@ -82,6 +97,9 @@ func ExampleClient_ObjectValue() {
}

func ExampleClient_Boolean() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
ctx := context.Background()
client := openfeature.NewClient("example-client")

Expand All @@ -95,6 +113,9 @@ func ExampleClient_Boolean() {
}

func ExampleClient_String() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
ctx := context.Background()
client := openfeature.NewClient("example-client")

Expand All @@ -104,6 +125,9 @@ func ExampleClient_String() {
}

func ExampleClient_Float() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
ctx := context.Background()
client := openfeature.NewClient("example-client")

Expand All @@ -113,6 +137,9 @@ func ExampleClient_Float() {
}

func ExampleClient_Int() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
ctx := context.Background()
client := openfeature.NewClient("example-client")

Expand All @@ -122,6 +149,9 @@ func ExampleClient_Int() {
}

func ExampleClient_Object() {
if err := openfeature.SetNamedProviderAndWait("example-client", openfeature.NoopProvider{}); err != nil {
log.Fatalf("error setting up provider %v", err)
}
ctx := context.Background()
client := openfeature.NewClient("example-client")

Expand Down
Loading

0 comments on commit 9695ff6

Please sign in to comment.