Skip to content

Commit

Permalink
generic resolving and tests
Browse files Browse the repository at this point in the history
Signed-off-by: Kavindu Dodanduwa <[email protected]>
  • Loading branch information
Kavindu-Dodan committed Jul 21, 2023
1 parent 299f4a9 commit 2d9ec68
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 56 deletions.
90 changes: 34 additions & 56 deletions pkg/openfeature/testing/in_memory_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,8 @@ func (i InMemoryProvider) BooleanEvaluation(ctx context.Context, flag string, de
}
}

resolveFlag, detail := memoryFlag.Resolve(evalCtx)

var result bool
res, ok := resolveFlag.(bool)
if ok {
result = res
} else {
result = defaultValue
detail.Reason = openfeature.ErrorReason
detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
}
resolveFlag, detail := memoryFlag.Resolve(defaultValue, evalCtx)
result := genericResolve[bool](resolveFlag, defaultValue, &detail)

return openfeature.BoolResolutionDetail{
Value: result,
Expand All @@ -69,17 +60,8 @@ func (i InMemoryProvider) StringEvaluation(ctx context.Context, flag string, def
}
}

resolveFlag, detail := memoryFlag.Resolve(evalCtx)

var result string
res, ok := resolveFlag.(string)
if ok {
result = res
} else {
result = defaultValue
detail.Reason = openfeature.ErrorReason
detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
}
resolveFlag, detail := memoryFlag.Resolve(defaultValue, evalCtx)
result := genericResolve[string](resolveFlag, defaultValue, &detail)

return openfeature.StringResolutionDetail{
Value: result,
Expand All @@ -99,17 +81,8 @@ func (i InMemoryProvider) FloatEvaluation(ctx context.Context, flag string, defa
}
}

resolveFlag, detail := memoryFlag.Resolve(evalCtx)

var result float64
res, ok := resolveFlag.(float64)
if ok {
result = res
} else {
result = defaultValue
detail.Reason = openfeature.ErrorReason
detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
}
resolveFlag, detail := memoryFlag.Resolve(defaultValue, evalCtx)
result := genericResolve[float64](resolveFlag, defaultValue, &detail)

return openfeature.FloatResolutionDetail{
Value: result,
Expand All @@ -129,20 +102,11 @@ func (i InMemoryProvider) IntEvaluation(ctx context.Context, flag string, defaul
}
}

resolveFlag, detail := memoryFlag.Resolve(evalCtx)

var result int64
res, ok := resolveFlag.(int)
if ok {
result = int64(res)
} else {
result = defaultValue
detail.Reason = openfeature.ErrorReason
detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
}
resolveFlag, detail := memoryFlag.Resolve(defaultValue, evalCtx)
result := genericResolve[int](resolveFlag, int(defaultValue), &detail)

return openfeature.IntResolutionDetail{
Value: result,
Value: int64(result),
ProviderResolutionDetail: detail,
}
}
Expand All @@ -159,7 +123,7 @@ func (i InMemoryProvider) ObjectEvaluation(ctx context.Context, flag string, def
}
}

resolveFlag, detail := memoryFlag.Resolve(evalCtx)
resolveFlag, detail := memoryFlag.Resolve(defaultValue, evalCtx)

