Skip to content

Commit dc81e91

Browse files
authored
chore: absorb extracted test suite (#143)
* chore: absorb extracted test suite Signed-off-by: Skye Gill <[email protected]> * add flagd test module to readme Signed-off-by: Skye Gill <[email protected]> --------- Signed-off-by: Skye Gill <[email protected]>
1 parent 31bd8b7 commit dc81e91

File tree

6 files changed

+12
-1048
lines changed

6 files changed

+12
-1048
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Run unit tests with `make test`.
105105

106106
#### Integration tests
107107

108-
The continuous integration runs a set of [gherkin integration tests](https://github.com/open-feature/test-harness/blob/main/features) using the [flagd provider](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/flagd) and [flagd](https://github.com/open-feature/flagd).
108+
The continuous integration runs a set of [gherkin integration tests](https://github.com/open-feature/test-harness/blob/main/features) using the [flagd provider](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/flagd), [flagd](https://github.com/open-feature/flagd) and [the flagd test module](https://github.com/open-feature/go-sdk-contrib/tree/main/tests/flagd).
109109
If you'd like to run them locally, first pull the `test-harness` git submodule
110110
```
111111
git submodule update --init --recursive

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ require (
66
github.com/cucumber/godog v0.12.6
77
github.com/go-logr/logr v1.2.3
88
github.com/golang/mock v1.6.0
9-
github.com/golang/protobuf v1.5.2
10-
github.com/open-feature/flagd v0.3.4
119
github.com/open-feature/go-sdk-contrib/providers/flagd v0.1.4
10+
github.com/open-feature/go-sdk-contrib/tests/flagd v1.0.1
1211
golang.org/x/text v0.6.0
1312
)
1413

@@ -22,6 +21,7 @@ require (
2221
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
2322
github.com/diegoholiveira/jsonlogic/v3 v3.2.7 // indirect
2423
github.com/gofrs/uuid v4.2.0+incompatible // indirect
24+
github.com/golang/protobuf v1.5.2 // indirect
2525
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
2626
github.com/hashicorp/go-memdb v1.3.2 // indirect
2727
github.com/hashicorp/golang-lru v0.5.4 // indirect
@@ -30,6 +30,7 @@ require (
3030
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
3131
github.com/mitchellh/copystructure v1.2.0 // indirect
3232
github.com/mitchellh/reflectwalk v1.0.2 // indirect
33+
github.com/open-feature/flagd v0.3.4 // indirect
3334
github.com/open-feature/schemas v0.2.8 // indirect
3435
github.com/prometheus/client_golang v1.14.0 // indirect
3536
github.com/prometheus/client_model v0.3.0 // indirect

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
4949
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
5050
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
5151
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
52-
github.com/bufbuild/connect-go v1.4.1 h1:6usL3JGjKhxQpvDlizP7u8VfjAr1JkckcAUbrdcbgNY=
53-
github.com/bufbuild/connect-go v1.4.1/go.mod h1:9iNvh/NOsfhNBUH5CtvXeVUskQO1xsrEviH7ZArwZ3I=
5452
github.com/bufbuild/connect-go v1.5.0 h1:IfbgbzzaaZvF+OM3SfxO2EjtvNJarNAz2DIRuuNjAgc=
5553
github.com/bufbuild/connect-go v1.5.0/go.mod h1:9iNvh/NOsfhNBUH5CtvXeVUskQO1xsrEviH7ZArwZ3I=
5654
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -207,12 +205,12 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
207205
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
208206
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
209207
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
210-
github.com/open-feature/flagd v0.3.2 h1:Xfmtd8z2LQ80ux2nwymNx8GrLebsLfbtDF3bFC1nVVM=
211-
github.com/open-feature/flagd v0.3.2/go.mod h1:Xlle3jks+TBtZ6MKl/AiAeSB5FocSEwdq5CjO1O5eUs=
212208
github.com/open-feature/flagd v0.3.4 h1:EeEzMgRGzyyrpsvk3fZgK1dDa8+7zxLBJV5fXii6si0=
213209
github.com/open-feature/flagd v0.3.4/go.mod h1:JOIpaTkqYfAoUCCbVdSGoxKTJr2SV4FGuKmGjRfaQq8=
214210
github.com/open-feature/go-sdk-contrib/providers/flagd v0.1.4 h1:AkaY+pqcjHQe8W/ZFdfJpsPGsxoLyPRYHpTMIhiboAw=
215211
github.com/open-feature/go-sdk-contrib/providers/flagd v0.1.4/go.mod h1:5U1Ry0iFy4j466JafVdK210E7wo6YODKnoaREyhCiHo=
212+
github.com/open-feature/go-sdk-contrib/tests/flagd v1.0.1 h1:FATKM2T7O4OBeVF7pjJldwegbGm1JXhX11f5ffldbFA=
213+
github.com/open-feature/go-sdk-contrib/tests/flagd v1.0.1/go.mod h1:Oo6p7XjTcMHbiWrU/UwxN6Uguo0lHGfMsTYksEs/cKE=
216214
github.com/open-feature/schemas v0.2.8 h1:oA75hJXpOd9SFgmNI2IAxWZkwzQPUDm7Jyyh3q489wM=
217215
github.com/open-feature/schemas v0.2.8/go.mod h1:vj+rfTsOLlh5PtGGkAbitnJmFPYuTHXTjOy13kzNgKQ=
218216
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

integration/caching_test.go

Lines changed: 4 additions & 269 deletions
Original file line numberDiff line numberDiff line change
@@ -1,283 +1,28 @@
11
package integration_test
22

33
import (
4-
"context"
5-
"encoding/json"
6-
"errors"
7-
"fmt"
8-
"os"
94
"testing"
10-
"time"
5+
6+
"github.com/open-feature/go-sdk-contrib/tests/flagd/pkg/integration"
117

128
"github.com/cucumber/godog"
13-
"github.com/open-feature/flagd/pkg/eval"
14-
flagd "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg"
15-
"github.com/open-feature/go-sdk/pkg/openfeature"
169
)
1710

1811
const flagConfigurationPath = "../test-harness/testing-flags.json"
1912

20-
var testingFlags eval.Flags
21-
22-
func loadFlagConfiguration(path string) (eval.Flags, error) {
23-
file, err := os.Open(path)
24-
if err != nil {
25-
return eval.Flags{}, err
26-
}
27-
28-
var flags eval.Flags
29-
err = json.NewDecoder(file).Decode(&flags)
30-
if err != nil {
31-
return eval.Flags{}, fmt.Errorf("decode %s: %v", path, err)
32-
}
33-
34-
return flags, nil
35-
}
36-
37-
func theFlagsConfigurationWithKeyIsUpdatedToDefaultVariant(flagKey, defaultVariant string) error {
38-
file, err := os.Create(flagConfigurationPath)
39-
if err != nil {
40-
return fmt.Errorf("open flag configuration: %w", err)
41-
}
42-
43-
flags := copyFlags(testingFlags)
44-
flagConfig := flags.Flags[flagKey]
45-
flagConfig.DefaultVariant = defaultVariant
46-
flags.Flags[flagKey] = flagConfig
47-
48-
err = json.NewEncoder(file).Encode(flags)
49-
if err != nil {
50-
return fmt.Errorf("write flag configuration to file: %w", err)
51-
}
52-
53-
return nil
54-
}
55-
56-
func theResolvedBooleanDetailsReasonOfFlagWithKeyShouldBe(ctx context.Context, flagkey, reason string) error {
57-
store, ok := ctx.Value(ctxStorageKey{}).(map[string]openfeature.BooleanEvaluationDetails)
58-
if !ok {
59-
return errors.New("no flag resolution result")
60-
}
61-
62-
got, ok := store[flagkey]
63-
if !ok {
64-
return fmt.Errorf("flag result with key %s not found", flagkey)
65-
}
66-
67-
if string(got.Reason) != reason {
68-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
69-
}
70-
71-
return nil
72-
}
73-
74-
func theResolvedStringDetailsReasonOfFlagWithKeyShouldBe(ctx context.Context, flagkey, reason string) error {
75-
store, ok := ctx.Value(ctxStorageKey{}).(map[string]openfeature.StringEvaluationDetails)
76-
if !ok {
77-
return errors.New("no flag resolution result")
78-
}
79-
80-
got, ok := store[flagkey]
81-
if !ok {
82-
return fmt.Errorf("flag result with key %s not found", flagkey)
83-
}
84-
85-
if string(got.Reason) != reason {
86-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
87-
}
88-
89-
return nil
90-
}
91-
92-
func theResolvedIntegerDetailsReasonOfFlagWithKeyShouldBe(ctx context.Context, flagkey, reason string) error {
93-
store, ok := ctx.Value(ctxStorageKey{}).(map[string]openfeature.IntEvaluationDetails)
94-
if !ok {
95-
return errors.New("no flag resolution result")
96-
}
97-
98-
got, ok := store[flagkey]
99-
if !ok {
100-
return fmt.Errorf("flag result with key %s not found", flagkey)
101-
}
102-
103-
if string(got.Reason) != reason {
104-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
105-
}
106-
107-
return nil
108-
}
109-
110-
func theResolvedFloatDetailsReasonOfFlagWithKeyShouldBe(ctx context.Context, flagkey, reason string) error {
111-
store, ok := ctx.Value(ctxStorageKey{}).(map[string]openfeature.FloatEvaluationDetails)
112-
if !ok {
113-
return errors.New("no flag resolution result")
114-
}
115-
116-
got, ok := store[flagkey]
117-
if !ok {
118-
return fmt.Errorf("flag result with key %s not found", flagkey)
119-
}
120-
121-
if string(got.Reason) != reason {
122-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
123-
}
124-
125-
return nil
126-
}
127-
128-
func theResolvedObjectDetailsReasonOfFlagWithKeyShouldBe(ctx context.Context, flagkey, reason string) error {
129-
store, ok := ctx.Value(ctxStorageKey{}).(map[string]openfeature.InterfaceEvaluationDetails)
130-
if !ok {
131-
return errors.New("no flag resolution result")
132-
}
133-
134-
got, ok := store[flagkey]
135-
if !ok {
136-
return fmt.Errorf("flag result with key %s not found", flagkey)
137-
}
138-
139-
if string(got.Reason) != reason {
140-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
141-
}
142-
143-
return nil
144-
}
145-
146-
func theResolvedBooleanDetailsReasonShouldBe(ctx context.Context, reason string) error {
147-
got, err := firstBooleanEvaluationDetails(ctx)
148-
if err != nil {
149-
return err
150-
}
151-
152-
if string(got.Reason) != reason {
153-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
154-
}
155-
156-
return nil
157-
}
158-
159-
func theResolvedStringDetailsReasonShouldBe(ctx context.Context, reason string) error {
160-
got, err := firstStringEvaluationDetails(ctx)
161-
if err != nil {
162-
return err
163-
}
164-
165-
if string(got.Reason) != reason {
166-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
167-
}
168-
169-
return nil
170-
}
171-
172-
func theResolvedIntegerDetailsReasonShouldBe(ctx context.Context, reason string) error {
173-
got, err := firstIntegerEvaluationDetails(ctx)
174-
if err != nil {
175-
return err
176-
}
177-
178-
if string(got.Reason) != reason {
179-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
180-
}
181-
182-
return nil
183-
}
184-
185-
func theResolvedFloatDetailsReasonShouldBe(ctx context.Context, reason string) error {
186-
got, err := firstFloatEvaluationDetails(ctx)
187-
if err != nil {
188-
return err
189-
}
190-
191-
if string(got.Reason) != reason {
192-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
193-
}
194-
195-
return nil
196-
}
197-
198-
func theResolvedObjectDetailsReasonShouldBe(ctx context.Context, reason string) error {
199-
got, err := firstInterfaceEvaluationDetails(ctx)
200-
if err != nil {
201-
return err
202-
}
203-
204-
if string(got.Reason) != reason {
205-
return fmt.Errorf("expected reason to be %s, got %s", reason, got.Reason)
206-
}
207-
208-
return nil
209-
}
210-
211-
func sleepForMilliseconds(milliseconds int64) error {
212-
time.Sleep(time.Duration(milliseconds) * time.Millisecond)
213-
return nil
214-
}
215-
216-
func resetState(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
217-
file, err := os.Create(flagConfigurationPath)
218-
if err != nil {
219-
return ctx, fmt.Errorf("open flag configuration: %w", err)
220-
}
221-
222-
err = json.NewEncoder(file).Encode(testingFlags)
223-
if err != nil {
224-
return ctx, fmt.Errorf("write flag configuration to file: %w", err)
225-
}
226-
227-
return ctx, nil
228-
}
229-
230-
func aProviderIsRegisteredWithCacheEnabled(ctx context.Context) (context.Context, error) {
231-
provider := flagd.NewProvider(flagd.WithPort(8013))
232-
openfeature.SetProvider(provider)
233-
client := openfeature.NewClient("caching tests")
234-
235-
select {
236-
case <-provider.IsReady():
237-
case <-time.After(500 * time.Millisecond):
238-
return ctx, errors.New("provider not ready after 500 milliseconds")
239-
}
240-
241-
return context.WithValue(ctx, ctxClientKey{}, client), nil
242-
}
243-
244-
func InitializeCachingScenario(ctx *godog.ScenarioContext) {
245-
ctx.Step(`^a boolean flag with key "([^"]*)" is evaluated with details and default value "([^"]*)"$`, aBooleanFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue)
246-
ctx.Step(`^a float flag with key "([^"]*)" is evaluated with details and default value (\d+)\.(\d+)$`, aFloatFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue)
247-
ctx.Step(`^a string flag with key "([^"]*)" is evaluated with details and default value "([^"]*)"$`, aStringFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue)
248-
ctx.Step(`^an integer flag with key "([^"]*)" is evaluated with details and default value (\d+)$`, anIntegerFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue)
249-
ctx.Step(`^an object flag with key "([^"]*)" is evaluated with details and a null default value$`, anObjectFlagWithKeyIsEvaluatedWithDetailsAndANullDefaultValue)
250-
ctx.Step(`^a provider is registered with cache enabled$`, aProviderIsRegisteredWithCacheEnabled)
251-
ctx.Step(`^sleep for (\d+) milliseconds$`, sleepForMilliseconds)
252-
ctx.Step(`^the flag\'s configuration with key "([^"]*)" is updated to defaultVariant "([^"]*)"$`, theFlagsConfigurationWithKeyIsUpdatedToDefaultVariant)
253-
ctx.Step(`^the resolved boolean details reason of flag with key "([^"]*)" should be "([^"]*)"$`, theResolvedBooleanDetailsReasonOfFlagWithKeyShouldBe)
254-
ctx.Step(`^the resolved boolean details reason should be "([^"]*)"$`, theResolvedBooleanDetailsReasonShouldBe)
255-
ctx.Step(`^the resolved float details reason of flag with key "([^"]*)" should be "([^"]*)"$`, theResolvedFloatDetailsReasonOfFlagWithKeyShouldBe)
256-
ctx.Step(`^the resolved float details reason should be "([^"]*)"$`, theResolvedFloatDetailsReasonShouldBe)
257-
ctx.Step(`^the resolved integer details reason of flag with key "([^"]*)" should be "([^"]*)"$`, theResolvedIntegerDetailsReasonOfFlagWithKeyShouldBe)
258-
ctx.Step(`^the resolved integer details reason should be "([^"]*)"$`, theResolvedIntegerDetailsReasonShouldBe)
259-
ctx.Step(`^the resolved object details reason of flag with key "([^"]*)" should be "([^"]*)"$`, theResolvedObjectDetailsReasonOfFlagWithKeyShouldBe)
260-
ctx.Step(`^the resolved object details reason should be "([^"]*)"$`, theResolvedObjectDetailsReasonShouldBe)
261-
ctx.Step(`^the resolved string details reason of flag with key "([^"]*)" should be "([^"]*)"$`, theResolvedStringDetailsReasonOfFlagWithKeyShouldBe)
262-
ctx.Step(`^the resolved string details reason should be "([^"]*)"$`, theResolvedStringDetailsReasonShouldBe)
263-
264-
ctx.Before(resetState)
265-
}
266-
26713
func TestCaching(t *testing.T) {
26814
if testing.Short() {
26915
t.Skip()
27016
}
27117

272-
var err error
273-
testingFlags, err = loadFlagConfiguration(flagConfigurationPath)
18+
initializeCachingScenario, err := integration.InitializeCachingScenario(flagConfigurationPath)
27419
if err != nil {
27520
t.Fatal(err)
27621
}
27722

27823
suite := godog.TestSuite{
27924
Name: "caching.feature",
280-
ScenarioInitializer: InitializeCachingScenario,
25+
ScenarioInitializer: initializeCachingScenario,
28126
Options: &godog.Options{
28227
Format: "pretty",
28328
Paths: []string{"../test-harness/features/caching.feature"},
@@ -289,13 +34,3 @@ func TestCaching(t *testing.T) {
28934
t.Fatal("non-zero status returned, failed to run caching tests")
29035
}
29136
}
292-
293-
func copyFlags(flags eval.Flags) eval.Flags {
294-
f := eval.Flags{Flags: map[string]eval.Flag{}}
295-
296-
for key, flag := range flags.Flags {
297-
f.Flags[key] = flag
298-
}
299-
300-
return f
301-
}

0 commit comments

Comments
 (0)