Skip to content

Commit 9483947

Browse files
authored
test: improve testing and fixing test state issues (#743)
Signed-off-by: Simon Schrottner <[email protected]>
1 parent 49ba246 commit 9483947

File tree

9 files changed

+287
-230
lines changed

9 files changed

+287
-230
lines changed

providers/flagd/e2e/config_test.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package e2e
22

33
import (
4-
context2 "context"
4+
"context"
55
"testing"
66

77
"github.com/cucumber/godog"
@@ -40,23 +40,31 @@ func TestConfiguration(t *testing.T) {
4040
t.Run(tc.name, func(t *testing.T) {
4141
suite := godog.TestSuite{
4242
Name: "flagd-config-" + tc.name,
43-
ScenarioInitializer: func(context *godog.ScenarioContext) {
44-
state := testframework.TestState{
45-
EnvVars: make(map[string]string),
46-
EvalContext: make(map[string]interface{}),
47-
EventChannel: make(chan testframework.EventRecord, 100),
48-
}
49-
testframework.InitializeConfigScenario(context, &state)
50-
context.After(func(ctx context2.Context, sc *godog.Scenario, err error) (context2.Context, error) {
51-
state.CleanupEnvironmentVariables()
52-
return ctx, nil
43+
ScenarioInitializer: func(sc *godog.ScenarioContext) {
44+
45+
testframework.InitializeConfigScenario(sc)
46+
sc.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
47+
state := &testframework.TestState{
48+
EnvVars: make(map[string]string),
49+
EvalContext: make(map[string]interface{}),
50+
EventChannel: make(chan testframework.EventRecord, 100),
51+
}
52+
53+
return context.WithValue(ctx, testframework.TestStateKey{}, state), nil
54+
})
55+
sc.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
56+
if state, ok := ctx.Value(testframework.TestStateKey{}).(*testframework.TestState); ok {
57+
state.CleanupEnvironmentVariables()
58+
}
59+
return ctx, err
5360
})
5461
},
5562
Options: &godog.Options{
56-
Format: "pretty",
57-
Paths: []string{"../flagd-testbed/gherkin/config.feature"},
58-
Tags: tc.tags,
59-
TestingT: t,
63+
Format: "pretty",
64+
Paths: []string{"../flagd-testbed/gherkin/config.feature"},
65+
Tags: tc.tags,
66+
TestingT: t,
67+
DefaultContext: context.Background(),
6068
},
6169
}
6270

tests/flagd/testframework/config_steps.go

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ import (
1212
flagd "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg"
1313
)
1414

15-
// Type definitions moved to types.go
16-
17-
// Context keys moved to types.go
18-
1915
// ignoredOptions a list of options that are currently not supported
2016
var ignoredOptions = []string{
2117
"deadlineMs",
@@ -28,33 +24,32 @@ var ignoredOptions = []string{
2824
}
2925

3026
// InitializeConfigScenario initializes the config test scenario
31-
func InitializeConfigScenario(ctx *godog.ScenarioContext, state *TestState) {
32-
ctx.Step(`^a config was initialized$`, state.aConfigWasInitialized)
33-
ctx.Step(`^an environment variable "([^"]*)" with value "([^"]*)"$`, state.anEnvironmentVariableWithValue)
34-
ctx.Step(`^an option "([^"]*)" of type "([^"]*)" with value "([^"]*)"$`, state.anOptionOfTypeWithValue)
27+
func InitializeConfigScenario(ctx *godog.ScenarioContext) {
28+
ctx.Step(`^a config was initialized$`, withStateNoArgs((*TestState).aConfigWasInitialized))
29+
ctx.Step(`^an environment variable "([^"]*)" with value "([^"]*)"$`, withState2Args((*TestState).anEnvironmentVariableWithValue))
30+
ctx.Step(`^an option "([^"]*)" of type "([^"]*)" with value "([^"]*)"$`, withState3Args((*TestState).anOptionOfTypeWithValue))
3531
ctx.Step(
3632
`^the option "([^"]*)" of type "([^"]*)" should have the value "([^"]*)"$`,
37-
state.theOptionOfTypeShouldHaveTheValue,
33+
withState3ArgsReturningContext((*TestState).theOptionOfTypeShouldHaveTheValue),
3834
)
39-
ctx.Step(`^we should have an error$`, state.weShouldHaveAnError)
35+
ctx.Step(`^we should have an error$`, withStateNoArgsReturningContext((*TestState).weShouldHaveAnError))
4036
}
4137

42-
func (s *TestState) aConfigWasInitialized(ctx context.Context) {
43-
opts := s.GenerateOpts()
38+
// State methods - these now expect state as first parameter
4439

40+
func (s *TestState) aConfigWasInitialized(ctx context.Context) error {
41+
opts := s.GenerateOpts()
4542
providerConfiguration, err := flagd.NewProviderConfiguration(opts)
46-
4743
s.ProviderConfig = ErrorAwareProviderConfiguration{
4844
Configuration: providerConfiguration,
4945
Error: err,
5046
}
47+
return nil
5148
}
5249

5350
func (s *TestState) GenerateOpts() []flagd.ProviderOption {
5451
providerOptions := s.ProviderOptions
55-
5652
var opts []flagd.ProviderOption
57-
5853
for _, providerOption := range providerOptions {
5954
if !slices.Contains(ignoredOptions, providerOption.Option) {
6055
opts = append(
@@ -66,25 +61,24 @@ func (s *TestState) GenerateOpts() []flagd.ProviderOption {
6661
return opts
6762
}
6863

69-
func (s *TestState) anEnvironmentVariableWithValue(key, value string) {
70-
64+
func (s *TestState) anEnvironmentVariableWithValue(ctx context.Context, key, value string) error {
7165
s.EnvVars[key] = os.Getenv(key)
7266
err := os.Setenv(key, value)
7367
if err != nil {
74-
panic(err)
68+
return fmt.Errorf("failed to set environment variable %s: %w", key, err)
7569
}
70+
return nil
7671
}
7772

78-
func (s *TestState) anOptionOfTypeWithValue(ctx context.Context, option, valueType, value string) {
73+
func (s *TestState) anOptionOfTypeWithValue(ctx context.Context, option, valueType, value string) error {
7974
providerOptions := s.ProviderOptions
80-
8175
data := ProviderOption{
8276
Option: option,
8377
ValueType: valueType,
8478
Value: value,
8579
}
86-
8780
s.ProviderOptions = append(providerOptions, data)
81+
return nil
8882
}
8983

9084
func (s *TestState) theOptionOfTypeShouldHaveTheValue(
@@ -95,15 +89,13 @@ func (s *TestState) theOptionOfTypeShouldHaveTheValue(
9589
}
9690

9791
errorAwareConfiguration := s.ProviderConfig
98-
9992
// gherkins null value needs to converted to an empty string
10093
if expectedValueS == "null" {
10194
expectedValueS = ""
10295
}
10396

10497
config := errorAwareConfiguration.Configuration
10598
currentValue := reflect.ValueOf(config).Elem().FieldByName(ToFieldName(option))
106-
10799
converter := NewValueConverter()
108100
var expectedValue = converter.ConvertToReflectValue(valueType, expectedValueS, currentValue.Type())
109101

@@ -115,18 +107,15 @@ func (s *TestState) theOptionOfTypeShouldHaveTheValue(
115107
fmt.Sprintf("%v", currentValue),
116108
)
117109
}
118-
119110
return ctx, nil
120111
}
121112

122113
func (s *TestState) weShouldHaveAnError(ctx context.Context) (context.Context, error) {
123114
errorAwareConfiguration := s.ProviderConfig
124-
125115
if errorAwareConfiguration.Error == nil {
126116
return ctx, errors.New("configuration check succeeded, but should not")
127-
} else {
128-
return ctx, nil
129117
}
118+
return ctx, nil
130119
}
131120

132121
func genericProviderOption(option, valueType, value string) flagd.ProviderOption {

tests/flagd/testframework/context_steps.go

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,72 @@
11
package testframework
22

33
import (
4+
"context"
45
"fmt"
5-
66
"github.com/cucumber/godog"
77
)
88

99
// initializeContextSteps registers evaluation context step definitions
10-
func initializeContextSteps(ctx *godog.ScenarioContext, state *TestState) {
11-
ctx.Step(`^a context containing a key "([^"]*)", with type "([^"]*)" and with value "([^"]*)"$`, state.addContextValue)
12-
ctx.Step(`^an empty context$`, state.clearContext)
13-
ctx.Step(`^a context with the following keys:$`, state.addContextFromTable)
10+
func InitializeContextSteps(ctx *godog.ScenarioContext) {
11+
ctx.Step(`^a context containing a key "([^"]*)", with type "([^"]*)" and with value "([^"]*)"$`,
12+
withState3Args((*TestState).addContextValue))
13+
ctx.Step(`^an empty context$`,
14+
withStateNoArgs((*TestState).clearContext))
15+
ctx.Step(`^a context with the following keys:$`,
16+
withStateTable((*TestState).addContextFromTable))
1417

1518
// Missing step definitions - added as stubs
16-
ctx.Step(`^a context containing a nested property with outer key "([^"]*)" and inner key "([^"]*)", with value "([^"]*)"$`, state.addNestedContextProperty)
17-
ctx.Step(`^a context containing a targeting key with value "([^"]*)"$`, state.addTargetingKeyToContext)
19+
ctx.Step(`^a context containing a nested property with outer key "([^"]*)" and inner key "([^"]*)", with value "([^"]*)"$`,
20+
withState3Args((*TestState).addNestedContextProperty))
21+
ctx.Step(`^a context containing a targeting key with value "([^"]*)"$`,
22+
withState1Arg((*TestState).addTargetingKeyToContext))
23+
24+
// Context validation steps
25+
ctx.Step(`^the context should contain key "([^"]*)"$`,
26+
withState1Arg((*TestState).contextContainsKeyStep))
27+
ctx.Step(`^the context should be empty$`,
28+
withStateNoArgs((*TestState).contextIsEmptyStep))
29+
ctx.Step(`^the context should have (\d+) keys?$`,
30+
withStateIntArg((*TestState).contextShouldHaveKeysStep))
31+
ctx.Step(`^the context value for "([^"]*)" should be "([^"]*)" of type "([^"]*)"$`,
32+
withState3Args((*TestState).contextValueShouldBeStep))
33+
}
34+
35+
// Additional helper functions for different signatures
36+
func withState1Arg(fn func(*TestState, context.Context, string) error) func(context.Context, string) error {
37+
return func(ctx context.Context, arg1 string) error {
38+
state := GetStateFromContext(ctx)
39+
if state == nil {
40+
return fmt.Errorf("test state not found in context")
41+
}
42+
return fn(state, ctx, arg1)
43+
}
44+
}
45+
46+
func withStateTable(fn func(*TestState, context.Context, *godog.Table) error) func(context.Context, *godog.Table) error {
47+
return func(ctx context.Context, table *godog.Table) error {
48+
state := GetStateFromContext(ctx)
49+
if state == nil {
50+
return fmt.Errorf("test state not found in context")
51+
}
52+
return fn(state, ctx, table)
53+
}
1854
}
1955

56+
func withStateIntArg(fn func(*TestState, context.Context, int) error) func(context.Context, int) error {
57+
return func(ctx context.Context, arg1 int) error {
58+
state := GetStateFromContext(ctx)
59+
if state == nil {
60+
return fmt.Errorf("test state not found in context")
61+
}
62+
return fn(state, ctx, arg1)
63+
}
64+
}
65+
66+
// State methods - these now expect context as first parameter after state
67+
2068
// addContextValue adds a typed value to the evaluation context
21-
func (s *TestState) addContextValue(key, valueType, value string) error {
69+
func (s *TestState) addContextValue(ctx context.Context, key, valueType, value string) error {
2270
convertedValue, err := convertValueForSteps(value, valueType)
2371
if err != nil {
2472
return fmt.Errorf("failed to convert context value for key %s: %w", key, err)
@@ -29,13 +77,13 @@ func (s *TestState) addContextValue(key, valueType, value string) error {
2977
}
3078

3179
// clearContext removes all values from the evaluation context
32-
func (s *TestState) clearContext() error {
80+
func (s *TestState) clearContext(ctx context.Context) error {
3381
s.EvalContext = make(map[string]interface{})
3482
return nil
3583
}
3684

3785
// addContextFromTable adds multiple context values from a Gherkin data table
38-
func (s *TestState) addContextFromTable(table *godog.Table) error {
86+
func (s *TestState) addContextFromTable(ctx context.Context, table *godog.Table) error {
3987
if len(table.Rows) < 2 {
4088
return fmt.Errorf("table must have at least header row and one data row")
4189
}
@@ -72,7 +120,7 @@ func (s *TestState) addContextFromTable(table *godog.Table) error {
72120
valueType := row.Cells[typeCol].Value
73121
value := row.Cells[valueCol].Value
74122

75-
if err := s.addContextValue(key, valueType, value); err != nil {
123+
if err := s.addContextValue(ctx, key, valueType, value); err != nil {
76124
return err
77125
}
78126
}
@@ -138,28 +186,20 @@ func (s *TestState) contextIsEmpty() error {
138186
return nil
139187
}
140188

141-
// Additional step definitions for context validation
142-
143-
// registerContextValidationSteps adds validation steps for evaluation context
144-
func (s *TestState) registerContextValidationSteps(ctx *godog.ScenarioContext) {
145-
ctx.Step(`^the context should contain key "([^"]*)"$`, s.contextContainsKeyStep)
146-
ctx.Step(`^the context should be empty$`, s.contextIsEmptyStep)
147-
ctx.Step(`^the context should have (\d+) keys?$`, s.contextShouldHaveKeysStep)
148-
ctx.Step(`^the context value for "([^"]*)" should be "([^"]*)" of type "([^"]*)"$`, s.contextValueShouldBeStep)
149-
}
189+
// Step definition wrappers
150190

151191
// contextContainsKeyStep is a step definition wrapper
152-
func (s *TestState) contextContainsKeyStep(key string) error {
192+
func (s *TestState) contextContainsKeyStep(ctx context.Context, key string) error {
153193
return s.contextContainsKey(key)
154194
}
155195

156196
// contextIsEmptyStep is a step definition wrapper
157-
func (s *TestState) contextIsEmptyStep() error {
197+
func (s *TestState) contextIsEmptyStep(ctx context.Context) error {
158198
return s.contextIsEmpty()
159199
}
160200

161201
// contextShouldHaveKeysStep checks the number of keys in context
162-
func (s *TestState) contextShouldHaveKeysStep(expectedCount int) error {
202+
func (s *TestState) contextShouldHaveKeysStep(ctx context.Context, expectedCount int) error {
163203
actualCount := s.contextSize()
164204
if actualCount != expectedCount {
165205
return fmt.Errorf("expected context to have %d keys, but it has %d", expectedCount, actualCount)
@@ -168,7 +208,7 @@ func (s *TestState) contextShouldHaveKeysStep(expectedCount int) error {
168208
}
169209

170210
// contextValueShouldBeStep checks a specific context value
171-
func (s *TestState) contextValueShouldBeStep(key, expectedValue, valueType string) error {
211+
func (s *TestState) contextValueShouldBeStep(ctx context.Context, key, expectedValue, valueType string) error {
172212
convertedExpected, err := convertValueForSteps(expectedValue, valueType)
173213
if err != nil {
174214
return fmt.Errorf("failed to convert expected value: %w", err)
@@ -177,15 +217,15 @@ func (s *TestState) contextValueShouldBeStep(key, expectedValue, valueType strin
177217
return s.contextValueEquals(key, convertedExpected)
178218
}
179219

180-
// Missing step definition implementations - added as stubs that throw errors
220+
// Missing step definition implementations
181221

182222
// addNestedContextProperty adds a nested property to evaluation context
183-
func (s *TestState) addNestedContextProperty(outerKey, innerKey, value string) error {
184-
return s.addContextValue(outerKey, "Object", fmt.Sprintf("{\"%s\": \"%s\"}", innerKey, value))
223+
func (s *TestState) addNestedContextProperty(ctx context.Context, outerKey, innerKey, value string) error {
224+
return s.addContextValue(ctx, outerKey, "Object", fmt.Sprintf("{\"%s\": \"%s\"}", innerKey, value))
185225
}
186226

187227
// addTargetingKeyToContext adds a targeting key to evaluation context
188-
func (s *TestState) addTargetingKeyToContext(value string) error {
228+
func (s *TestState) addTargetingKeyToContext(ctx context.Context, value string) error {
189229
s.TargetingKey = value
190230
return nil
191231
}

0 commit comments

Comments
 (0)