Skip to content

Commit ba85538

Browse files
authored
Add unit tests for the chart templates (#11)
1 parent d28cb66 commit ba85538

File tree

7 files changed

+310
-0
lines changed

7 files changed

+310
-0
lines changed

.github/workflows/ci.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,33 @@ jobs:
106106
files: "manifests"
107107
version: ${{ matrix.k8s }}
108108

109+
template-test:
110+
name: template-test
111+
runs-on: ubuntu-latest
112+
steps:
113+
- name: Checkout
114+
uses: actions/checkout@v3
115+
with:
116+
fetch-depth: 0
117+
118+
- name: Set up Helm
119+
uses: azure/setup-helm@v3
120+
with:
121+
version: v3.6.3
122+
123+
- name: Set up Go
124+
uses: actions/setup-go@v5
125+
with:
126+
go-version: stable
127+
128+
- name: golangci-lint
129+
uses: golangci/golangci-lint-action@v6
130+
with:
131+
version: v1.61
132+
133+
- name: Run go test
134+
run: go test -v ./...
135+
109136
install-chart:
110137
name: install-chart
111138
runs-on: ubuntu-latest

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module github.com/surrealdb/helm-charts
2+
3+
go 1.23.2
4+
5+
require (
6+
github.com/davecgh/go-spew v1.1.1 // indirect
7+
github.com/pmezard/go-difflib v1.0.0 // indirect
8+
github.com/stretchr/testify v1.9.0 // indirect
9+
gopkg.in/yaml.v3 v3.0.1 // indirect
10+
)

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
6+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
9+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

tests/helpers.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package tests
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"gopkg.in/yaml.v3"
12+
)
13+
14+
// testTemplate is a helper function that tests a "surrealdb" chart template.
15+
//
16+
// It runs helm-template to render the template at the specified path within the surrealdb chart,
17+
// with the specified values, and compares the output to the expected output corresponds to the specified subject.
18+
//
19+
// We do snapshot testing here, so the caller does not need to manually populate the expected template output
20+
// corresponds to the subject and the values.
21+
//
22+
// To let the test record the snapshot, set the environment variable `UPDATE_SNAPSHOT={path}/{subject}` before running the test.
23+
//
24+
// For example, to update the snapshot for the "default" subject of the "deployment.yaml" template:
25+
// ```
26+
// UPDATE_SNAPSHOT=deployment.yaml/default go test -v ./tests
27+
// ```
28+
//
29+
// To update the snapshot for all subjects of the "deployment.yaml" template:
30+
// ```
31+
// UPDATE_SNAPSHOT="deployment.yaml/*" go test -v ./tests
32+
// ```
33+
//
34+
// To update the snapshot for all templates:
35+
// ```
36+
// UPDATE_SNAPSHOT="*" go test -v ./tests
37+
// ```
38+
func testTemplate(t *testing.T, path string, subject string, values map[string]interface{}) {
39+
t.Helper()
40+
41+
name := fmt.Sprintf("%s/%s", path, subject)
42+
43+
t.Run(name, func(t *testing.T) {
44+
actual := renderTemplate(t, filepath.Join("templates", path), values)
45+
46+
if shouldUpdateSnapshot(os.Getenv("UPDATE_SNAPSHOT"), path, subject) {
47+
writeSnapshot(t, name, actual)
48+
t.Skip("Updated snapshot")
49+
}
50+
51+
expected := readSnapshot(t, name)
52+
53+
assert.Equal(t, expected, actual)
54+
})
55+
}
56+
57+
func renderTemplate(t *testing.T, path string, values map[string]interface{}) string {
58+
t.Helper()
59+
60+
valuesFile := filepath.Join(t.TempDir(), "values.yaml")
61+
if err := writeValuesFile(valuesFile, values); err != nil {
62+
t.Fatalf("failed to write values file: %v", err)
63+
}
64+
65+
helmCmd := exec.Command(
66+
"helm", "template",
67+
// Release name
68+
"testrelease",
69+
// Chart path
70+
"./charts/surrealdb",
71+
"--values", valuesFile,
72+
"--show-only", path,
73+
)
74+
// We assume the test is running from the tests directory,
75+
// which is one-level deep from the root of the project.
76+
helmCmd.Dir = ".."
77+
78+
out, err := helmCmd.CombinedOutput()
79+
if err != nil {
80+
t.Fatalf("failed to render template: %v\n%s", err, out)
81+
}
82+
83+
return string(out)
84+
}
85+
86+
func writeValuesFile(path string, values map[string]interface{}) error {
87+
d, err := yaml.Marshal(values)
88+
if err != nil {
89+
return err
90+
}
91+
92+
if err := os.WriteFile(path, d, 0644); err != nil {
93+
return err
94+
}
95+
96+
return nil
97+
}
98+
99+
func shouldUpdateSnapshot(env, path, subject string) bool {
100+
switch env {
101+
case "*", fmt.Sprintf("%s/*", path), fmt.Sprintf("%s/%s", path, subject):
102+
return true
103+
}
104+
105+
return false
106+
}
107+
108+
func readSnapshot(t *testing.T, name string) string {
109+
t.Helper()
110+
111+
f := getSnapshotPath(name)
112+
d, err := os.ReadFile(f)
113+
if err != nil {
114+
t.Logf("Create snapshot file at %s", f)
115+
t.Fatalf("failed to read snapshot: %v", err)
116+
}
117+
118+
return string(d)
119+
}
120+
121+
func writeSnapshot(t *testing.T, name string, actual string) {
122+
t.Helper()
123+
124+
f := getSnapshotPath(name)
125+
126+
if err := os.MkdirAll(filepath.Dir(f), 0755); err != nil {
127+
t.Fatalf("failed to create snapshot directory: %v", err)
128+
}
129+
130+
if err := os.WriteFile(f, []byte(actual), 0644); err != nil {
131+
t.Fatalf("failed to write snapshot: %v", err)
132+
}
133+
134+
t.Logf("Snapshot updated at %s", f)
135+
}
136+
137+
func getSnapshotPath(name string) string {
138+
return filepath.Join("testdata", "snapshots", name)
139+
}

