Skip to content

Commit 83030ae

Browse files
test(flagd): adding testcontainer support (open-feature#655)
Signed-off-by: Raphael Wigoutschnigg <[email protected]>
1 parent 32f138b commit 83030ae

File tree

15 files changed

+402
-486
lines changed

15 files changed

+402
-486
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,6 @@ jobs:
4343

4444
test:
4545
runs-on: ubuntu-latest
46-
services:
47-
# flagd-testbed for flagd-provider e2e tests
48-
flagd:
49-
image: ghcr.io/open-feature/flagd-testbed:v0.5.6
50-
ports:
51-
- 8013:8013
52-
# sync-testbed for flagd-provider e2e tests
53-
sync:
54-
image: ghcr.io/open-feature/sync-testbed:v0.5.6
55-
ports:
56-
- 9090:9090
5746
steps:
5847
- name: Install Go
5948
uses: actions/setup-go@v4

CONTRIBUTING.md

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,13 @@ make test
6060

6161
It is recommended to include end-to-end (e2e) tests in your provider when possible.
6262
If you have dependency services for your e2e tests, make sure to add them as service in the build pipeline.
63-
Also, make sure to include them in the makefile for easy use by contributors.
6463

65-
To start e2e test helpers use the command:
66-
67-
```shell
68-
make e2e-start-helpers
69-
```
70-
71-
Then, you can run all tests, including e2e tests using the command:
64+
You can run all tests, including e2e tests using the command:
7265

7366
```shell
7467
make e2e
7568
```
7669

77-
Once tests are complete, you can remove e2e test helpers using the command:
78-
79-
```shell
80-
make e2e-remove-helpers
81-
```
82-
8370
## Releases
8471

8572
This repo uses _Release Please_ to release packages. Release Please sets up a running PR that tracks all changes for the library components, and maintains the versions according to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), generated when [PRs are merged](https://github.com/amannn/action-semantic-pull-request).

Makefile

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,7 @@ workspace-update:
1313
test:
1414
go list -f '{{.Dir}}/...' -m | xargs -I{} go test -v {}
1515

16-
e2e-start-helpers:
17-
docker run --name $(FLAGD_TESTBED) -d -p 8013:8013 ghcr.io/open-feature/flagd-testbed:v0.5.6
18-
docker run --name $(FLAGD_SYNC) -d -p 9090:9090 ghcr.io/open-feature/sync-testbed:v0.5.6
19-
20-
e2e-remove-helpers:
21-
docker stop $(FLAGD_TESTBED)
22-
docker stop $(FLAGD_SYNC)
23-
docker rm $(FLAGD_TESTBED)
24-
docker rm $(FLAGD_SYNC)
25-
16+
# call with TESTCONTAINERS_RYUK_DISABLED="true" to avoid problems with podman on Macs
2617
e2e:
2718
go clean -testcache && go list -f '{{.Dir}}/...' -m | xargs -I{} go test -tags=e2e {}
2819

providers/flagd/e2e/config_test.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ func TestConfig(t *testing.T) {
2626

2727
testSuite := godog.TestSuite{
2828
Name: name,
29-
TestSuiteInitializer: integration.InitializeConfigTestSuite(func(envVar, envVarValue string) {
30-
t.Setenv(envVar, envVarValue)
31-
usedEnvVars = append(usedEnvVars, envVar)
32-
}),
29+
TestSuiteInitializer: func(testSuiteContext *godog.TestSuiteContext) {
30+
integration.PrepareConfigTestSuite(
31+
func(envVar, envVarValue string) {
32+
t.Setenv(envVar, envVarValue)
33+
usedEnvVars = append(usedEnvVars, envVar)
34+
},
35+
)
36+
},
3337
ScenarioInitializer: func(ctx *godog.ScenarioContext) {
3438
for _, envVar := range usedEnvVars {
3539
err := os.Unsetenv(envVar)
@@ -43,7 +47,7 @@ func TestConfig(t *testing.T) {
4347
},
4448
Options: &godog.Options{
4549
Format: "pretty",
46-
Paths: []string{"./gherkin/config.feature"},
50+
Paths: []string{"../flagd-testbed/gherkin/config.feature"},
4751
TestingT: t, // Testing instance that will run subtests.
4852
Strict: true,
4953
},
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package containers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/testcontainers/testcontainers-go"
7+
"github.com/testcontainers/testcontainers-go/wait"
8+
"math/rand"
9+
"os"
10+
"strings"
11+
)
12+
13+
type ExposedPort string
14+
15+
const (
16+
Remote ExposedPort = "remote"
17+
InProcess ExposedPort = "in-process"
18+
Launchpad ExposedPort = "launchpad"
19+
)
20+
21+
const (
22+
flagdPortRemote = "8013"
23+
flagdPortInProcess = "8015"
24+
flagdPortLaunchpad = "8016"
25+
)
26+
27+
type flagdConfig struct {
28+
version string
29+
}
30+
31+
type FlagdContainer struct {
32+
testcontainers.Container
33+
portRemote int
34+
portInProcess int
35+
portLaunchpad int
36+
}
37+
38+
func (fc *FlagdContainer) GetPort(port ExposedPort) int {
39+
switch port {
40+
case Remote:
41+
return fc.portRemote
42+
case InProcess:
43+
return fc.portInProcess
44+
case Launchpad:
45+
return fc.portLaunchpad
46+
}
47+
return fc.portRemote
48+
}
49+
50+
func NewFlagd(ctx context.Context) (*FlagdContainer, error) {
51+
version, err := readTestbedVersion()
52+
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
c := &flagdConfig{
58+
version: fmt.Sprintf("v%v", version),
59+
}
60+
61+
return setupContainer(ctx, c)
62+
}
63+
64+
func setupContainer(ctx context.Context, cfg *flagdConfig) (*FlagdContainer, error) {
65+
registry := "ghcr.io/open-feature"
66+
imgName := "flagd-testbed"
67+
68+
fullImgName := registry + "/" + imgName + ":" + cfg.version
69+
70+
req := testcontainers.ContainerRequest{
71+
Image: fullImgName,
72+
Name: fmt.Sprintf("%s-%d", imgName, rand.Int()),
73+
ExposedPorts: []string{
74+
flagdPortRemote + "/tcp",
75+
flagdPortInProcess + "/tcp",
76+
flagdPortLaunchpad + "/tcp",
77+
},
78+
WaitingFor: wait.ForExposedPort(),
79+
Privileged: false,
80+
}
81+
82+
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
83+
ContainerRequest: req,
84+
Started: true,
85+
Reuse: true,
86+
})
87+
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
mappedPortRemote, errRemote := c.MappedPort(ctx, flagdPortRemote)
93+
mappedPortInProcess, errInProcess := c.MappedPort(ctx, flagdPortInProcess)
94+
mappedPortLaunchpad, errLaunchpad := c.MappedPort(ctx, flagdPortLaunchpad)
95+
if errRemote != nil || errInProcess != nil || errLaunchpad != nil {
96+
return nil, err
97+
}
98+
99+
return &FlagdContainer{
100+
Container: c,
101+
portRemote: mappedPortRemote.Int(),
102+
portInProcess: mappedPortInProcess.Int(),
103+
portLaunchpad: mappedPortLaunchpad.Int(),
104+
}, nil
105+
}
106+
107+
func readTestbedVersion() (string, error) {
108+
wd, _ := os.Getwd()
109+
fileName := "../flagd-testbed/version.txt"
110+
111+
content, err := os.ReadFile(fmt.Sprintf("%s/%s", wd, fileName))
112+
if err != nil {
113+
fmt.Printf("Failed to read file: %s", fileName)
114+
return "", err
115+
}
116+
117+
return strings.TrimSuffix(string(content), "\n"), nil
118+
}

providers/flagd/e2e/evaluation_test.go

Lines changed: 20 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,81 +3,34 @@
33
package e2e
44

55
import (
6-
"flag"
7-
"testing"
8-
9-
"github.com/cucumber/godog"
6+
"github.com/open-feature/go-sdk-contrib/providers/flagd/e2e/containers"
107
flagd "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg"
118
"github.com/open-feature/go-sdk-contrib/tests/flagd/pkg/integration"
12-
"github.com/open-feature/go-sdk/openfeature"
9+
"testing"
1310
)
1411

1512
func TestETestEvaluationFlagdInRPC(t *testing.T) {
16-
if testing.Short() {
17-
// skip e2e if testing -short
18-
t.Skip()
19-
}
20-
21-
flag.Parse()
22-
23-
name := "evaluation.feature"
24-
25-
testSuite := godog.TestSuite{
26-
Name: name,
27-
TestSuiteInitializer: integration.InitializeEvaluationTestSuite(func() openfeature.FeatureProvider {
28-
provider, err := flagd.NewProvider(flagd.WithPort(8013))
29-
30-
if err != nil {
31-
t.Fatal("Creating provider failed:", err)
32-
}
33-
34-
return provider
35-
}),
36-
ScenarioInitializer: integration.InitializeEvaluationScenario,
37-
Options: &godog.Options{
38-
Format: "pretty",
39-
Paths: []string{"../spec/specification/assets/gherkin/evaluation.feature"},
40-
TestingT: t, // Testing instance that will run subtests.
41-
Strict: true,
42-
},
43-
}
44-
45-
if testSuite.Run() != 0 {
46-
t.Fatal("non-zero status returned, failed to run evaluation tests")
47-
}
13+
testJsonEvaluatorFlagd(t, containers.Remote)
4814
}
4915

5016
func TestJsonEvaluatorFlagdInProcess(t *testing.T) {
51-
if testing.Short() {
52-
// skip e2e if testing -short
53-
t.Skip()
54-
}
55-
56-
flag.Parse()
57-
58-
name := "evaluation.feature"
59-
60-
testSuite := godog.TestSuite{
61-
Name: name,
62-
TestSuiteInitializer: integration.InitializeEvaluationTestSuite(func() openfeature.FeatureProvider {
63-
provider, err := flagd.NewProvider(flagd.WithInProcessResolver(), flagd.WithPort(9090))
64-
65-
if err != nil {
66-
t.Fatal("Creating provider failed:", err)
67-
}
17+
testJsonEvaluatorFlagd(t, containers.InProcess, flagd.WithInProcessResolver())
18+
}
6819

69-
return provider
70-
}),
71-
ScenarioInitializer: integration.InitializeEvaluationScenario,
72-
Options: &godog.Options{
73-
Format: "pretty",
74-
Paths: []string{"../spec/specification/assets/gherkin/evaluation.feature"},
75-
TestingT: t, // Testing instance that will run subtests.
76-
Strict: true,
20+
func testJsonEvaluatorFlagd(
21+
t *testing.T,
22+
exposedPort containers.ExposedPort,
23+
providerOptions ...flagd.ProviderOption,
24+
) {
25+
runGherkinTestWithFeatureProvider(
26+
gherkinTestRunConfig{
27+
t: t,
28+
prepareTestSuite: integration.PrepareEvaluationTestSuite,
29+
scenarioInitializer: integration.InitializeEvaluationScenario,
30+
name: "evaluation.feature",
31+
gherkinFile: "../spec/specification/assets/gherkin/evaluation.feature",
32+
port: exposedPort,
33+
providerOptions: providerOptions,
7734
},
78-
}
79-
80-
if testSuite.Run() != 0 {
81-
t.Fatal("non-zero status returned, failed to run evaluation tests")
82-
}
35+
)
8336
}

0 commit comments

Comments
 (0)