more tests added
This commit is contained in:
@@ -26,7 +26,7 @@ type OpenFeatureConfig struct {
|
||||
// HTTPClient is a pre-configured HTTP client (optional, used by features-service + OFREP providers)
|
||||
HTTPClient *http.Client
|
||||
// StaticFlags are the feature flags to use with static provider
|
||||
StaticFlags map[string]bool
|
||||
StaticFlags map[string]setting.FeatureToggle
|
||||
// TargetingKey is used for evaluation context
|
||||
TargetingKey string
|
||||
// ContextAttrs are additional attributes for evaluation context
|
||||
@@ -100,7 +100,7 @@ func InitOpenFeatureWithCfg(cfg *setting.Cfg) error {
|
||||
func createProvider(
|
||||
providerType string,
|
||||
u *url.URL,
|
||||
staticFlags map[string]bool,
|
||||
StaticFlags map[string]setting.FeatureToggle,
|
||||
httpClient *http.Client,
|
||||
) (openfeature.FeatureProvider, error) {
|
||||
if providerType == setting.FeaturesServiceProviderType || providerType == setting.OFREPProviderType {
|
||||
|
||||
@@ -47,7 +47,7 @@ func ProvideManagerService(cfg *setting.Cfg) (*FeatureManager, error) {
|
||||
}
|
||||
mgmt.warnings[key] = "unknown flag in config"
|
||||
}
|
||||
mgmt.startup[key] = val
|
||||
mgmt.startup[key] = val.Value == true
|
||||
}
|
||||
|
||||
// update the values
|
||||
|
||||
@@ -3,6 +3,7 @@ package featuremgmt
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/open-feature/go-sdk/openfeature"
|
||||
"github.com/open-feature/go-sdk/openfeature/memprovider"
|
||||
"strconv"
|
||||
@@ -31,51 +32,43 @@ func (p *inMemoryBulkProvider) ListFlags() ([]string, error) {
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func newStaticProvider(confFlags map[string]bool, standardFlags []FeatureFlag) (openfeature.FeatureProvider, error) {
|
||||
func newStaticProvider(confFlags map[string]setting.FeatureToggle, standardFlags []FeatureFlag) (openfeature.FeatureProvider, error) {
|
||||
flags := make(map[string]memprovider.InMemoryFlag, len(standardFlags))
|
||||
|
||||
// Add flags from config.ini file
|
||||
for name, value := range confFlags {
|
||||
flags[name] = createInMemoryFlag(name, value)
|
||||
}
|
||||
|
||||
index := make(map[string]FeatureFlag, len(standardFlags))
|
||||
// Add standard flags
|
||||
for _, flag := range standardFlags {
|
||||
_, exists := flags[flag.Name]
|
||||
|
||||
// Fail fast if a flag is declared with a mismatched type
|
||||
if exists && flag.Type != Boolean {
|
||||
return nil, fmt.Errorf("flag %s already declared as boolean", flag.Name)
|
||||
}
|
||||
|
||||
// config.ini take precedence over code
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
inMemFlag, err := createTypedFlag(flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flags[flag.Name] = inMemFlag
|
||||
index[flag.Name] = flag
|
||||
}
|
||||
|
||||
// Add flags from config.ini file
|
||||
for name, flag := range confFlags {
|
||||
standard, exists := index[flag.Name]
|
||||
|
||||
// Fail fast if a flag is declared with a mismatched type
|
||||
if exists && standard.Type.String() != string(flag.Type) {
|
||||
return nil, fmt.Errorf("type mismatch for flag '%s' detected", flag.Name)
|
||||
}
|
||||
|
||||
flags[name] = createInMemoryFlag(flag)
|
||||
}
|
||||
|
||||
return newInMemoryBulkProvider(flags), nil
|
||||
}
|
||||
|
||||
func createInMemoryFlag(name string, enabled bool) memprovider.InMemoryFlag {
|
||||
variant := "disabled"
|
||||
if enabled {
|
||||
variant = "enabled"
|
||||
}
|
||||
func createInMemoryFlag(flag setting.FeatureToggle) memprovider.InMemoryFlag {
|
||||
variant := "default"
|
||||
|
||||
return memprovider.InMemoryFlag{
|
||||
Key: name,
|
||||
Key: flag.Name,
|
||||
DefaultVariant: variant,
|
||||
Variants: map[string]interface{}{
|
||||
"enabled": true,
|
||||
"disabled": false,
|
||||
Variants: map[string]any{
|
||||
variant: flag.Value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +95,19 @@ ABCD = true
|
||||
}
|
||||
|
||||
func Test_StaticProvider_FailfastOnMismatchedType(t *testing.T) {
|
||||
staticFlags := map[string]bool{"oldBooleanFlag": true}
|
||||
staticFlags := map[string]setting.FeatureToggle{"oldBooleanFlag": {
|
||||
Type: setting.Boolean,
|
||||
Name: "oldBooleanFlag",
|
||||
Value: true,
|
||||
}}
|
||||
|
||||
flag := FeatureFlag{
|
||||
Name: "oldBooleanFlag",
|
||||
Expression: "1.0",
|
||||
Type: Integer,
|
||||
Type: Float,
|
||||
}
|
||||
_, err := newStaticProvider(staticFlags, []FeatureFlag{flag})
|
||||
assert.EqualError(t, err, "flag oldBooleanFlag already declared as boolean")
|
||||
assert.EqualError(t, err, "type mismatch for flag 'oldBooleanFlag' detected")
|
||||
}
|
||||
|
||||
func Test_StaticProvider_TypedFlags(t *testing.T) {
|
||||
@@ -180,3 +184,88 @@ func Test_StaticProvider_TypedFlags(t *testing.T) {
|
||||
assert.Equal(t, tt.expectedValue, result)
|
||||
}
|
||||
}
|
||||
func Test_StaticProvider_ConfigOverride(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
typ FeatureFlagType
|
||||
originalValue string
|
||||
configValue any
|
||||
}{
|
||||
{
|
||||
name: "bool",
|
||||
typ: Boolean,
|
||||
originalValue: "false",
|
||||
configValue: true,
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
typ: Integer,
|
||||
originalValue: "0",
|
||||
configValue: int64(1),
|
||||
},
|
||||
{
|
||||
name: "float",
|
||||
typ: Float,
|
||||
originalValue: "0.0",
|
||||
configValue: 1.0,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
typ: String,
|
||||
originalValue: "foo",
|
||||
configValue: "bar",
|
||||
},
|
||||
{
|
||||
name: "structure",
|
||||
typ: Structure,
|
||||
originalValue: "{}",
|
||||
configValue: make(map[string]any),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
configFlags, standardFlags := makeFlags(tt)
|
||||
provider, err := newStaticProvider(configFlags, standardFlags)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var result any
|
||||
switch tt.typ {
|
||||
case Boolean:
|
||||
result = provider.BooleanEvaluation(t.Context(), tt.name, false, openfeature.FlattenedContext{}).Value
|
||||
case Float:
|
||||
result = provider.FloatEvaluation(t.Context(), tt.name, 0.0, openfeature.FlattenedContext{}).Value
|
||||
case String:
|
||||
result = provider.StringEvaluation(t.Context(), tt.name, "foo", openfeature.FlattenedContext{}).Value
|
||||
case Integer:
|
||||
result = provider.IntEvaluation(t.Context(), tt.name, 1, openfeature.FlattenedContext{}).Value
|
||||
case Structure:
|
||||
result = provider.ObjectEvaluation(t.Context(), tt.name, make(map[string]any), openfeature.FlattenedContext{}).Value
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.configValue, result)
|
||||
}
|
||||
}
|
||||
|
||||
func makeFlags(tt struct {
|
||||
name string
|
||||
typ FeatureFlagType
|
||||
originalValue string
|
||||
configValue any
|
||||
}) (map[string]setting.FeatureToggle, []FeatureFlag) {
|
||||
orig := FeatureFlag{
|
||||
Name: tt.name,
|
||||
Expression: tt.originalValue,
|
||||
Type: tt.typ,
|
||||
}
|
||||
|
||||
config := map[string]setting.FeatureToggle{
|
||||
tt.name: {
|
||||
Name: tt.name,
|
||||
Type: setting.FeatureToggleType(tt.typ.String()),
|
||||
Value: tt.configValue,
|
||||
},
|
||||
}
|
||||
|
||||
return config, []FeatureFlag{orig}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user