more tests added

This commit is contained in:
grambbledook
2026-01-12 16:30:41 +01:00
parent 9228b8f0a4
commit f52a6bf88e
4 changed files with 116 additions and 34 deletions
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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
+21 -28
View File
@@ -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}
}