From 6bdcac93f517d2b2433300257bf9d2165f60533a Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Mon, 18 Dec 2023 11:36:22 +0100 Subject: [PATCH] fix: add tests and impl --- client.go | 27 ++++--- client_test.go | 213 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 9 deletions(-) diff --git a/client.go b/client.go index 0254e72..fd886ae 100644 --- a/client.go +++ b/client.go @@ -416,21 +416,30 @@ func (uc *Client) getVariantWithoutMetrics(feature string, options ...VariantOpt strategyResult, f = uc.isEnabled(feature, WithContext(*ctx)) } - if !strategyResult.Enabled { - return defaultVariant - } - - if f == nil { + getFallbackWithFeatureEnabled := func(featureEnabled bool) *api.Variant { if opts.variantFallbackFunc != nil { - return opts.variantFallbackFunc(feature, ctx) + variant := opts.variantFallbackFunc(feature, ctx) + if variant != nil { + variant.FeatureEnabled = featureEnabled + } + return variant } else if opts.variantFallback != nil { + opts.variantFallback.FeatureEnabled = featureEnabled return opts.variantFallback } + + if featureEnabled { + return disabledVariantFeatureEnabled + } return defaultVariant } - if !f.Enabled { - return defaultVariant + if !strategyResult.Enabled { + return getFallbackWithFeatureEnabled(false) + } + + if f == nil || !f.Enabled { + return getFallbackWithFeatureEnabled(false) } if strategyResult.Variant != nil { @@ -438,7 +447,7 @@ func (uc *Client) getVariantWithoutMetrics(feature string, options ...VariantOpt } if len(f.Variants) == 0 { - return disabledVariantFeatureEnabled + return getFallbackWithFeatureEnabled(true) } return api.VariantCollection{ diff --git a/client_test.go b/client_test.go index 8950ce6..3c913ac 100644 --- a/client_test.go +++ b/client_test.go @@ -1144,3 +1144,216 @@ func TestClient_VariantFromEnabledFeatureWithNoVariants(t *testing.T) { assert.True(gock.IsDone(), "there should be no more mocks") } + +func TestGetVariantWithFallbackVariantWhenFeatureDisabled(t *testing.T) { + assert := assert.New(t) + defer gock.OffAll() + + gock.New(mockerServer). + Post("/client/register"). + MatchHeader("UNLEASH-APPNAME", mockAppName). + MatchHeader("UNLEASH-INSTANCEID", mockInstanceId). + Reply(200) + + feature := "feature-disabled" + features := []api.Feature{ + { + Name: feature, + Description: "feature-desc", + Enabled: false, + CreatedAt: time.Date(1974, time.May, 19, 1, 2, 3, 4, time.UTC), + Strategy: "default-strategy", + Strategies: []api.Strategy{ + { + Id: 1, + Name: "default", + }, + }, + }, + } + + gock.New(mockerServer). + Get("/client/features"). + Reply(200). + JSON(api.FeatureResponse{ + Features: features, + Segments: []api.Segment{}, + }) + + mockListener := &MockedListener{} + mockListener.On("OnReady").Return() + mockListener.On("OnRegistered", mock.AnythingOfType("ClientData")) + mockListener.On("OnCount", feature, false).Return() + mockListener.On("OnError").Return() + + client, err := NewClient( + WithUrl(mockerServer), + WithAppName(mockAppName), + WithInstanceId(mockInstanceId), + WithListener(mockListener), + ) + + assert.NoError(err) + client.WaitForReady() + + fallbackVariant := api.Variant{ + Name: "fallback-variant", + FeatureEnabled: true, + } + + variant := client.GetVariant(feature, WithVariantFallback(&fallbackVariant)) + + assert.False(variant.Enabled) + + assert.False(variant.FeatureEnabled) + + assert.Equal(fallbackVariant, *variant) + + fallbackFunc := func(feature string, ctx *context.Context) *api.Variant { + return &fallbackVariant + } + + variantWithFallbackFunc := client.GetVariant(feature, WithVariantFallbackFunc(fallbackFunc)) + + assert.Equal(fallbackVariant, *variantWithFallbackFunc) + + assert.True(gock.IsDone(), "there should be no more mocks") +} + +func TestGetVariantWithFallbackVariantWhenFeatureEnabledButNoVariants(t *testing.T) { + assert := assert.New(t) + defer gock.OffAll() + + gock.New(mockerServer). + Post("/client/register"). + MatchHeader("UNLEASH-APPNAME", mockAppName). + MatchHeader("UNLEASH-INSTANCEID", mockInstanceId). + Reply(200) + + feature := "feature-no-variants" + features := []api.Feature{ + { + Name: feature, + Description: "feature-desc", + Enabled: true, + CreatedAt: time.Date(1974, time.May, 19, 1, 2, 3, 4, time.UTC), + Strategy: "default-strategy", + Strategies: []api.Strategy{ + { + Id: 1, + Name: "default", + }, + }, + }, + } + + gock.New(mockerServer). + Get("/client/features"). + Reply(200). + JSON(api.FeatureResponse{ + Features: features, + Segments: []api.Segment{}, + }) + + mockListener := &MockedListener{} + mockListener.On("OnReady").Return() + mockListener.On("OnRegistered", mock.AnythingOfType("ClientData")) + mockListener.On("OnCount", feature, true).Return() + mockListener.On("OnError").Return() + + client, err := NewClient( + WithUrl(mockerServer), + WithAppName(mockAppName), + WithInstanceId(mockInstanceId), + WithListener(mockListener), + ) + + assert.NoError(err) + client.WaitForReady() + + fallbackVariant := api.Variant{ + Name: "fallback-variant", + FeatureEnabled: false, + } + + variant := client.GetVariant(feature, WithVariantFallback(&fallbackVariant)) + + assert.False(variant.Enabled) + + assert.True(variant.FeatureEnabled) + + assert.Equal(fallbackVariant, *variant) + + fallbackFunc := func(feature string, ctx *context.Context) *api.Variant { + return &fallbackVariant + } + + variantWithFallbackFunc := client.GetVariant(feature, WithVariantFallbackFunc(fallbackFunc)) + + assert.Equal(fallbackVariant, *variantWithFallbackFunc) + + assert.True(gock.IsDone(), "there should be no more mocks") +} + +func TestGetVariantWithFallbackVariantWhenFeatureDoesntExist(t *testing.T) { + assert := assert.New(t) + defer gock.OffAll() + + gock.New(mockerServer). + Post("/client/register"). + MatchHeader("UNLEASH-APPNAME", mockAppName). + MatchHeader("UNLEASH-INSTANCEID", mockInstanceId). + Reply(200) + + feature := "feature-no-variants" + features := []api.Feature{ + {}, + } + + gock.New(mockerServer). + Get("/client/features"). + Reply(200). + JSON(api.FeatureResponse{ + Features: features, + Segments: []api.Segment{}, + }) + + mockListener := &MockedListener{} + mockListener.On("OnReady").Return() + mockListener.On("OnRegistered", mock.AnythingOfType("ClientData")) + mockListener.On("OnCount", feature, false).Return() + mockListener.On("OnError").Return() + + client, err := NewClient( + WithUrl(mockerServer), + WithAppName(mockAppName), + WithInstanceId(mockInstanceId), + WithListener(mockListener), + ) + + assert.NoError(err) + client.WaitForReady() + + fallbackVariant := api.Variant{ + Name: "fallback-variant", + FeatureEnabled: true, + } + + variant := client.GetVariant(feature, WithVariantFallback(&fallbackVariant)) + + assert.False(variant.Enabled) + + assert.False(variant.FeatureEnabled) + + assert.Equal(fallbackVariant, *variant) + + fallbackFunc := func(feature string, ctx *context.Context) *api.Variant { + return &fallbackVariant + } + + variantWithFallbackFunc := client.GetVariant(feature, WithVariantFallbackFunc(fallbackFunc)) + + assert.Equal(fallbackVariant, *variantWithFallbackFunc) + + assert.True(gock.IsDone(), "there should be no more mocks") +}