Skip to content

Commit 6f28e49

Browse files
authored
add e2e test (tellerops#104)
add e2e test Signed-off-by: Elad Kaplan <[email protected]>
1 parent 18bca60 commit 6f28e49

17 files changed

+723
-0
lines changed

.github/workflows/ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ jobs:
3636

3737
- name: Test
3838
run: make test
39+
40+
- name: E2E
41+
run: make e2e

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ deps:
3636
release:
3737
goreleaser --rm-dist
3838

39+
build:
40+
go build -ldflags "-s -w -X main.version=0.0.0 -X main.commit=0000000000000000000000000000000000000000 -X main.date=2022-01-01"
41+
42+
e2e: build
43+
BINARY_PATH="$(shell pwd)/teller" go test -v ./e2e
44+
3945
coverage:
4046
go test ./pkg/... -coverprofile=coverage.out
4147
go tool cover -func=coverage.out

e2e/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# End to End Tests
2+
3+
This package contains integration tests.
4+
5+
## Running
6+
```sh
7+
make e2e
8+
```
9+
10+
## Create E2E test
11+
12+
You have two different options to create an E2E test in Teller.
13+
14+
### Simple
15+
The simple and fast way is based on `yml` file configuration. All you need to do is create a *.yml file in [test folder](./tests/) with the following fields:
16+
17+
| Field | Description |
18+
|-------------------------------|------------------------------------
19+
| `name` | E2E name.
20+
| `command` | Command to execute. You need to keep `<name>`; this placeholder will replace with the `BINARY_PATH` binary value.
21+
| `config_file_name` | Configuration file name. If empty, the configuration file will not be created.
22+
| `config_content` | Configuration file content.
23+
| `init_snapshot` | List of files that were going to create before Teller binary is executed.
24+
| `init_snapshot.path` | Create file in path.
25+
| `init_snapshot.file_name` | File name.
26+
| `init_snapshot.content` | File content.
27+
| `expected_snapshot` | Compare the init_snapshot folder with the expected snapshot content. If empty, this compare check will be ignored.
28+
| `expected_snapshot.path` | Create file in path.
29+
| `expected_snapshot.file_name` | File name.
30+
| `expected_snapshot.content` | File content.
31+
| `expected_stdout` | Check if Teller stdout equals this value. If empty, this check will be ignored.
32+
| `expected_stderr` | Check if Teller stderr equals this value. If empty, this check will be ignored.
33+
34+
### Advanced
35+
In case the E2E `yml`not flexible enough you have the option to create a `*.go` file in [test folder](./tests/). the go file most to:
36+
1. Implement [TestCaseDescriber](./register/interface.go) interface
37+
2. Must to user `init` function and register to [AddSuite](./register/register.go)

e2e/e2e_test.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package e2e
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"testing"
11+
12+
"github.com/spectralops/teller/e2e/register"
13+
_ "github.com/spectralops/teller/e2e/tests"
14+
"github.com/spectralops/teller/e2e/testutils"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
const (
19+
replaceToStaticPath = "DYNAMIC-FULL-PATH"
20+
replaceToShortStaticPath = "DYNAMIC-SHORT-PATH"
21+
removeBinaryPlaceholder = "<binary-path>"
22+
testsFolder = "tests"
23+
)
24+
25+
func TestE2E(t *testing.T) {
26+
t.Parallel()
27+
28+
// validate given binary path
29+
binaryPath, err := getBinaryPath()
30+
if err != nil {
31+
assert.FailNow(t, err.Error())
32+
}
33+
34+
snapshotSuites, err := testutils.GetYmlSnapshotSuites(testsFolder)
35+
assert.Nil(t, err)
36+
37+
// consider to replace `diff` command which is depended on OS to golang plugin.
38+
// could't find something better
39+
differ := testutils.NewExecDiffer()
40+
// Loop on all test/*.yml files
41+
for _, snapshot := range snapshotSuites {
42+
43+
t.Run(snapshot.Name, func(t *testing.T) {
44+
45+
// create a temp folder for the test
46+
tempFolder, err := os.MkdirTemp(t.TempDir(), strings.ReplaceAll(snapshot.Name, " ", ""))
47+
// descrive the base snapshot data of the test
48+
snapshotFolder := filepath.Join(tempFolder, "snapshot")
49+
assert.Nil(t, err, "could not create temp folder")
50+
defer os.RemoveAll(tempFolder)
51+
52+
if len(snapshot.InitSnapshot) > 0 {
53+
err = snapshot.CreateSnapshotData(snapshot.InitSnapshot, snapshotFolder)
54+
assert.Nil(t, err)
55+
}
56+
57+
if snapshot.ConfigFileName != "" {
58+
err = snapshot.CrateConfig(snapshotFolder)
59+
assert.Nil(t, err)
60+
}
61+
62+
flagsCommand := strings.TrimPrefix(snapshot.Command, removeBinaryPlaceholder)
63+
stdout, stderr, err := testutils.ExecCmd(binaryPath, strings.Split(flagsCommand, " "), snapshotFolder)
64+
assert.Nil(t, err, stderr)
65+
66+
// In case the stdout/stderr include the dynamic folder path, we want to replace with static-content for better snapshot text compare
67+
stdout, stderr = replaceFolderName(stdout, stderr, snapshotFolder)
68+
69+
if snapshot.ExpectedStdOut != "" {
70+
assert.Equal(t, snapshot.ExpectedStdOut, stdout)
71+
}
72+
73+
if snapshot.ExpectedStdErr != "" {
74+
assert.Equal(t, snapshot.ExpectedStdErr, stderr)
75+
}
76+
77+
if len(snapshot.ExpectedSnapshot) > 0 {
78+
destSnapshotFolder := filepath.Join(tempFolder, "dest")
79+
err = snapshot.CreateSnapshotData(snapshot.ExpectedSnapshot, destSnapshotFolder)
80+
assert.Nil(t, err)
81+
82+
diffResult, err := testutils.FolderDiff(differ, destSnapshotFolder, snapshotFolder, []string{snapshot.ConfigFileName})
83+
if err != nil {
84+
t.Fatalf("snapshot folder is not equal. results: %v", diffResult)
85+
}
86+
assert.Nil(t, err)
87+
}
88+
})
89+
}
90+
91+
// loop on register suites (from *.go files)
92+
for name, suite := range register.GetSuites() {
93+
t.Run(name, func(t *testing.T) {
94+
95+
// creates temp dir for test path.
96+
tempFolder, err := os.MkdirTemp(t.TempDir(), strings.ReplaceAll(name, " ", ""))
97+
assert.Nil(t, err, "could not create temp folder")
98+
99+
defer os.RemoveAll(tempFolder)
100+
101+
// initialized test case
102+
testInstance := suite(tempFolder)
103+
104+
err = testInstance.SetupTest()
105+
assert.Nil(t, err)
106+
107+
// get Teller flags command
108+
flags := testInstance.GetFlags()
109+
110+
stdout, stderr, err := testutils.ExecCmd(binaryPath, flags, tempFolder)
111+
assert.Nil(t, err)
112+
113+
stdout, stderr = replaceFolderName(stdout, stderr, tempFolder)
114+
err = testInstance.Check(stdout, stderr)
115+
assert.Nil(t, err)
116+
})
117+
}
118+
}
119+
120+
func replaceFolderName(stdout, stderr, workingDirectory string) (string, string) {
121+
stdout = strings.ReplaceAll(stdout, workingDirectory, replaceToStaticPath)
122+
stderr = strings.ReplaceAll(stderr, workingDirectory, replaceToStaticPath)
123+
shortFolderPath := workingDirectory[0:13]
124+
stdout = strings.ReplaceAll(stdout, shortFolderPath, replaceToShortStaticPath)
125+
stderr = strings.ReplaceAll(stderr, shortFolderPath, replaceToShortStaticPath)
126+
127+
return stdout, stderr
128+
}
129+
130+
func getBinaryPath() (string, error) {
131+
binaryPath, isExists := os.LookupEnv("BINARY_PATH")
132+
if !isExists {
133+
return "", errors.New("missing `BINARY_PATH`")
134+
}
135+
136+
info, err := os.Stat(binaryPath)
137+
errors.Is(err, os.ErrNotExist)
138+
139+
if err != nil || info.IsDir() {
140+
return "", fmt.Errorf("%s not found", binaryPath)
141+
}
142+
return binaryPath, nil
143+
}

e2e/register/interface.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package register
2+
3+
type NewSuite func(tempFolderPath string) TestCaseDescriber
4+
5+
type TestCaseDescriber interface {
6+
SetupTest() error
7+
GetFlags() []string
8+
Check(stdOut, stderr string) error
9+
}

e2e/register/register.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package register
2+
3+
var suite = map[string]NewSuite{}
4+
5+
func AddSuite(name string, test NewSuite) {
6+
suite[name] = test
7+
}
8+
9+
func GetSuites() map[string]NewSuite {
10+
return suite
11+
}

e2e/tests/custom-config-file.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Custom config name
3+
command: <binary-path> show -c .custom-teller.yml
4+
config_file_name: .custom-teller.yml
5+
config_content: >
6+
project: test
7+
8+
providers:
9+
filesystem:
10+
env:
11+
FOO:
12+
path: {{.Folder}}/settings/test/foo
13+
init_snapshot:
14+
- path: settings/test
15+
file_name: foo
16+
content: shazam
17+
expected_snapshot:
18+
expected_stdout: |
19+
-*- teller: loaded variables for test using .custom-teller.yml -*-
20+
21+
[filesystem DYNAMIC-SHORT-PATH...tings/test/foo] FOO = sh*****
22+
23+
expected_stderr:

e2e/tests/delete.yml

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
name: Delete multiple entires
3+
command: <binary-path> delete FOO BAR --providers filesystem
4+
config_file_name: .teller.yml
5+
config_content: >
6+
project: test
7+
8+
providers:
9+
filesystem:
10+
env_sync:
11+
path: {{.Folder}}/settings/test/all
12+
env:
13+
FOO:
14+
path: {{.Folder}}/settings/test/foo
15+
BAR:
16+
path: {{.Folder}}/settings/test/bar
17+
init_snapshot:
18+
- path: settings/test
19+
file_name: foo
20+
content: shazam
21+
- path: settings/test
22+
file_name: bar
23+
content: shazam
24+
- path: settings/test/all
25+
file_name: secret-a
26+
content: mailman
27+
- path: settings/test/all
28+
file_name: secret-b
29+
content: shazam
30+
- path: settings/test/all
31+
file_name: secret-c
32+
content: shazam-1
33+
expected_snapshot:
34+
- path: settings/test/all
35+
file_name: secret-a
36+
content: mailman
37+
- path: settings/test/all
38+
file_name: secret-b
39+
content: shazam
40+
- path: settings/test/all
41+
file_name: secret-c
42+
content: shazam-1
43+
44+
expected_stdout: |
45+
Delete FOO (DYNAMIC-FULL-PATH/settings/test/foo) in filesystem: OK.
46+
Delete BAR (DYNAMIC-FULL-PATH/settings/test/bar) in filesystem: OK.
47+
48+
expected_stderr:

e2e/tests/json.yml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
name: Show JSON entries
3+
command: <binary-path> json
4+
config_file_name: .teller.yml
5+
config_content: >
6+
project: test
7+
8+
providers:
9+
filesystem:
10+
env_sync:
11+
path: {{.Folder}}/settings/test/all
12+
env:
13+
FOO:
14+
path: {{.Folder}}/settings/test/foo
15+
BAR:
16+
path: {{.Folder}}/settings/test/bar
17+
init_snapshot:
18+
- path: settings/test
19+
file_name: foo
20+
content: shazam
21+
- path: settings/test
22+
file_name: bar
23+
content: shazam
24+
- path: settings/test/all
25+
file_name: secret-a
26+
content: mailman
27+
- path: settings/test/all
28+
file_name: secret-b
29+
content: shazam
30+
- path: settings/test/all
31+
file_name: secret-c
32+
content: shazam-1
33+
expected_snapshot:
34+
expected_stdout: >-
35+
{
36+
"BAR": "shazam",
37+
"FOO": "shazam",
38+
"secret-a": "mailman",
39+
"secret-b": "shazam",
40+
"secret-c": "shazam-1"
41+
}
42+
expected_stderr:

0 commit comments

Comments
 (0)