var result interface{}
if resolveFlag != nil {
Expand All @@ -177,10 +141,24 @@ func (i InMemoryProvider) ObjectEvaluation(ctx context.Context, flag string, def
}

func (i InMemoryProvider) Hooks() []openfeature.Hook {
//TODO implement some hooks
return []openfeature.Hook{}
}

// helpers

// genericResolve is a helper to extract type verified evaluation and fill openfeature.ProviderResolutionDetail
func genericResolve[T comparable](value interface{}, defaultValue T, detail *openfeature.ProviderResolutionDetail) T {
v, ok := value.(T)

if ok {
return v
}

detail.Reason = openfeature.ErrorReason
detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
return defaultValue
}

// Type Definitions for InMemoryProvider flag

// State of the feature flag
Expand All @@ -199,24 +177,24 @@ type InMemoryFlag struct {
ContextEvaluator ContextEvaluator
}

func (flag *InMemoryFlag) Resolve(evalCtx openfeature.FlattenedContext) (
func (flag *InMemoryFlag) Resolve(defaultValue interface{}, evalCtx openfeature.FlattenedContext) (
interface{}, openfeature.ProviderResolutionDetail) {

// first resolve from context callback
if flag.ContextEvaluator != nil {
return (*flag.ContextEvaluator)(*flag, evalCtx)
}

// fallback to evaluation

// check the state
if flag.State == Disabled {
return nil, openfeature.ProviderResolutionDetail{
return defaultValue, openfeature.ProviderResolutionDetail{
ResolutionError: openfeature.NewGeneralResolutionError("flag is disabled"),
Reason: openfeature.DisabledReason,
}
}

// first resolve from context callback
if flag.ContextEvaluator != nil {
return (*flag.ContextEvaluator)(*flag, evalCtx)
}

// fallback to evaluation

return flag.Variants[flag.DefaultVariant], openfeature.ProviderResolutionDetail{
Reason: openfeature.StaticReason,
Variant: flag.DefaultVariant,
Expand Down
80 changes: 80 additions & 0 deletions pkg/openfeature/testing/in_memory_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,83 @@ func TestInMemoryProvider_WithContext(t *testing.T) {
}
})
}

func TestInMemoryProvider_MissingFlag(t *testing.T) {
memoryProvider := NewInMemoryProvider(map[string]InMemoryFlag{})

ctx := context.Background()

t.Run("test missing flag", func(t *testing.T) {
evaluation := memoryProvider.StringEvaluation(ctx, "missing-flag", "GoodBye", nil)

if evaluation.Value != "GoodBye" {
t.Errorf("incorect evaluation, expected %v, got %v", "SomeResult", evaluation.Value)
}

if evaluation.Reason != openfeature.ErrorReason {
t.Errorf("incorect reason, expected %v, got %v", openfeature.ErrorReason, evaluation.Reason)
}

if evaluation.ResolutionDetail().ErrorCode != openfeature.FlagNotFoundCode {
t.Errorf("incorect reason, expected %v, got %v", openfeature.ErrorReason, evaluation.ResolutionDetail().ErrorCode)
}
})
}

func TestInMemoryProvider_TypeMismatch(t *testing.T) {
memoryProvider := NewInMemoryProvider(map[string]InMemoryFlag{
"boolFlag": {
Key: "boolFlag",
State: Enabled,
DefaultVariant: "true",
Variants: map[string]interface{}{
"true": true,
"false": false,
},
ContextEvaluator: nil,
},
})

ctx := context.Background()

t.Run("test missing flag", func(t *testing.T) {
evaluation := memoryProvider.StringEvaluation(ctx, "boolFlag", "GoodBye", nil)

if evaluation.Value != "GoodBye" {
t.Errorf("incorect evaluation, expected %v, got %v", "SomeResult", evaluation.Value)
}

if evaluation.ResolutionDetail().ErrorCode != openfeature.TypeMismatchCode {
t.Errorf("incorect reason, expected %v, got %v", openfeature.ErrorReason, evaluation.Reason)
}
})
}

func TestInMemoryProvider_Disabled(t *testing.T) {
memoryProvider := NewInMemoryProvider(map[string]InMemoryFlag{
"boolFlag": {
Key: "boolFlag",
State: Disabled,
DefaultVariant: "true",
Variants: map[string]interface{}{
"true": true,
"false": false,
},
ContextEvaluator: nil,
},
})

ctx := context.Background()

t.Run("test missing flag", func(t *testing.T) {
evaluation := memoryProvider.BooleanEvaluation(ctx, "boolFlag", false, nil)

if evaluation.Value != false {
t.Errorf("incorect evaluation, expected %v, got %v", false, evaluation.Value)
}

if evaluation.Reason != openfeature.DisabledReason {
t.Errorf("incorect reason, expected %v, got %v", openfeature.ErrorReason, evaluation.Reason)
}
})
}

0 comments on commit 2d9ec68

Please sign in to comment.