-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbdd_feature_flag_scenarios_test.go
More file actions
209 lines (177 loc) · 5.94 KB
/
bdd_feature_flag_scenarios_test.go
File metadata and controls
209 lines (177 loc) · 5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package reverseproxy
import (
"context"
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"github.com/GoCodeAlone/modular"
)
// Feature Flag Scenario Step Implementations
// Mock evaluator for testing aggregator discovery
type testFeatureFlagEvaluator struct {
name string
weight int
flags map[string]bool
callCount int
returnErr error
}
func (e *testFeatureFlagEvaluator) EvaluateFlag(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request) (bool, error) {
e.callCount++
if e.returnErr != nil {
return false, e.returnErr
}
if val, exists := e.flags[flagID]; exists {
return val, nil
}
return false, ErrFeatureFlagNotFound
}
func (e *testFeatureFlagEvaluator) EvaluateFlagWithDefault(ctx context.Context, flagID string, tenantID modular.TenantID, req *http.Request, defaultValue bool) bool {
result, err := e.EvaluateFlag(ctx, flagID, tenantID, req)
if err != nil {
return defaultValue
}
return result
}
func (e *testFeatureFlagEvaluator) Weight() int {
return e.weight
}
// Step 1: I evaluate a feature flag
func (ctx *ReverseProxyBDDTestContext) iEvaluateAFeatureFlag() error {
// Set up a proper test environment with feature flag evaluator
ctx.resetContext()
// Create test backend servers for feature flag testing
primaryServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("primary backend response"))
}))
ctx.testServers = append(ctx.testServers, primaryServer)
altServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("alternative backend response"))
}))
ctx.testServers = append(ctx.testServers, altServer)
// Create configuration with feature flags
ctx.config = &ReverseProxyConfig{
BackendServices: map[string]string{
"primary-backend": primaryServer.URL,
"alt-backend": altServer.URL,
},
Routes: map[string]string{
"/api/test": "primary-backend",
},
RouteConfigs: map[string]RouteConfig{
"/api/test": {
FeatureFlagID: "test-feature-enabled",
AlternativeBackend: "alt-backend",
},
},
BackendConfigs: map[string]BackendServiceConfig{
"primary-backend": {URL: primaryServer.URL},
"alt-backend": {URL: altServer.URL},
},
FeatureFlags: FeatureFlagsConfig{
Enabled: true,
Flags: map[string]bool{
"test-feature-enabled": true, // Feature enabled by default
},
},
}
// Setup application with feature flag evaluator
if err := ctx.setupApplicationWithConfig(); err != nil {
return fmt.Errorf("failed to setup application: %w", err)
}
// Create a test evaluator
testEvaluator := &testFeatureFlagEvaluator{
name: "testEvaluator",
weight: 100,
flags: map[string]bool{"test-feature-enabled": true},
}
// Register the evaluator with the application
if ctx.app != nil {
ctx.app.RegisterService("testEvaluator", testEvaluator)
}
// Create feature flag service for evaluation
if ctx.service != nil && ctx.app != nil {
aggregator := NewFeatureFlagAggregator(ctx.app, slog.Default())
ctx.featureFlagService = &FileBasedFeatureFlagEvaluator{
app: ctx.app,
logger: slog.Default(),
}
// Set up aggregator as the main evaluator
ctx.app.RegisterService("featureFlagAggregator", aggregator)
}
// Evaluate a test feature flag
if ctx.featureFlagService != nil {
result, err := ctx.featureFlagService.EvaluateFlag(
context.Background(),
"test-feature-enabled",
"",
nil,
)
// Store the result and error for later verification
ctx.lastError = err
if err == nil {
// Store result in context for verification
if ctx.config.FeatureFlags.Flags == nil {
ctx.config.FeatureFlags.Flags = make(map[string]bool)
}
ctx.config.FeatureFlags.Flags["last-evaluated"] = result
}
}
return nil
}
// Step 2: the aggregator discovers evaluators
func (ctx *ReverseProxyBDDTestContext) theAggregatorDiscoversEvaluators() error {
// Set up test context with multiple evaluators
ctx.resetContext()
// Create a test app with service registry
testApp := NewMockTenantApplication()
ctx.app = testApp
// Register the tenant service so feature flag evaluators can access it
tenantService := &MockTenantService{
Configs: make(map[modular.TenantID]map[string]modular.ConfigProvider),
}
testApp.RegisterService("tenantService", tenantService)
// Create and register multiple test evaluators with different service names
eval1 := &testFeatureFlagEvaluator{
name: "customEvaluator",
weight: 50,
flags: map[string]bool{"test-flag": true},
}
eval2 := &testFeatureFlagEvaluator{
name: "remoteFlags",
weight: 75,
flags: map[string]bool{"test-flag": false},
}
eval3 := &testFeatureFlagEvaluator{
name: "rules-engine",
weight: 25,
flags: map[string]bool{"test-flag": true},
}
// Register evaluators with the application
testApp.RegisterService("customEvaluator", eval1)
testApp.RegisterService("remoteFlags", eval2)
testApp.RegisterService("rules-engine", eval3)
// Create aggregator and test discovery
logger := slog.Default()
aggregator := NewFeatureFlagAggregator(testApp, logger)
evaluators := aggregator.discoverEvaluators()
// Store for later verification
ctx.featureFlagService = &FileBasedFeatureFlagEvaluator{
app: testApp,
logger: logger,
}
// Verify discovery worked
if len(evaluators) < 3 {
return fmt.Errorf("expected at least 3 evaluators discovered, got %d", len(evaluators))
}
// Store aggregator in context for further testing
testApp.RegisterService("featureFlagAggregator", aggregator)
return nil
}
// Note: Steps 3-6 are already implemented in other BDD test files:
// - alternativeBackendsShouldBeUsedWhenFlagsAreDisabled is in bdd_feature_flags_test.go
// - alternativeSingleBackendsShouldBeUsedWhenDisabled is in bdd_feature_flags_test.go
// - tenantSpecificRoutingShouldBeApplied is in bdd_feature_flags_test.go
// - comparisonResultsShouldBeLoggedWithFlagContext is in bdd_dryrun_comparison_test.go