Skip to content

Commit

Permalink
Add unit tests for the chart templates (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
mumoshu authored Oct 28, 2024
1 parent d28cb66 commit ba85538
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,33 @@ jobs:
files: "manifests"
version: ${{ matrix.k8s }}

template-test:
name: template-test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: v3.6.3

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.61

- name: Run go test
run: go test -v ./...

install-chart:
name: install-chart
runs-on: ubuntu-latest
Expand Down
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/surrealdb/helm-charts

go 1.23.2

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
139 changes: 139 additions & 0 deletions tests/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package tests

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

// testTemplate is a helper function that tests a "surrealdb" chart template.
//
// It runs helm-template to render the template at the specified path within the surrealdb chart,
// with the specified values, and compares the output to the expected output corresponds to the specified subject.
//
// We do snapshot testing here, so the caller does not need to manually populate the expected template output
// corresponds to the subject and the values.
//
// To let the test record the snapshot, set the environment variable `UPDATE_SNAPSHOT={path}/{subject}` before running the test.
//
// For example, to update the snapshot for the "default" subject of the "deployment.yaml" template:
// ```
// UPDATE_SNAPSHOT=deployment.yaml/default go test -v ./tests
// ```
//
// To update the snapshot for all subjects of the "deployment.yaml" template:
// ```
// UPDATE_SNAPSHOT="deployment.yaml/*" go test -v ./tests
// ```
//
// To update the snapshot for all templates:
// ```
// UPDATE_SNAPSHOT="*" go test -v ./tests
// ```
func testTemplate(t *testing.T, path string, subject string, values map[string]interface{}) {
t.Helper()

name := fmt.Sprintf("%s/%s", path, subject)

t.Run(name, func(t *testing.T) {
actual := renderTemplate(t, filepath.Join("templates", path), values)

if shouldUpdateSnapshot(os.Getenv("UPDATE_SNAPSHOT"), path, subject) {
writeSnapshot(t, name, actual)
t.Skip("Updated snapshot")
}

expected := readSnapshot(t, name)

assert.Equal(t, expected, actual)
})
}

func renderTemplate(t *testing.T, path string, values map[string]interface{}) string {
t.Helper()

valuesFile := filepath.Join(t.TempDir(), "values.yaml")
if err := writeValuesFile(valuesFile, values); err != nil {
t.Fatalf("failed to write values file: %v", err)
}

helmCmd := exec.Command(
"helm", "template",
// Release name
"testrelease",
// Chart path
"./charts/surrealdb",
"--values", valuesFile,
"--show-only", path,
)
// We assume the test is running from the tests directory,
// which is one-level deep from the root of the project.
helmCmd.Dir = ".."

out, err := helmCmd.CombinedOutput()
if err != nil {
t.Fatalf("failed to render template: %v\n%s", err, out)
}

return string(out)
}

func writeValuesFile(path string, values map[string]interface{}) error {
d, err := yaml.Marshal(values)
if err != nil {
return err
}

if err := os.WriteFile(path, d, 0644); err != nil {
return err
}

return nil
}

func shouldUpdateSnapshot(env, path, subject string) bool {
switch env {
case "*", fmt.Sprintf("%s/*", path), fmt.Sprintf("%s/%s", path, subject):
return true
}

return false
}

func readSnapshot(t *testing.T, name string) string {
t.Helper()

f := getSnapshotPath(name)
d, err := os.ReadFile(f)
if err != nil {
t.Logf("Create snapshot file at %s", f)
t.Fatalf("failed to read snapshot: %v", err)
}

return string(d)
}

func writeSnapshot(t *testing.T, name string, actual string) {
t.Helper()

f := getSnapshotPath(name)

if err := os.MkdirAll(filepath.Dir(f), 0755); err != nil {
t.Fatalf("failed to create snapshot directory: %v", err)
}

if err := os.WriteFile(f, []byte(actual), 0644); err != nil {
t.Fatalf("failed to write snapshot: %v", err)
}

t.Logf("Snapshot updated at %s", f)
}

func getSnapshotPath(name string) string {
return filepath.Join("testdata", "snapshots", name)
}
58 changes: 58 additions & 0 deletions tests/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package tests

import "testing"

func TestShouldUpdateSnapshot(t *testing.T) {
tests := []struct {
name string
env string
path string
subject string
expected bool
}{
{
name: "empty env",
env: "",
path: "deployment.yaml",
subject: "default",
expected: false,
},
{
name: "wildcard env",
env: "*",
path: "deployment.yaml",
subject: "default",
expected: true,
},
{
name: "path env",
env: "deployment.yaml/*",
path: "deployment.yaml",
subject: "default",
expected: true,
},
{
name: "path/subject env",
env: "deployment.yaml/default",
path: "deployment.yaml",
subject: "default",
expected: true,
},
{
name: "path/different subject env",
env: "deployment.yaml/other",
path: "deployment.yaml",
subject: "default",
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := shouldUpdateSnapshot(tt.env, tt.path, tt.subject)
if actual != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, actual)
}
})
}
}
7 changes: 7 additions & 0 deletions tests/surrealdb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tests

import "testing"

func TestDeployment(t *testing.T) {
testTemplate(t, "deployment.yaml", "default", map[string]interface{}{})
}
60 changes: 60 additions & 0 deletions tests/testdata/snapshots/deployment.yaml/default
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
# Source: surrealdb/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: testrelease-surrealdb
labels:
helm.sh/chart: surrealdb-0.3.4
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
app.kubernetes.io/version: "1.0.0"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
template:
metadata:
labels:
app.kubernetes.io/name: surrealdb
app.kubernetes.io/instance: testrelease
spec:
serviceAccountName: testrelease-surrealdb
securityContext:
{}
containers:
- name: surrealdb
securityContext:
{}
image: "surrealdb/surrealdb:1.0.0"
imagePullPolicy: IfNotPresent
args:
- start
env:
- name: SURREAL_NO_BANNER
value: "true"
- name: SURREAL_PATH
value: memory
- name: SURREAL_LOG
value: info
- name: SURREAL_BIND
value: 0.0.0.0:8000
- name: SURREAL_AUTH
value: "true"
ports:
- name: http
containerPort: 8000
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: http
readinessProbe:
httpGet:
path: /health
port: http
resources:
{}

0 comments on commit ba85538

Please sign in to comment.