tests/helpers_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package tests
2+
3+
import "testing"
4+
5+
func TestShouldUpdateSnapshot(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
env string
9+
path string
10+
subject string
11+
expected bool
12+
}{
13+
{
14+
name: "empty env",
15+
env: "",
16+
path: "deployment.yaml",
17+
subject: "default",
18+
expected: false,
19+
},
20+
{
21+
name: "wildcard env",
22+
env: "*",
23+
path: "deployment.yaml",
24+
subject: "default",
25+
expected: true,
26+
},
27+
{
28+
name: "path env",
29+
env: "deployment.yaml/*",
30+
path: "deployment.yaml",
31+
subject: "default",
32+
expected: true,
33+
},
34+
{
35+
name: "path/subject env",
36+
env: "deployment.yaml/default",
37+
path: "deployment.yaml",
38+
subject: "default",
39+
expected: true,
40+
},
41+
{
42+
name: "path/different subject env",
43+
env: "deployment.yaml/other",
44+
path: "deployment.yaml",
45+
subject: "default",
46+
expected: false,
47+
},
48+
}
49+
50+
for _, tt := range tests {
51+
t.Run(tt.name, func(t *testing.T) {
52+
actual := shouldUpdateSnapshot(tt.env, tt.path, tt.subject)
53+
if actual != tt.expected {
54+
t.Errorf("expected %v, got %v", tt.expected, actual)
55+
}
56+
})
57+
}
58+
}

tests/surrealdb_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package tests
2+
3+
import "testing"
4+
5+
func TestDeployment(t *testing.T) {
6+
testTemplate(t, "deployment.yaml", "default", map[string]interface{}{})
7+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
# Source: surrealdb/templates/deployment.yaml
3+
apiVersion: apps/v1
4+
kind: Deployment
5+
metadata:
6+
name: testrelease-surrealdb
7+
labels:
8+
helm.sh/chart: surrealdb-0.3.4
9+
app.kubernetes.io/name: surrealdb
10+
app.kubernetes.io/instance: testrelease
11+
app.kubernetes.io/version: "1.0.0"
12+
app.kubernetes.io/managed-by: Helm
13+
spec:
14+
replicas: 1
15+
selector:
16+
matchLabels:
17+
app.kubernetes.io/name: surrealdb
18+
app.kubernetes.io/instance: testrelease
19+
template:
20+
metadata:
21+
labels:
22+
app.kubernetes.io/name: surrealdb
23+
app.kubernetes.io/instance: testrelease
24+
spec:
25+
serviceAccountName: testrelease-surrealdb
26+
securityContext:
27+
{}
28+
containers:
29+
- name: surrealdb
30+
securityContext:
31+
{}
32+
image: "surrealdb/surrealdb:1.0.0"
33+
imagePullPolicy: IfNotPresent
34+
args:
35+
- start
36+
env:
37+
- name: SURREAL_NO_BANNER
38+
value: "true"
39+
- name: SURREAL_PATH
40+
value: memory
41+
- name: SURREAL_LOG
42+
value: info
43+
- name: SURREAL_BIND
44+
value: 0.0.0.0:8000
45+
- name: SURREAL_AUTH
46+
value: "true"
47+
ports:
48+
- name: http
49+
containerPort: 8000
50+
protocol: TCP
51+
livenessProbe:
52+
httpGet:
53+
path: /health
54+
port: http
55+
readinessProbe:
56+
httpGet:
57+
path: /health
58+
port: http
59+
resources:
60+
{}

0 commit comments

Comments
 (0)