mirror of
https://github.com/jimeh/go-golden.git
synced 2026-02-19 11:16:47 +00:00
Compare commits
7 Commits
v0.2.1
...
improve-te
| Author | SHA1 | Date | |
|---|---|---|---|
|
826a0dcf84
|
|||
|
9d8db8e5f7
|
|||
|
f42dbf9010
|
|||
|
e0a9c7bd77
|
|||
|
dfcdfa3416
|
|||
|
0596869e3e
|
|||
|
138981b7d4
|
58
.github/workflows/ci.yml
vendored
58
.github/workflows/ci.yml
vendored
@@ -7,11 +7,15 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
version: v1.42
|
||||
go-version: "1.18"
|
||||
cache: false
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
version: v1.56
|
||||
env:
|
||||
VERBOSE: "true"
|
||||
|
||||
@@ -19,16 +23,10 @@ jobs:
|
||||
name: Tidy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.15
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
go-version: "1.18"
|
||||
- name: Check if mods are tidy
|
||||
run: make check-tidy
|
||||
|
||||
@@ -36,18 +34,12 @@ jobs:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.15
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
go-version: "1.18"
|
||||
- name: Publish coverage
|
||||
uses: paambaati/codeclimate-action@v2.7.4
|
||||
uses: paambaati/codeclimate-action@v5.0.0
|
||||
env:
|
||||
VERBOSE: "true"
|
||||
GOMAXPROCS: 4
|
||||
@@ -68,20 +60,16 @@ jobs:
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
go_version:
|
||||
- "1.15"
|
||||
- "1.16"
|
||||
- "1.17"
|
||||
- "1.18"
|
||||
- "1.19"
|
||||
- "1.20"
|
||||
- "1.21"
|
||||
- "1.22"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
go-version: "1.22"
|
||||
- name: Run tests
|
||||
run: go test -v -count=1 -race ./...
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
bin/*
|
||||
coverage.out
|
||||
|
||||
testdata/*
|
||||
!testdata/TestExample*
|
||||
testdata/TestFile*
|
||||
testdata/TestGet*
|
||||
testdata/TestSet*
|
||||
|
||||
@@ -25,7 +25,6 @@ linters:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errorlint
|
||||
@@ -69,9 +68,6 @@ linters:
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude:
|
||||
- Using the variable on range scope `tt` in function literal
|
||||
- Using the variable on range scope `tc` in function literal
|
||||
exclude-rules:
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
|
||||
18
Makefile
18
Makefile
@@ -34,23 +34,19 @@ SHELL := env \
|
||||
# Tools
|
||||
#
|
||||
|
||||
TOOLS += $(TOOLDIR)/gobin
|
||||
$(TOOLDIR)/gobin:
|
||||
GO111MODULE=off go get -u github.com/myitcv/gobin
|
||||
|
||||
# external tool
|
||||
define tool # 1: binary-name, 2: go-import-path
|
||||
TOOLS += $(TOOLDIR)/$(1)
|
||||
|
||||
$(TOOLDIR)/$(1): $(TOOLDIR)/gobin Makefile
|
||||
gobin $(V) "$(2)"
|
||||
$(TOOLDIR)/$(1): Makefile
|
||||
GOBIN="$(CURDIR)/$(TOOLDIR)" go install "$(2)"
|
||||
endef
|
||||
|
||||
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc))
|
||||
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt))
|
||||
$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports))
|
||||
$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42))
|
||||
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod))
|
||||
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc@latest))
|
||||
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt@latest))
|
||||
$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports@latest))
|
||||
$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56))
|
||||
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod@latest))
|
||||
|
||||
.PHONY: tools
|
||||
tools: $(TOOLS)
|
||||
|
||||
@@ -70,4 +70,4 @@ for documentation and examples.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/jimeh/go-golden/blob/master/LICENSE)
|
||||
[MIT](https://github.com/jimeh/go-golden/blob/main/LICENSE)
|
||||
|
||||
46
fs.go
Normal file
46
fs.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package golden
|
||||
|
||||
import "os"
|
||||
|
||||
type FS interface {
|
||||
// MkdirAll creates a directory named path, along with any necessary
|
||||
// parents, and returns nil, or else returns an error. The permission bits
|
||||
// perm (before umask) are used for all directories that MkdirAll creates.
|
||||
MkdirAll(path string, perm os.FileMode) error
|
||||
|
||||
// ReadFile reads the named file and returns the contents. A successful call
|
||||
// returns err == nil, not err == EOF. Because ReadFile reads the whole
|
||||
// file, it does not treat an EOF from Read as an error to be reported.
|
||||
ReadFile(filename string) ([]byte, error)
|
||||
|
||||
// WriteFile writes data to a file named by filename. If the file does not
|
||||
// exist, WriteFile creates it with permissions perm; otherwise WriteFile
|
||||
// truncates it before writing, without changing permissions.
|
||||
WriteFile(name string, data []byte, perm os.FileMode) error
|
||||
}
|
||||
|
||||
type fsImpl struct{}
|
||||
|
||||
var _ FS = fsImpl{}
|
||||
|
||||
// NewFS returns a new FS instance which operates against the host file system
|
||||
// via calls to functions in the os package.
|
||||
func NewFS() FS {
|
||||
return fsImpl{}
|
||||
}
|
||||
|
||||
// DefaultFS is the default FS instance used by all top-level package functions,
|
||||
// including the Default Golden instance, and also the New function.
|
||||
var DefaultFS = NewFS()
|
||||
|
||||
func (fsImpl) MkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
func (fsImpl) ReadFile(filename string) ([]byte, error) {
|
||||
return os.ReadFile(filename)
|
||||
}
|
||||
|
||||
func (fsImpl) WriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||
return os.WriteFile(filename, data, perm)
|
||||
}
|
||||
131
fs_test.go
Normal file
131
fs_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package golden
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMkdirAll(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
perm os.FileMode
|
||||
wantErr bool
|
||||
}{
|
||||
{"create new dir", "newdir", 0o755, false},
|
||||
{"create nested dirs", "nested/dir/structure", 0o755, false},
|
||||
{"invalid path", string([]byte{0, 0}), 0o755, true},
|
||||
}
|
||||
|
||||
fs := NewFS()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
path := filepath.Join(tempDir, tt.path)
|
||||
err := fs.MkdirAll(path, tt.perm)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
_, err := os.Stat(path)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
sampleFilePath := filepath.Join(tempDir, "sample.txt")
|
||||
sampleContent := []byte("Hello, world!")
|
||||
err := os.WriteFile(sampleFilePath, sampleContent, 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{"read existing file", sampleFilePath, sampleContent, false},
|
||||
{"file does not exist", "nonexistent.txt", nil, true},
|
||||
}
|
||||
|
||||
fs := NewFS()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := fs.ReadFile(tt.filename)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, string(tt.want), string(got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"write to new file",
|
||||
"newfile.txt",
|
||||
[]byte("new content"),
|
||||
0o644,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"overwrite existing file",
|
||||
"existing.txt",
|
||||
[]byte("overwritten content"),
|
||||
0o644,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid filename",
|
||||
string([]byte{0, 0}),
|
||||
[]byte("invalid filename"),
|
||||
0o644,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"non-existent directory",
|
||||
"nonexistentdir/newfile.txt",
|
||||
[]byte("this will fail"),
|
||||
0o644,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
fs := NewFS()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
filePath := filepath.Join(tempDir, tt.filename)
|
||||
err := fs.WriteFile(filePath, tt.data, tt.perm)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
content, err := os.ReadFile(filePath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.data, content)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
10
go.mod
10
go.mod
@@ -1,8 +1,14 @@
|
||||
module github.com/jimeh/go-golden
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/jimeh/envctl v0.1.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
10
go.sum
10
go.sum
@@ -1,14 +1,16 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/jimeh/envctl v0.1.0 h1:KTv3D+pi5M4/PgFVE/W8ssWqiZP3pDJ8Cga50L+1avo=
|
||||
github.com/jimeh/envctl v0.1.0/go.mod h1:aM27ffBbO1yUBKUzgJGCUorS4z+wyh+qhQe1ruxXZZo=
|
||||
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
173
gold.go
Normal file
173
gold.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package golden
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jimeh/go-golden/sanitize"
|
||||
)
|
||||
|
||||
// gold is the underlying struct that implements the Golden interface.
|
||||
type gold struct {
|
||||
// dirMode determines the file system permissions of any folders created to
|
||||
// hold golden files.
|
||||
dirMode os.FileMode
|
||||
|
||||
// fileMode determines the file system permissions of any created or updated
|
||||
// golden files written to disk.
|
||||
fileMode os.FileMode
|
||||
|
||||
// suffix determines the filename suffix for all golden files. Typically
|
||||
// this should be ".golden", but can be changed here if needed.
|
||||
suffix string
|
||||
|
||||
// dirname is the name of the top-level directory at the root of the package
|
||||
// which holds all golden files. Typically this should be "testdata", but
|
||||
// can be changed here if needed.
|
||||
dirname string
|
||||
|
||||
// updateFunc is used to determine if golden files should be updated or
|
||||
// not. Its boolean return value is returned by Update().
|
||||
updateFunc UpdateFunc
|
||||
|
||||
// fs is used for all file system operations. This enables providing custom
|
||||
// afero.fs instances which can be useful for testing purposes.
|
||||
fs FS
|
||||
|
||||
// logOnWrite determines if a message is logged with t.Logf when a golden
|
||||
// file is written to with either of the set methods.
|
||||
logOnWrite bool
|
||||
}
|
||||
|
||||
// Ensure golden satisfies Golden interface.
|
||||
var _ Golden = &gold{}
|
||||
|
||||
func (g *gold) Do(t TestingT, data []byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
if g.Update() {
|
||||
g.Set(t, data)
|
||||
}
|
||||
|
||||
return g.Get(t)
|
||||
}
|
||||
|
||||
func (g *gold) DoP(t TestingT, name string, data []byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
if g.Update() {
|
||||
g.SetP(t, name, data)
|
||||
}
|
||||
|
||||
return g.GetP(t, name)
|
||||
}
|
||||
|
||||
func (g *gold) File(t TestingT) string {
|
||||
t.Helper()
|
||||
|
||||
return g.file(t, "")
|
||||
}
|
||||
|
||||
func (g *gold) FileP(t TestingT, name string) string {
|
||||
t.Helper()
|
||||
|
||||
if name == "" {
|
||||
t.Fatalf("golden: name cannot be empty")
|
||||
}
|
||||
|
||||
return g.file(t, name)
|
||||
}
|
||||
|
||||
func (g *gold) file(t TestingT, name string) string {
|
||||
t.Helper()
|
||||
|
||||
if t.Name() == "" {
|
||||
t.Fatalf("golden: could not determine filename for TestingT instance")
|
||||
}
|
||||
|
||||
base := []string{g.dirname, filepath.FromSlash(t.Name())}
|
||||
if name != "" {
|
||||
base = append(base, name)
|
||||
}
|
||||
|
||||
f := filepath.Clean(filepath.Join(base...) + g.suffix)
|
||||
|
||||
dirty := strings.Split(f, string(os.PathSeparator))
|
||||
clean := make([]string, 0, len(dirty))
|
||||
for _, s := range dirty {
|
||||
clean = append(clean, sanitize.Filename(s))
|
||||
}
|
||||
|
||||
return strings.Join(clean, string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func (g *gold) Get(t TestingT) []byte {
|
||||
t.Helper()
|
||||
|
||||
return g.get(t, "")
|
||||
}
|
||||
|
||||
func (g *gold) GetP(t TestingT, name string) []byte {
|
||||
t.Helper()
|
||||
|
||||
if name == "" {
|
||||
t.Fatalf("golden: name cannot be empty")
|
||||
}
|
||||
|
||||
return g.get(t, name)
|
||||
}
|
||||
|
||||
func (g *gold) get(t TestingT, name string) []byte {
|
||||
t.Helper()
|
||||
|
||||
f := g.file(t, name)
|
||||
|
||||
b, err := g.fs.ReadFile(f)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: %s", err.Error())
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (g *gold) Set(t TestingT, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
g.set(t, "", data)
|
||||
}
|
||||
|
||||
func (g *gold) SetP(t TestingT, name string, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
if name == "" {
|
||||
t.Fatalf("golden: name cannot be empty")
|
||||
}
|
||||
|
||||
g.set(t, name, data)
|
||||
}
|
||||
|
||||
func (g *gold) set(t TestingT, name string, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
f := g.file(t, name)
|
||||
dir := filepath.Dir(f)
|
||||
|
||||
if g.logOnWrite {
|
||||
t.Logf("golden: writing golden file: %s", f)
|
||||
}
|
||||
|
||||
err := g.fs.MkdirAll(dir, g.dirMode)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: failed to create directory: %s", err.Error())
|
||||
}
|
||||
|
||||
err = g.fs.WriteFile(f, data, g.fileMode)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: filed to write file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gold) Update() bool {
|
||||
return g.updateFunc()
|
||||
}
|
||||
528
gold_test.go
Normal file
528
gold_test.go
Normal file
@@ -0,0 +1,528 @@
|
||||
package golden
|
||||
|
||||
// func Test_gold_File(t *testing.T) {
|
||||
// type fields struct {
|
||||
// suffix *string
|
||||
// dirname *string
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// testName string
|
||||
// fields fields
|
||||
// want string
|
||||
// wantFatals []string
|
||||
// }{
|
||||
// {
|
||||
// name: "top-level",
|
||||
// testName: "TestFooBar",
|
||||
// want: filepath.Join("testdata", "TestFooBar.golden"),
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test",
|
||||
// testName: "TestFooBar/it_is_here",
|
||||
// want: filepath.Join(
|
||||
// "testdata", "TestFooBar", "it_is_here.golden",
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// name: "blank test name",
|
||||
// testName: "",
|
||||
// wantFatals: []string{
|
||||
// "golden: could not determine filename for TestingT instance",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname",
|
||||
// testName: "TestFozBar",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// },
|
||||
// want: filepath.Join("goldenfiles", "TestFozBar.golden"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom suffix",
|
||||
// testName: "TestFozBaz",
|
||||
// fields: fields{
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// want: filepath.Join("testdata", "TestFozBaz.goldfile"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname and suffix",
|
||||
// testName: "TestFozBar",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// want: filepath.Join("goldenfiles", "TestFozBar.goldfile"),
|
||||
// },
|
||||
// {
|
||||
// name: "invalid chars in test name",
|
||||
// testName: `TestFooBar/foo?<>:*|"bar`,
|
||||
// want: filepath.Join(
|
||||
// "testdata", "TestFooBar", "foo_______bar.golden",
|
||||
// ),
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// if tt.fields.suffix == nil {
|
||||
// tt.fields.suffix = stringPtr(".golden")
|
||||
// }
|
||||
// if tt.fields.dirname == nil {
|
||||
// tt.fields.dirname = stringPtr("testdata")
|
||||
// }
|
||||
|
||||
// g := &gold{
|
||||
// suffix: *tt.fields.suffix,
|
||||
// dirname: *tt.fields.dirname,
|
||||
// }
|
||||
|
||||
// ft := &fakeTestingT{name: tt.testName}
|
||||
|
||||
// var got string
|
||||
// testInGoroutine(t, func() {
|
||||
// got = g.File(ft)
|
||||
// })
|
||||
|
||||
// assert.Equal(t, tt.want, got)
|
||||
// assert.Equal(t, tt.wantFatals, ft.fatals)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// func Test_gold_FileP(t *testing.T) {
|
||||
// type fields struct {
|
||||
// suffix *string
|
||||
// dirname *string
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// testName string
|
||||
// goldenName string
|
||||
// fields fields
|
||||
// want string
|
||||
// wantFatals []string
|
||||
// }{
|
||||
// {
|
||||
// name: "top-level",
|
||||
// testName: "TestFooBar",
|
||||
// goldenName: "yaml",
|
||||
// want: filepath.Join("testdata", "TestFooBar", "yaml.golden"),
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test",
|
||||
// testName: "TestFooBar/it_is_here",
|
||||
// goldenName: "json",
|
||||
// want: filepath.Join(
|
||||
// "testdata", "TestFooBar", "it_is_here", "json.golden",
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// name: "blank test name",
|
||||
// testName: "",
|
||||
// goldenName: "json",
|
||||
// wantFatals: []string{
|
||||
// "golden: could not determine filename for TestintT instance",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname",
|
||||
// testName: "TestFozBar",
|
||||
// goldenName: "xml",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// },
|
||||
// want: filepath.Join("goldenfiles", "TestFozBar", "xml.golden"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom suffix",
|
||||
// testName: "TestFozBaz",
|
||||
// goldenName: "toml",
|
||||
// fields: fields{
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// want: filepath.Join("testdata", "TestFozBaz", "toml.goldfile"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname and suffix",
|
||||
// testName: "TestFozBar",
|
||||
// goldenName: "json",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// want: filepath.Join("goldenfiles", "TestFozBar", "json.goldfile"),
|
||||
// },
|
||||
// {
|
||||
// name: "invalid chars in test name",
|
||||
// testName: `TestFooBar/foo?<>:*|"bar`,
|
||||
// goldenName: "yml",
|
||||
// want: filepath.Join(
|
||||
// "testdata", "TestFooBar", "foo_______bar", "yml.golden",
|
||||
// ),
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// if tt.fields.suffix == nil {
|
||||
// tt.fields.suffix = stringPtr(".golden")
|
||||
// }
|
||||
// if tt.fields.dirname == nil {
|
||||
// tt.fields.dirname = stringPtr("testdata")
|
||||
// }
|
||||
|
||||
// g := &gold{
|
||||
// suffix: *tt.fields.suffix,
|
||||
// dirname: *tt.fields.dirname,
|
||||
// }
|
||||
|
||||
// ft := &fakeTestingT{name: tt.testName}
|
||||
|
||||
// var got string
|
||||
// testInGoroutine(t, func() {
|
||||
// got = g.FileP(ft, tt.goldenName)
|
||||
// })
|
||||
|
||||
// assert.Equal(t, tt.want, got)
|
||||
// assert.Equal(t, tt.wantFatals, ft.fatals)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// func Test_gold_Get(t *testing.T) {
|
||||
// type fields struct {
|
||||
// suffix *string
|
||||
// dirname *string
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// testName string
|
||||
// fields fields
|
||||
// files map[string][]byte
|
||||
// want []byte
|
||||
// wantAborted bool
|
||||
// wantFailCount int
|
||||
// wantTestOutput []string
|
||||
// }{
|
||||
// {
|
||||
// name: "file exists",
|
||||
// testName: "TestFooBar",
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join("testdata", "TestFooBar.golden"): []byte(
|
||||
// "foo: bar\nhello: world",
|
||||
// ),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "file is missing",
|
||||
// testName: "TestFooBar",
|
||||
// files: map[string][]byte{},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: open " + filepath.Join(
|
||||
// "testdata", "TestFooBar.golden",
|
||||
// ) + ": file does not exist\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test file exists",
|
||||
// testName: "TestFooBar/it_is_here",
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "testdata", "TestFooBar", "it_is_here.golden",
|
||||
// ): []byte("this is really here ^_^\n"),
|
||||
// },
|
||||
// want: []byte("this is really here ^_^\n"),
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test file is missing",
|
||||
// testName: "TestFooBar/not_really_here",
|
||||
// files: map[string][]byte{},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: open " + filepath.Join(
|
||||
// "testdata", "TestFooBar", "not_really_here.golden",
|
||||
// ) + ": file does not exist\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "blank test name",
|
||||
// testName: "",
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: could not determine filename for given " +
|
||||
// "*mocktesting.T instance\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname",
|
||||
// testName: "TestFozBar",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join("goldenfiles", "TestFozBar.golden"): []byte(
|
||||
// "foo: bar\nhello: world",
|
||||
// ),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom suffix",
|
||||
// testName: "TestFozBaz",
|
||||
// fields: fields{
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join("testdata", "TestFozBaz.goldfile"): []byte(
|
||||
// "foo: bar\nhello: world",
|
||||
// ),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname and suffix",
|
||||
// testName: "TestFozBar",
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join("goldenfiles", "TestFozBar.goldfile"): []byte(
|
||||
// "foo: bar\nhello: world",
|
||||
// ),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "invalid chars in test name",
|
||||
// testName: `TestFooBar/foo?<>:*|"bar`,
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "testdata", "TestFooBar", "foo_______bar.golden",
|
||||
// ): []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// fs := NewFS() // TODO: Replace with in-memory stub FS.
|
||||
// for f, b := range tt.files {
|
||||
// _ = fs.WriteFile(f, b, 0o644)
|
||||
// }
|
||||
|
||||
// if tt.fields.suffix == nil {
|
||||
// tt.fields.suffix = stringPtr(".golden")
|
||||
// }
|
||||
// if tt.fields.dirname == nil {
|
||||
// tt.fields.dirname = stringPtr("testdata")
|
||||
// }
|
||||
|
||||
// g := &gold{
|
||||
// suffix: *tt.fields.suffix,
|
||||
// dirname: *tt.fields.dirname,
|
||||
// fs: fs,
|
||||
// }
|
||||
|
||||
// mt := mocktesting.NewT(tt.testName)
|
||||
|
||||
// var got []byte
|
||||
// mocktesting.Go(func() {
|
||||
// got = g.Get(mt)
|
||||
// })
|
||||
|
||||
// assert.Equal(t, tt.want, got)
|
||||
// assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||
// assert.Equal(t,
|
||||
// tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||
// )
|
||||
// assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// func Test_gold_GetP(t *testing.T) {
|
||||
// type args struct {
|
||||
// name string
|
||||
// }
|
||||
// type fields struct {
|
||||
// suffix *string
|
||||
// dirname *string
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// testName string
|
||||
// args args
|
||||
// fields fields
|
||||
// files map[string][]byte
|
||||
// want []byte
|
||||
// wantAborted bool
|
||||
// wantFailCount int
|
||||
// wantTestOutput []string
|
||||
// }{
|
||||
// {
|
||||
// name: "file exists",
|
||||
// testName: "TestFooBar",
|
||||
// args: args{name: "yaml"},
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join("testdata", "TestFooBar", "yaml.golden"): []byte(
|
||||
// "foo: bar\nhello: world",
|
||||
// ),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "file is missing",
|
||||
// testName: "TestFooBar",
|
||||
// args: args{name: "yaml"},
|
||||
// files: map[string][]byte{},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: open " + filepath.Join(
|
||||
// "testdata", "TestFooBar", "yaml.golden",
|
||||
// ) + ": file does not exist\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test file exists",
|
||||
// testName: "TestFooBar/it_is_here",
|
||||
// args: args{name: "plain"},
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "testdata", "TestFooBar", "it_is_here", "plain.golden",
|
||||
// ): []byte("this is really here ^_^\n"),
|
||||
// },
|
||||
// want: []byte("this is really here ^_^\n"),
|
||||
// },
|
||||
// {
|
||||
// name: "sub-test file is missing",
|
||||
// testName: "TestFooBar/not_really_here",
|
||||
// args: args{name: "plain"},
|
||||
// files: map[string][]byte{},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: open " + filepath.Join(
|
||||
// "testdata", "TestFooBar", "not_really_here", "plain.golden",
|
||||
// ) + ": file does not exist\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "blank test name",
|
||||
// testName: "",
|
||||
// args: args{name: "plain"},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: could not determine filename for given " +
|
||||
// "*mocktesting.T instance\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "blank name",
|
||||
// testName: "TestFooBar",
|
||||
// args: args{name: ""},
|
||||
// wantAborted: true,
|
||||
// wantFailCount: 1,
|
||||
// wantTestOutput: []string{
|
||||
// "golden: name cannot be empty\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname",
|
||||
// testName: "TestFozBar",
|
||||
// args: args{name: "yaml"},
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "goldenfiles", "TestFozBar", "yaml.golden",
|
||||
// ): []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom suffix",
|
||||
// testName: "TestFozBaz",
|
||||
// args: args{name: "yaml"},
|
||||
// fields: fields{
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "testdata", "TestFozBaz", "yaml.goldfile",
|
||||
// ): []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "custom dirname and suffix",
|
||||
// testName: "TestFozBar",
|
||||
// args: args{name: "yaml"},
|
||||
// fields: fields{
|
||||
// dirname: stringPtr("goldenfiles"),
|
||||
// suffix: stringPtr(".goldfile"),
|
||||
// },
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "goldenfiles", "TestFozBar", "yaml.goldfile",
|
||||
// ): []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// {
|
||||
// name: "invalid chars in test name",
|
||||
// testName: `TestFooBar/foo?<>:*|"bar`,
|
||||
// args: args{name: "trash"},
|
||||
// files: map[string][]byte{
|
||||
// filepath.Join(
|
||||
// "testdata", "TestFooBar", "foo_______bar", "trash.golden",
|
||||
// ): []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// want: []byte("foo: bar\nhello: world"),
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// fs := NewFS() // TODO: Replace with in-memory stub FS
|
||||
// for f, b := range tt.files {
|
||||
// _ = fs.WriteFile(f, b, 0o644)
|
||||
// }
|
||||
|
||||
// if tt.fields.suffix == nil {
|
||||
// tt.fields.suffix = stringPtr(".golden")
|
||||
// }
|
||||
// if tt.fields.dirname == nil {
|
||||
// tt.fields.dirname = stringPtr("testdata")
|
||||
// }
|
||||
|
||||
// g := &gold{
|
||||
// suffix: *tt.fields.suffix,
|
||||
// dirname: *tt.fields.dirname,
|
||||
// fs: fs,
|
||||
// }
|
||||
|
||||
// mt := mocktesting.NewT(tt.testName)
|
||||
|
||||
// var got []byte
|
||||
// mocktesting.Go(func() {
|
||||
// got = g.GetP(mt, tt.args.name)
|
||||
// })
|
||||
|
||||
// assert.Equal(t, tt.want, got)
|
||||
// assert.Equal(t, tt.wantAborted, mt.Aborted(), "aborted")
|
||||
// assert.Equal(t,
|
||||
// tt.wantFailCount, mt.FailedCount(), "failed count",
|
||||
// )
|
||||
// assert.Equal(t, tt.wantTestOutput, mt.Output(), "test output")
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
502
golden.go
502
golden.go
@@ -2,177 +2,271 @@
|
||||
// with a focus on simplicity through it's default behavior.
|
||||
//
|
||||
// Golden file names are based on the name of the test function and any subtest
|
||||
// names by calling t.Name(). File names are sanitized to ensure they're
|
||||
// compatible with Linux, macOS and Windows systems regardless of what crazy
|
||||
// names by calling t.Name(). File names are sanitized to ensure they are
|
||||
// compatible with Linux, macOS and Windows systems regardless of what
|
||||
// characters might be in a subtest's name.
|
||||
//
|
||||
// Usage
|
||||
// # Usage
|
||||
//
|
||||
// Typical usage should look something like this:
|
||||
//
|
||||
// func TestExampleMyStruct(t *testing.T) {
|
||||
// got, err := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
// require.NoError(t, err)
|
||||
// func TestExampleMyStruct(t *testing.T) {
|
||||
// got, err := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
// require.NoError(t, err)
|
||||
//
|
||||
// if golden.Update() {
|
||||
// golden.Set(t, got)
|
||||
// }
|
||||
// want := golden.Get(t)
|
||||
// want := golden.Do(t, got)
|
||||
//
|
||||
// assert.Equal(t, want, got)
|
||||
// }
|
||||
// assert.Equal(t, want, got)
|
||||
// }
|
||||
//
|
||||
// The above example will read/write to:
|
||||
// The above example will attempt to read/write to:
|
||||
//
|
||||
// testdata/TestExampleMyStruct.golden
|
||||
// testdata/TestExampleMyStruct.golden
|
||||
//
|
||||
// The call to golden.Do() is equivalent to:
|
||||
//
|
||||
// if golden.Update() {
|
||||
// golden.Set(t, got)
|
||||
// }
|
||||
// want := golden.Get(t)
|
||||
//
|
||||
// To update the golden file (have golden.Update() return true), simply set the
|
||||
// GOLDEN_UPDATE environment variable to one of "1", "y", "t", "yes", "on", or
|
||||
// "true" when running tests.
|
||||
//
|
||||
// Sub-Tests
|
||||
// # Sub-Tests
|
||||
//
|
||||
// As the golden filename is based on t.Name(), it works with sub-tests too,
|
||||
// ensuring each sub-test gets it's own golden file. For example:
|
||||
//
|
||||
// func TestExampleMyStructTabular(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// obj *MyStruct
|
||||
// }{
|
||||
// {name: "empty struct", obj: &MyStruct{}},
|
||||
// {name: "full struct", obj: &MyStruct{Foo: "Bar"}},
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// got, err := json.Marshal(tt.obj)
|
||||
// require.NoError(t, err)
|
||||
// func TestExampleMyStructTabular(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// obj *MyStruct
|
||||
// }{
|
||||
// {name: "empty struct", obj: &MyStruct{}},
|
||||
// {name: "full struct", obj: &MyStruct{Foo: "Bar"}},
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// got, err := json.Marshal(tt.obj)
|
||||
// require.NoError(t, err)
|
||||
//
|
||||
// if golden.Update() {
|
||||
// golden.Set(t, got)
|
||||
// }
|
||||
// want := golden.Get(t)
|
||||
// want := golden.Do(t, got)
|
||||
//
|
||||
// assert.Equal(t, want, got)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// assert.Equal(t, want, got)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The above example will read/write to:
|
||||
//
|
||||
// testdata/TestExampleMyStructTabular/empty_struct.golden
|
||||
// testdata/TestExampleMyStructTabular/full_struct.golden
|
||||
// testdata/TestExampleMyStructTabular/empty_struct.golden
|
||||
// testdata/TestExampleMyStructTabular/full_struct.golden
|
||||
//
|
||||
// Multiple Golden Files in a Single Test
|
||||
// # Multiple Golden Files in a Single Test
|
||||
//
|
||||
// The "P" suffixed methods, GetP(), SetP(), and FileP(), all take a name
|
||||
// The "P" suffixed methods, GetP(), SetP(), DoP(), and FileP(), all take a name
|
||||
// argument which allows using specific golden files within a given *testing.T
|
||||
// instance.
|
||||
//
|
||||
// func TestExampleMyStructP(t *testing.T) {
|
||||
// gotJSON, _ := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
// gotXML, _ := xml.Marshal(&MyStruct{Foo: "Bar"})
|
||||
// func TestExampleMyStructP(t *testing.T) {
|
||||
// gotJSON, _ := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
// gotXML, _ := xml.Marshal(&MyStruct{Foo: "Bar"})
|
||||
//
|
||||
// if golden.Update() {
|
||||
// golden.SetP(t, "json", gotJSON)
|
||||
// golden.SetP(t, "xml", gotXML)
|
||||
// }
|
||||
// wantJSON := golden.DoP(t, "json", gotJSON)
|
||||
// wantXML := golden.DoP(t, "xml", gotXML)
|
||||
//
|
||||
// assert.Equal(t, golden.GetP(t, "json"), gotJSON)
|
||||
// assert.Equal(t, golden.GetP(t, "xml"), gotXML)
|
||||
// }
|
||||
// assert.Equal(t, wantJSON, gotJSON)
|
||||
// assert.Equal(t, wantXML, gotXML)
|
||||
// }
|
||||
//
|
||||
// The above example will read/write to:
|
||||
//
|
||||
// testdata/TestExampleMyStructP/json.golden
|
||||
// testdata/TestExampleMyStructP/xml.golden
|
||||
// testdata/TestExampleMyStructP/json.golden
|
||||
// testdata/TestExampleMyStructP/xml.golden
|
||||
//
|
||||
// This works with tabular tests too of course:
|
||||
//
|
||||
// func TestExampleMyStructTabularP(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// obj *MyStruct
|
||||
// }{
|
||||
// {name: "empty struct", obj: &MyStruct{}},
|
||||
// {name: "full struct", obj: &MyStruct{Foo: "Bar"}},
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// gotJSON, _ := json.Marshal(tt.obj)
|
||||
// gotXML, _ := xml.Marshal(tt.obj)
|
||||
// func TestExampleMyStructTabularP(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// obj *MyStruct
|
||||
// }{
|
||||
// {name: "empty struct", obj: &MyStruct{}},
|
||||
// {name: "full struct", obj: &MyStruct{Foo: "Bar"}},
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// gotJSON, _ := json.Marshal(tt.obj)
|
||||
// gotXML, _ := xml.Marshal(tt.obj)
|
||||
//
|
||||
// if golden.Update() {
|
||||
// golden.SetP(t, "json", gotJSON)
|
||||
// golden.SetP(t, "xml", gotXML)
|
||||
// }
|
||||
// wantJSON := golden.DoP(t, "json", gotJSON)
|
||||
// wantXML := golden.DoP(t, "xml", gotXML)
|
||||
//
|
||||
// assert.Equal(t, golden.GetP(t, "json"), gotJSON)
|
||||
// assert.Equal(t, golden.GetP(t, "xml"), gotXML)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// assert.Equal(t, wantJSON, gotJSON)
|
||||
// assert.Equal(t, wantXML, gotXML)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The above example will read/write to:
|
||||
//
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/xml.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/xml.golden
|
||||
//
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/xml.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/xml.golden
|
||||
package golden
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
import "os"
|
||||
|
||||
var (
|
||||
// DefaultGolden is the default Golden instance used by all top-level
|
||||
// package functions.
|
||||
DefaultGolden = New()
|
||||
|
||||
// DefaultDirMode is the default file system permissions used for any
|
||||
// created directories to hold golden files.
|
||||
DefaultDirMode = os.FileMode(0o755)
|
||||
|
||||
// DefaultFileMode is the default file system permissions used for any
|
||||
// created or updated golden files written to disk.
|
||||
DefaultFileMode = os.FileMode(0o644)
|
||||
|
||||
// DefaultSuffix is the default filename suffix used for all golden files.
|
||||
DefaultSuffix = ".golden"
|
||||
|
||||
// DefaultDirname is the default name of the top-level directory used to
|
||||
// hold golden files.
|
||||
DefaultDirname = "testdata"
|
||||
|
||||
// DefaultUpdateFunc is the default function used to determine if golden
|
||||
// files should be updated or not. It is called by Update().
|
||||
DefaultUpdateFunc = EnvUpdateFunc
|
||||
|
||||
// DefaultLogOnWrite is the default value for logOnWrite on all Golden
|
||||
// instances.
|
||||
DefaultLogOnWrite = true
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDirMode = 0o755
|
||||
DefaultFileMode = 0o644
|
||||
DefaultSuffix = ".golden"
|
||||
DefaultDirname = "testdata"
|
||||
)
|
||||
// Golden handles all interactions with golden files. The top-level package
|
||||
// functions proxy through to a default global Golden instance.
|
||||
type Golden interface {
|
||||
// File returns the filename of the golden file for the given testing.TB
|
||||
// instance as determined by t.Name().
|
||||
File(t TestingT) string
|
||||
|
||||
var DefaultUpdateFunc = EnvUpdateFunc
|
||||
// Get returns the content of the golden file for the given TestingT
|
||||
// instance as determined by t.Name(). If no golden file can be found/read,
|
||||
// it will fail the test by calling t.Fatal().
|
||||
Get(t TestingT) []byte
|
||||
|
||||
var global = New()
|
||||
// Set writes given data to the golden file for the given TestingT
|
||||
// instance as determined by t.Name(). If writing fails it will fail the
|
||||
// test by calling t.Fatal() with error details.
|
||||
Set(t TestingT, data []byte)
|
||||
|
||||
// Do is a convenience function for calling Update(), Set(), and Get() in a
|
||||
// single call. If Update() returns true, data will be written to the golden
|
||||
// file using Set(), before reading it back with Get().
|
||||
Do(t TestingT, data []byte) []byte
|
||||
|
||||
// FileP returns the filename of the specifically named golden file for the
|
||||
// given TestingT instance as determined by t.Name().
|
||||
FileP(t TestingT, name string) string
|
||||
|
||||
// GetP returns the content of the specifically named golden file belonging
|
||||
// to the given TestingT instance as determined by t.Name(). If no golden
|
||||
// file can be found/read, it will fail the test with t.Fatal().
|
||||
//
|
||||
// This is very similar to Get(), but it allows multiple different golden
|
||||
// files to be used within the same one TestingT instance.
|
||||
GetP(t TestingT, name string) []byte
|
||||
|
||||
// SetP writes given data of the specifically named golden file belonging to
|
||||
// the given TestingT instance as determined by t.Name(). If writing fails
|
||||
// it will fail the test with t.Fatal() detailing the error.
|
||||
//
|
||||
// This is very similar to Set(), but it allows multiple different golden
|
||||
// files to be used within the same one TestingT instance.
|
||||
SetP(t TestingT, name string, data []byte)
|
||||
|
||||
// DoP is a convenience function for calling Update(), SetP(), and GetP() in
|
||||
// a single call. If Update() returns true, data will be written to the
|
||||
// golden file using SetP(), before reading it back with GetP().
|
||||
DoP(t TestingT, name string, data []byte) []byte
|
||||
|
||||
// Update returns true when golden is set to update golden files. Should be
|
||||
// used to determine if golden.Set() or golden.SetP() should be called or
|
||||
// not.
|
||||
//
|
||||
// Default behavior uses EnvUpdateFunc() to check if the "GOLDEN_UPDATE"
|
||||
// environment variable is set to a truthy value. To customize set a new
|
||||
// UpdateFunc value on *Golden.
|
||||
Update() bool
|
||||
}
|
||||
|
||||
// New returns a new Golden instance. Used to create custom Golden instances.
|
||||
// See the the various Option functions for details of what can be customized.
|
||||
func New(options ...Option) Golden {
|
||||
g := &gold{
|
||||
dirMode: DefaultDirMode,
|
||||
fileMode: DefaultFileMode,
|
||||
suffix: DefaultSuffix,
|
||||
dirname: DefaultDirname,
|
||||
updateFunc: DefaultUpdateFunc,
|
||||
fs: DefaultFS,
|
||||
logOnWrite: DefaultLogOnWrite,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt.apply(g)
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// Do is a convenience function for calling Update(), Set(), and Get() in a
|
||||
// single call. If Update() returns true, data will be written to the golden
|
||||
// file using Set(), before reading it back with Get().
|
||||
func Do(t TestingT, data []byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
return DefaultGolden.Do(t, data)
|
||||
}
|
||||
|
||||
// DoP is a convenience function for calling Update(), SetP(), and GetP() in a
|
||||
// single call. If Update() returns true, data will be written to the golden
|
||||
// file using SetP(), before reading it back with GetP().
|
||||
func DoP(t TestingT, name string, data []byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
return DefaultGolden.DoP(t, name, data)
|
||||
}
|
||||
|
||||
// File returns the filename of the golden file for the given *testing.T
|
||||
// instance as determined by t.Name().
|
||||
func File(t *testing.T) string {
|
||||
func File(t TestingT) string {
|
||||
t.Helper()
|
||||
|
||||
return global.File(t)
|
||||
return DefaultGolden.File(t)
|
||||
}
|
||||
|
||||
// FileP returns the filename of the specifically named golden file for the
|
||||
// given *testing.T instance as determined by t.Name().
|
||||
func FileP(t TestingT, name string) string {
|
||||
t.Helper()
|
||||
|
||||
return DefaultGolden.FileP(t, name)
|
||||
}
|
||||
|
||||
// Get returns the content of the golden file for the given *testing.T instance
|
||||
// as determined by t.Name(). If no golden file can be found/read, it will fail
|
||||
// the test by calling t.Fatal().
|
||||
func Get(t *testing.T) []byte {
|
||||
func Get(t TestingT) []byte {
|
||||
t.Helper()
|
||||
|
||||
return global.Get(t)
|
||||
}
|
||||
|
||||
// Set writes given data to the golden file for the given *testing.T instance as
|
||||
// determined by t.Name(). If writing fails it will fail the test by calling
|
||||
// t.Fatal() with error details.
|
||||
func Set(t *testing.T, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
global.Set(t, data)
|
||||
}
|
||||
|
||||
// FileP returns the filename of the specifically named golden file for the
|
||||
// given *testing.T instance as determined by t.Name().
|
||||
func FileP(t *testing.T, name string) string {
|
||||
t.Helper()
|
||||
|
||||
return global.FileP(t, name)
|
||||
return DefaultGolden.Get(t)
|
||||
}
|
||||
|
||||
// GetP returns the content of the specifically named golden file belonging
|
||||
@@ -181,10 +275,19 @@ func FileP(t *testing.T, name string) string {
|
||||
//
|
||||
// This is very similar to Get(), but it allows multiple different golden files
|
||||
// to be used within the same one *testing.T instance.
|
||||
func GetP(t *testing.T, name string) []byte {
|
||||
func GetP(t TestingT, name string) []byte {
|
||||
t.Helper()
|
||||
|
||||
return global.GetP(t, name)
|
||||
return DefaultGolden.GetP(t, name)
|
||||
}
|
||||
|
||||
// Set writes given data to the golden file for the given *testing.T instance as
|
||||
// determined by t.Name(). If writing fails it will fail the test by calling
|
||||
// t.Fatal() with error details.
|
||||
func Set(t TestingT, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
DefaultGolden.Set(t, data)
|
||||
}
|
||||
|
||||
// SetP writes given data of the specifically named golden file belonging to
|
||||
@@ -193,10 +296,10 @@ func GetP(t *testing.T, name string) []byte {
|
||||
//
|
||||
// This is very similar to Set(), but it allows multiple different golden files
|
||||
// to be used within the same one *testing.T instance.
|
||||
func SetP(t *testing.T, name string, data []byte) {
|
||||
func SetP(t TestingT, name string, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
global.SetP(t, name, data)
|
||||
DefaultGolden.SetP(t, name, data)
|
||||
}
|
||||
|
||||
// Update returns true when golden is set to update golden files. Should be used
|
||||
@@ -206,170 +309,5 @@ func SetP(t *testing.T, name string, data []byte) {
|
||||
// environment variable is set to a truthy value. To customize create a custom
|
||||
// *Golden instance with New() and set a new UpdateFunc value.
|
||||
func Update() bool {
|
||||
return global.Update()
|
||||
}
|
||||
|
||||
// Golden handles all interactions with golden files. The top-level package
|
||||
// functions all just proxy through to a default global *Golden instance.
|
||||
type Golden struct {
|
||||
// DirMode determines the file system permissions of any folders created to
|
||||
// hold golden files.
|
||||
DirMode os.FileMode
|
||||
|
||||
// FileMode determines the file system permissions of any created or updated
|
||||
// golden files written to disk.
|
||||
FileMode os.FileMode
|
||||
|
||||
// Suffix determines the filename suffix for all golden files. Typically
|
||||
// this should be ".golden", but can be changed here if needed.
|
||||
Suffix string
|
||||
|
||||
// Dirname is the name of the top-level directory at the root of the package
|
||||
// which holds all golden files. Typically this should "testdata", but can
|
||||
// be changed here if needed.
|
||||
Dirname string
|
||||
|
||||
// UpdateFunc is used to determine if golden files should be updated or
|
||||
// not. Its boolean return value is returned by Update().
|
||||
UpdateFunc UpdateFunc
|
||||
}
|
||||
|
||||
// New returns a new *Golden instance with default values correctly
|
||||
// populated. This is ideally how you should create a custom *Golden, and then
|
||||
// modify the relevant fields as you see fit.
|
||||
func New() *Golden {
|
||||
return &Golden{
|
||||
DirMode: DefaultDirMode,
|
||||
FileMode: DefaultFileMode,
|
||||
Suffix: DefaultSuffix,
|
||||
Dirname: DefaultDirname,
|
||||
UpdateFunc: DefaultUpdateFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// File returns the filename of the golden file for the given *testing.T
|
||||
// instance as determined by t.Name().
|
||||
func (s *Golden) File(t *testing.T) string {
|
||||
return s.file(t, "")
|
||||
}
|
||||
|
||||
// Get returns the content of the golden file for the given *testing.T instance
|
||||
// as determined by t.Name(). If no golden file can be found/read, it will fail
|
||||
// the test by calling t.Fatal().
|
||||
func (s *Golden) Get(t *testing.T) []byte {
|
||||
return s.get(t, "")
|
||||
}
|
||||
|
||||
// Set writes given data to the golden file for the given *testing.T instance as
|
||||
// determined by t.Name(). If writing fails it will fail the test by calling
|
||||
// t.Fatal() with error details.
|
||||
func (s *Golden) Set(t *testing.T, data []byte) {
|
||||
s.set(t, "", data)
|
||||
}
|
||||
|
||||
// FileP returns the filename of the specifically named golden file for the
|
||||
// given *testing.T instance as determined by t.Name().
|
||||
func (s *Golden) FileP(t *testing.T, name string) string {
|
||||
if name == "" {
|
||||
if t != nil {
|
||||
t.Fatal("golden: name cannot be empty")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
return s.file(t, name)
|
||||
}
|
||||
|
||||
// GetP returns the content of the specifically named golden file belonging
|
||||
// to the given *testing.T instance as determined by t.Name(). If no golden file
|
||||
// can be found/read, it will fail the test with t.Fatal().
|
||||
//
|
||||
// This is very similar to Get(), but it allows multiple different golden files
|
||||
// to be used within the same one *testing.T instance.
|
||||
func (s *Golden) GetP(t *testing.T, name string) []byte {
|
||||
if name == "" {
|
||||
t.Fatal("golden: name cannot be empty")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.get(t, name)
|
||||
}
|
||||
|
||||
// SetP writes given data of the specifically named golden file belonging to
|
||||
// the given *testing.T instance as determined by t.Name(). If writing fails it
|
||||
// will fail the test with t.Fatal() detailing the error.
|
||||
//
|
||||
// This is very similar to Set(), but it allows multiple different golden files
|
||||
// to be used within the same one *testing.T instance.
|
||||
func (s *Golden) SetP(t *testing.T, name string, data []byte) {
|
||||
if name == "" {
|
||||
t.Fatal("golden: name cannot be empty")
|
||||
}
|
||||
|
||||
s.set(t, name, data)
|
||||
}
|
||||
|
||||
func (s *Golden) file(t *testing.T, name string) string {
|
||||
if t.Name() == "" {
|
||||
t.Fatalf("golden: could not determine filename for: %+v", t)
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
base := []string{s.Dirname, filepath.FromSlash(t.Name())}
|
||||
if name != "" {
|
||||
base = append(base, name)
|
||||
}
|
||||
|
||||
f := filepath.Clean(filepath.Join(base...) + s.Suffix)
|
||||
|
||||
dirty := strings.Split(f, string(os.PathSeparator))
|
||||
clean := make([]string, 0, len(dirty))
|
||||
for _, s := range dirty {
|
||||
clean = append(clean, sanitizeFilename(s))
|
||||
}
|
||||
|
||||
return strings.Join(clean, string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func (s *Golden) get(t *testing.T, name string) []byte {
|
||||
f := s.file(t, name)
|
||||
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: failed reading %s: %s", f, err.Error())
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (s *Golden) set(t *testing.T, name string, data []byte) {
|
||||
f := s.file(t, name)
|
||||
dir := filepath.Dir(f)
|
||||
|
||||
t.Logf("golden: writing .golden file: %s", f)
|
||||
|
||||
err := os.MkdirAll(dir, s.DirMode)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: failed to create directory: %s", err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(f, data, s.FileMode)
|
||||
if err != nil {
|
||||
t.Fatalf("golden: filed to write file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Update returns true when golden is set to update golden files. Should be used
|
||||
// to determine if golden.Set() or golden.SetP() should be called or not.
|
||||
//
|
||||
// Default behavior uses EnvUpdateFunc() to check if the "GOLDEN_UPDATE"
|
||||
// environment variable is set to a truthy value. To customize set a new
|
||||
// UpdateFunc value on *Golden.
|
||||
func (s *Golden) Update() bool {
|
||||
return s.UpdateFunc()
|
||||
return DefaultGolden.Update()
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ type MyStruct struct {
|
||||
|
||||
// TestExampleMyStruct reads/writes the following golden file:
|
||||
//
|
||||
// testdata/TestExampleMyStruct.golden
|
||||
//
|
||||
// testdata/TestExampleMyStruct.golden
|
||||
func TestExampleMyStruct(t *testing.T) {
|
||||
got, err := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
require.NoError(t, err)
|
||||
@@ -32,9 +31,8 @@ func TestExampleMyStruct(t *testing.T) {
|
||||
|
||||
// TestExampleMyStructTabular reads/writes the following golden files:
|
||||
//
|
||||
// testdata/TestExampleMyStructTabular/empty_struct.golden
|
||||
// testdata/TestExampleMyStructTabular/full_struct.golden
|
||||
//
|
||||
// testdata/TestExampleMyStructTabular/empty_struct.golden
|
||||
// testdata/TestExampleMyStructTabular/full_struct.golden
|
||||
func TestExampleMyStructTabular(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -60,9 +58,8 @@ func TestExampleMyStructTabular(t *testing.T) {
|
||||
|
||||
// TestExampleMyStructP reads/writes the following golden file:
|
||||
//
|
||||
// testdata/TestExampleMyStructP/json.golden
|
||||
// testdata/TestExampleMyStructP/xml.golden
|
||||
//
|
||||
// testdata/TestExampleMyStructP/json.golden
|
||||
// testdata/TestExampleMyStructP/xml.golden
|
||||
func TestExampleMyStructP(t *testing.T) {
|
||||
gotJSON, _ := json.Marshal(&MyStruct{Foo: "Bar"})
|
||||
gotXML, _ := xml.Marshal(&MyStruct{Foo: "Bar"})
|
||||
@@ -78,11 +75,10 @@ func TestExampleMyStructP(t *testing.T) {
|
||||
|
||||
// TestExampleMyStructTabularP reads/writes the following golden file:
|
||||
//
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/xml.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/xml.golden
|
||||
//
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/empty_struct/xml.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/json.golden
|
||||
// testdata/TestExampleMyStructTabularP/full_struct/xml.golden
|
||||
func TestExampleMyStructTabularP(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
1187
golden_test.go
1187
golden_test.go
File diff suppressed because it is too large
Load Diff
80
options.go
Normal file
80
options.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package golden
|
||||
|
||||
import "os"
|
||||
|
||||
type Option interface {
|
||||
apply(*gold)
|
||||
}
|
||||
|
||||
type optionFunc func(*gold)
|
||||
|
||||
func (fn optionFunc) apply(g *gold) {
|
||||
fn(g)
|
||||
}
|
||||
|
||||
// WithDirMode sets the file system permissions used for any folders created to
|
||||
// hold golden files.
|
||||
//
|
||||
// When this option is not provided, the default value is 0o755.
|
||||
func WithDirMode(mode os.FileMode) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.dirMode = mode
|
||||
})
|
||||
}
|
||||
|
||||
// WithFileMode sets the file system permissions used for any created or updated
|
||||
// golden files written to.
|
||||
//
|
||||
// When this option is not provided, the default value is 0o644.
|
||||
func WithFileMode(mode os.FileMode) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.fileMode = mode
|
||||
})
|
||||
}
|
||||
|
||||
// WithSuffix sets the filename suffix used for all golden files.
|
||||
//
|
||||
// When this option is not provided, the default value is ".golden".
|
||||
func WithSuffix(suffix string) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.suffix = suffix
|
||||
})
|
||||
}
|
||||
|
||||
// WithDirname sets the name of the top-level directory used to hold golden
|
||||
// files.
|
||||
//
|
||||
// When this option is not provided, the default value is "testdata".
|
||||
func WithDirname(name string) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.dirname = name
|
||||
})
|
||||
}
|
||||
|
||||
// WithUpdateFunc sets the function used to determine if golden files should be
|
||||
// updated or not. Essentially the provided UpdateFunc is called by Update().
|
||||
//
|
||||
// When this option is not provided, the default value is EnvUpdateFunc.
|
||||
func WithUpdateFunc(fn UpdateFunc) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.updateFunc = fn
|
||||
})
|
||||
}
|
||||
|
||||
// WithFS sets the afero.Fs instance which is used for all file system
|
||||
// operations to read/write golden files.
|
||||
//
|
||||
// When this option is not provided, the default value is afero.NewOsFs().
|
||||
func WithFS(fs FS) Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.fs = fs
|
||||
})
|
||||
}
|
||||
|
||||
// WithSilentWrites silences the "golden: writing [...]" log messages whenever
|
||||
// set functions write a golden file to disk.
|
||||
func WithSilentWrites() Option {
|
||||
return optionFunc(func(g *gold) {
|
||||
g.logOnWrite = false
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package golden
|
||||
package sanitize
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
@@ -15,7 +15,7 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
func sanitizeFilename(name string) string {
|
||||
func Filename(name string) string {
|
||||
if reservedNames.MatchString(name) || winReserved.MatchString(name) {
|
||||
var b []byte
|
||||
for i := 0; i < len(name); i++ {
|
||||
@@ -1,12 +1,13 @@
|
||||
package golden
|
||||
package sanitize_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jimeh/go-golden/sanitize"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_sanitizeFilename(t *testing.T) {
|
||||
func TestFilename(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
@@ -69,6 +70,7 @@ func Test_sanitizeFilename(t *testing.T) {
|
||||
filename: "foobar.golden .. .. .. ",
|
||||
want: "foobar.golden",
|
||||
},
|
||||
// Protected Windows filenames.
|
||||
{name: "con", filename: "con", want: "___"},
|
||||
{name: "prn", filename: "prn", want: "___"},
|
||||
{name: "aux", filename: "aux", want: "___"},
|
||||
@@ -116,7 +118,7 @@ func Test_sanitizeFilename(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := sanitizeFilename(tt.filename)
|
||||
got := sanitize.Filename(tt.filename)
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
21
sanitize/line_breaks.go
Normal file
21
sanitize/line_breaks.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package sanitize
|
||||
|
||||
import "bytes"
|
||||
|
||||
var (
|
||||
lf = []byte{10}
|
||||
cr = []byte{13}
|
||||
crlf = []byte{13, 10}
|
||||
)
|
||||
|
||||
// LineBreaks replaces Windows CRLF (\r\n) and MacOS Classic CR (\r)
|
||||
// line-breaks with Unix LF (\n) line breaks.
|
||||
func LineBreaks(data []byte) []byte {
|
||||
// Replace Windows CRLF (\r\n) with Unix LF (\n)
|
||||
result := bytes.ReplaceAll(data, crlf, lf)
|
||||
|
||||
// Replace Classic MacOS CR (\r) with Unix LF (\n)
|
||||
result = bytes.ReplaceAll(result, cr, lf)
|
||||
|
||||
return result
|
||||
}
|
||||
67
sanitize/line_breaks_test.go
Normal file
67
sanitize/line_breaks_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package sanitize_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jimeh/go-golden/sanitize"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLineBreaks(t *testing.T) {
|
||||
type args struct {
|
||||
data []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
args: args{data: nil},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
args: args{data: []byte{}},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "no line breaks",
|
||||
args: args{data: []byte("hello world")},
|
||||
want: []byte("hello world"),
|
||||
},
|
||||
{
|
||||
name: "UNIX line breaks",
|
||||
args: args{data: []byte("hello\nworld\nhow are you?")},
|
||||
want: []byte("hello\nworld\nhow are you?"),
|
||||
},
|
||||
{
|
||||
name: "Windows line breaks",
|
||||
args: args{data: []byte("hello\r\nworld\r\nhow are you?")},
|
||||
want: []byte("hello\nworld\nhow are you?"),
|
||||
},
|
||||
{
|
||||
name: "MacOS Classic line breaks",
|
||||
args: args{data: []byte("hello\rworld\rhow are you?")},
|
||||
want: []byte("hello\nworld\nhow are you?"),
|
||||
},
|
||||
{
|
||||
name: "Windows and MacOS Classic line breaks",
|
||||
args: args{data: []byte("hello\r\nworld\rhow are you?")},
|
||||
want: []byte("hello\nworld\nhow are you?"),
|
||||
},
|
||||
{
|
||||
name: "Windows, MacOS Classic, and UNIX line breaks",
|
||||
args: args{data: []byte("hello\r\nworld\rhow are you?\nGood!")},
|
||||
want: []byte("hello\nworld\nhow are you?\nGood!"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := sanitize.LineBreaks(tt.args.data)
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
275
test/testfs/testfs.go
Normal file
275
test/testfs/testfs.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package testfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
isDir bool
|
||||
}
|
||||
|
||||
type FS struct {
|
||||
Pwd string
|
||||
Nodes map[string]*Node
|
||||
}
|
||||
|
||||
func New() *FS {
|
||||
return &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o700, isDir: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) MkdirAll(name string, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
dirs := []string{name}
|
||||
for d := path.Dir(name); d != "/"; d = path.Dir(d) {
|
||||
dirs = append(dirs, d)
|
||||
}
|
||||
dirs = append(dirs, "/")
|
||||
|
||||
for i := len(dirs) - 1; i >= 0; i-- {
|
||||
dir := dirs[i]
|
||||
parent := path.Dir(dir)
|
||||
|
||||
if info, ok := fs.Nodes[dir]; ok {
|
||||
if !info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
parentInfo, ok := fs.Nodes[parent]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if !parentInfo.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions, and direct
|
||||
// parent also has write permission.
|
||||
if parentInfo.perm&0o100 == 0 || i == 1 && parentInfo.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[dir] = &Node{perm: perm, isDir: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) ReadFile(name string) ([]byte, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o400 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
return info.data, nil
|
||||
}
|
||||
|
||||
func (fs *FS) WriteFile(name string, data []byte, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if ok {
|
||||
if info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return error if file exists and has no write permission, or if the file
|
||||
// does not exist and the direct parent has no write permission.
|
||||
if ok && info.perm&0o200 == 0 || !ok && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[name] = &Node{data: data, perm: perm}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Remove(name string) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parent != nil && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
for p := range fs.Nodes {
|
||||
if strings.HasPrefix(p, name) && p != name {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("directory not empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(fs.Nodes, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Exists(name string) bool {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, ok := fs.Nodes[name]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs *FS) FileMode(name string) (os.FileMode, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
if info, ok := fs.Nodes[name]; ok {
|
||||
return info.perm, nil
|
||||
}
|
||||
|
||||
return 0, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) checkParents(absPath string, noExistError bool) (*Node, error) {
|
||||
var parents []string
|
||||
for d := path.Dir(absPath); d != "/"; d = path.Dir(d) {
|
||||
parents = append(parents, d)
|
||||
}
|
||||
parents = append(parents, "/")
|
||||
var directParent *Node
|
||||
|
||||
for i := 0; i < len(parents); i++ {
|
||||
dir := parents[i]
|
||||
info, ok := fs.Nodes[dir]
|
||||
if !ok && noExistError {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info != nil && !info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions.
|
||||
if info != nil && info.perm&0o100 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
directParent = info
|
||||
}
|
||||
}
|
||||
|
||||
return directParent, nil
|
||||
}
|
||||
670
test/testfs/testfs_test.go
Normal file
670
test/testfs/testfs_test.go
Normal file
@@ -0,0 +1,670 @@
|
||||
package testfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFSMkdirAll(t *testing.T) {
|
||||
type args struct {
|
||||
path string
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
want map[string]*Node
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "create relative new dir",
|
||||
args: args{path: "newdir", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/newdir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute new dir",
|
||||
args: args{path: "/opt/newdir", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/newdir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "create relative nested dirs",
|
||||
args: args{path: "nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs",
|
||||
args: args{path: "/opt/nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/nested": {perm: 0o755, isDir: true},
|
||||
"/opt/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/opt/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative nested dirs with other perms",
|
||||
args: args{path: "nested/dir/structure", perm: 0o750},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o750, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o750, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o750, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs with other perms",
|
||||
args: args{path: "/opt/nested/dir/structure", perm: 0o750},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o750, isDir: true},
|
||||
"/opt/nested": {perm: 0o750, isDir: true},
|
||||
"/opt/nested/dir": {perm: 0o750, isDir: true},
|
||||
"/opt/nested/dir/structure": {perm: 0o750, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative nested dirs with existing dirs",
|
||||
args: args{path: "nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs with existing dirs",
|
||||
args: args{path: "/root/nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative under file",
|
||||
args: args{path: "file/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root/file": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute under file",
|
||||
args: args{path: "/root/file/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root/file": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create relative directory without execute permission",
|
||||
args: args{path: "dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute directory without execute permission",
|
||||
args: args{path: "/root/dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create relative directory without write permission",
|
||||
args: args{path: "dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o444},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute directory without write permission",
|
||||
args: args{path: "/root/dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o444},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o700, isDir: true},
|
||||
},
|
||||
}
|
||||
|
||||
for fp, info := range tt.nodes {
|
||||
fs.Nodes[fp] = info
|
||||
}
|
||||
|
||||
err := fs.MkdirAll(tt.args.path, tt.args.perm)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
for fp, info := range tt.want {
|
||||
got := fs.Nodes[fp]
|
||||
assert.Equal(t, info, got, "path: %s", fp)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSReadFile(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative read existing file",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "absolute read existing file",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "relative file does not exist",
|
||||
args: args{name: "nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file does not exist",
|
||||
args: args{name: "/opt/nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file is a directory",
|
||||
args: args{name: "dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file is a directory",
|
||||
args: args{name: "/opt/dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file permission denied",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory read permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o355, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "relative no directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no grandparent directory execute permission",
|
||||
args: args{name: "foo/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/foo": {perm: 0o755, isDir: true},
|
||||
"/root/foo/file.txt": {data: []byte("hello"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
got, err := fs.ReadFile(tt.args.name)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSWriteFile(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
wantPath string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative write to new file",
|
||||
args: args{
|
||||
name: "newfile.txt",
|
||||
data: []byte("new content"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute write to new file",
|
||||
args: args{
|
||||
name: "/opt/newfile.txt",
|
||||
data: []byte("new content"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative overwrite existing file",
|
||||
args: args{
|
||||
name: "existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/existing": {data: []byte("existing"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite existing file",
|
||||
args: args{
|
||||
name: "/opt/existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/existing": {data: []byte("existing"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative overwrite file permissions denied",
|
||||
args: args{
|
||||
name: "existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/existing.txt": {data: []byte("existing"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite file permissions denied",
|
||||
args: args{
|
||||
name: "/opt/existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/existing.txt": {data: []byte("existing"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative overwrite directory",
|
||||
args: args{
|
||||
name: "dir",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o644, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite directory",
|
||||
args: args{
|
||||
name: "/opt/dir",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/dir",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o644, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative write to non-existent directory",
|
||||
args: args{
|
||||
name: "nonexistentdir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/nonexistentdir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute write to non-existent directory",
|
||||
args: args{
|
||||
name: "/opt/nonexistentdir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/nonexistentdir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative write parent directory is a file",
|
||||
args: args{
|
||||
name: "file/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/file/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/file": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no parent directory write permission denied",
|
||||
args: args{
|
||||
name: "dir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o500, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no parent directory execute permission denied",
|
||||
args: args{
|
||||
name: "dir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o600, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/tmp",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
err := fs.WriteFile(tt.args.name, tt.args.data, tt.args.perm)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
if _, ok := tt.nodes[tt.wantPath]; ok {
|
||||
assert.Equal(t,
|
||||
tt.nodes[tt.wantPath],
|
||||
fs.Nodes[tt.wantPath],
|
||||
)
|
||||
} else {
|
||||
assert.NotContains(t, fs.Nodes, tt.wantPath)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
got := fs.Nodes[tt.wantPath]
|
||||
assert.Equal(t, tt.args.data, got.data)
|
||||
assert.Equal(t, tt.args.perm, got.perm)
|
||||
assert.Equal(t, false, got.isDir)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSRemove(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative remove existing file",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute remove existing file",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative file does not exist",
|
||||
args: args{name: "nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file does not exist",
|
||||
args: args{name: "/opt/nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file is a directory",
|
||||
args: args{name: "dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute file is a directory",
|
||||
args: args{name: "/opt/dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative file permission denied",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file permission denied",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory write permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o555, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no grandparent directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
"/root/dir/file.txt": {
|
||||
data: []byte("file content"), perm: 0o644,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
err := fs.Remove(tt.args.name)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
10
testingt.go
Normal file
10
testingt.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package golden
|
||||
|
||||
// TestingT is a interface describing a sub-set of methods of *testing.T which
|
||||
// golden uses.
|
||||
type TestingT interface {
|
||||
Fatalf(format string, args ...interface{})
|
||||
Helper()
|
||||
Logf(format string, args ...interface{})
|
||||
Name() string
|
||||
}
|
||||
30
testingt_test.go
Normal file
30
testingt_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package golden
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type fakeTestingT struct {
|
||||
helper bool
|
||||
name string
|
||||
logs []string
|
||||
fatals []string
|
||||
}
|
||||
|
||||
func (m *fakeTestingT) Helper() {
|
||||
m.helper = true
|
||||
}
|
||||
|
||||
func (m *fakeTestingT) Fatalf(format string, args ...interface{}) {
|
||||
m.fatals = append(m.fatals, fmt.Sprintf(format, args...))
|
||||
runtime.Goexit()
|
||||
}
|
||||
|
||||
func (m *fakeTestingT) Logf(format string, args ...interface{}) {
|
||||
m.logs = append(m.logs, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (m *fakeTestingT) Name() string {
|
||||
return m.name
|
||||
}
|
||||
27
update.go
27
update.go
@@ -1,6 +1,10 @@
|
||||
package golden
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var truthyStrings = []string{"1", "y", "t", "yes", "on", "true"}
|
||||
|
||||
@@ -14,10 +18,29 @@ type UpdateFunc func() bool
|
||||
func EnvUpdateFunc() bool {
|
||||
env := os.Getenv("GOLDEN_UPDATE")
|
||||
for _, v := range truthyStrings {
|
||||
if env == v {
|
||||
if strings.ToLower(env) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
updateFlagSet *flag.FlagSet
|
||||
updateFlag bool
|
||||
)
|
||||
|
||||
// UpdateFunc returns a function that checks a -update flag is set.
|
||||
func FlagUpdateFunc() bool {
|
||||
if updateFlagSet == nil {
|
||||
updateFlagSet = flag.NewFlagSet("golden", flag.ContinueOnError)
|
||||
updateFlagSet.BoolVar(&updateFlag,
|
||||
"update", false, "update golden files",
|
||||
)
|
||||
}
|
||||
|
||||
_ = updateFlagSet.Parse(os.Args[1:])
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
115
update_test.go
115
update_test.go
@@ -41,56 +41,171 @@ var envUpdateFuncTestCases = []struct {
|
||||
env: map[string]string{"GOLDEN_UPDATE": "y"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to Y",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "Y"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to n",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "n"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to N",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "N"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to t",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "t"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to T",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "T"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to f",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "f"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to F",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "F"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to yes",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "yes"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to Yes",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "Yes"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to YeS",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "YeS"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to YES",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "YES"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to no",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "no"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to No",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "No"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to nO",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "nO"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to NO",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "NO"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to on",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "on"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to oN",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "oN"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to On",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "On"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to ON",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "ON"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to off",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "off"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to Off",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "Off"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to oFF",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "oFF"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to OFF",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "OFF"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to true",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "true"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to True",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "True"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to TruE",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "TruE"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to TRUE",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "TRUE"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to false",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "false"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to False",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "False"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to FaLsE",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "FaLsE"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to FALSE",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "FALSE"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to foobarnopebbq",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "foobarnopebbq"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "GOLDEN_UPDATE set to FOOBARNOPEBBQ",
|
||||
env: map[string]string{"GOLDEN_UPDATE": "FOOBARNOPEBBQ"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEnvUpdateFunc(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user