Skip to content

Commit

Permalink
Merge pull request #30 from sebdah/feature/improve-structure-tests
Browse files Browse the repository at this point in the history
feature/improve-structure-tests
  • Loading branch information
sebdah authored May 16, 2020
2 parents a14e261 + 2d2fc31 commit 5d28ee4
Show file tree
Hide file tree
Showing 6 changed files with 579 additions and 525 deletions.
220 changes: 220 additions & 0 deletions assertions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package goldie

import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
"text/template"
)

// Assert compares the actual data received with the expected data in the
// golden files. If the update flag is set, it will also update the golden
// file.
//
// `name` refers to the name of the test and it should typically be unique
// within the package. Also it should be a valid file name (so keeping to
// `a-z0-9\-\_` is a good idea).
func (g *Goldie) Assert(t *testing.T, name string, actualData []byte) {
t.Helper()
if *update {
err := g.Update(t, name, actualData)
if err != nil {
t.Error(err)
t.FailNow()
}
}

err := g.compare(t, name, actualData)
if err != nil {
{
var e *errFixtureNotFound
if errors.As(err, &e) {
t.Error(err)
t.FailNow()
return
}
}

{
var e *errFixtureMismatch
if errors.As(err, &e) {
t.Error(err)
return
}
}

t.Error(err)
}
}

// AssertJson compares the actual json data received with expected data in the
// golden files. If the update flag is set, it will also update the golden
// file.
//
// `name` refers to the name of the test and it should typically be unique
// within the package. Also it should be a valid file name (so keeping to
// `a-z0-9\-\_` is a good idea).
func (g *Goldie) AssertJson(t *testing.T, name string, actualJsonData interface{}) {
t.Helper()
js, err := json.MarshalIndent(actualJsonData, "", " ")

if err != nil {
t.Error(err)
t.FailNow()
}

g.Assert(t, name, normalizeLF(js))
}

// AssertXml compares the actual xml data received with expected data in the
// golden files. If the update flag is set, it will also update the golden
// file.
//
// `name` refers to the name of the test and it should typically be unique
// within the package. Also it should be a valid file name (so keeping to
// `a-z0-9\-\_` is a good idea).
func (g *Goldie) AssertXml(t *testing.T, name string, actualXmlData interface{}) {
t.Helper()
x, err := xml.MarshalIndent(actualXmlData, "", " ")

if err != nil {
t.Error(err)
t.FailNow()
}

g.Assert(t, name, normalizeLF(x))
}

// normalizeLF normalizes line feed character set across os (es)
// \r\n (windows) & \r (mac) into \n (unix)
func normalizeLF(d []byte) []byte {
// if empty / nil return as is
if len(d) == 0 {
return d
}
// replace CR LF \r\n (windows) with LF \n (unix)
d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1)
// replace CF \r (mac) with LF \n (unix)
d = bytes.Replace(d, []byte{13}, []byte{10}, -1)
return d
}

// Assert compares the actual data received with the expected data in the
// golden files after executing it as a template with data parameter. If the
// update flag is set, it will also update the golden file. `name` refers to
// the name of the test and it should typically be unique within the package.
// Also it should be a valid file name (so keeping to `a-z0-9\-\_` is a good
// idea).
func (g *Goldie) AssertWithTemplate(t *testing.T, name string, data interface{}, actualData []byte) {
t.Helper()
if *update {
err := g.Update(t, name, actualData)
if err != nil {
t.Error(err)
t.FailNow()
}
}

err := g.compareTemplate(t, name, data, actualData)
if err != nil {
{
var e *errFixtureNotFound
if errors.As(err, &e) {
t.Error(err)
t.FailNow()
return
}
}

{
var e *errFixtureMismatch
if errors.As(err, &e) {
t.Error(err)
return
}
}

t.Error(err)
}
}

// compare is reading the golden fixture file and compare the stored data with
// the actual data.
func (g *Goldie) compare(t *testing.T, name string, actualData []byte) error {
expectedData, err := ioutil.ReadFile(g.GoldenFileName(t, name))

if err != nil {
if os.IsNotExist(err) {
return newErrFixtureNotFound()
}

return fmt.Errorf("expected %s to be nil", err.Error())
}

if !bytes.Equal(actualData, expectedData) {
msg := "Result did not match the golden fixture. Diff is below:\n\n"
actual := string(actualData)
expected := string(expectedData)

if g.diffFn != nil {
msg += g.diffFn(actual, expected)
} else {
msg += Diff(g.diffEngine, actual, expected)
}

return newErrFixtureMismatch(msg)
}

return nil
}

// compareTemplate is reading the golden fixture file and compare the stored
// data with the actual data.
func (g *Goldie) compareTemplate(t *testing.T, name string, data interface{}, actualData []byte) error {
expectedDataTmpl, err := ioutil.ReadFile(g.GoldenFileName(t, name))

if err != nil {
if os.IsNotExist(err) {
return newErrFixtureNotFound()
}

return fmt.Errorf("expected %s to be nil", err.Error())
}

missingKey := "error"
if g.ignoreTemplateErrors {
missingKey = "default"
}

tmpl, err := template.New("test").Option("missingkey=" + missingKey).Parse(string(expectedDataTmpl))
if err != nil {
return fmt.Errorf("expected %s to be nil", err.Error())
}

var expectedData bytes.Buffer
err = tmpl.Execute(&expectedData, data)
if err != nil {
return newErrMissingKey(fmt.Sprintf("Template error: %s", err.Error()))
}

if !bytes.Equal(actualData, expectedData.Bytes()) {
msg := "Result did not match the golden fixture. Diff is below:\n\n"
actual := string(actualData)
expected := expectedData.String()

if g.diffFn != nil {
msg += g.diffFn(actual, expected)
} else {
msg += Diff(g.diffEngine, actual, expected)
}

return newErrFixtureMismatch(msg)
}

return nil
}
149 changes: 149 additions & 0 deletions assertions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package goldie

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

"github.com/stretchr/testify/assert"
)

func TestCompare(t *testing.T) {
tests := []struct {
name string
actualData []byte
expectedData []byte
update bool
err error
}{
{
name: "example",
actualData: []byte("abc"),
expectedData: []byte("abc"),
update: true,
err: nil,
},
{
name: "example",
actualData: []byte("abc"),
expectedData: []byte("abc"),
update: false,
err: &errFixtureNotFound{},
},
{
name: "example",
actualData: []byte("bc"),
expectedData: []byte("abc"),
update: true,
err: &errFixtureMismatch{},
},
{
name: "nil",
actualData: nil,
expectedData: nil,
update: true,
err: nil,
},
}

g := New(t)

for _, test := range tests {
if test.update {
err := g.Update(t, test.name, test.expectedData)
assert.Nil(t, err)
}

err := g.compare(t, test.name, test.actualData)
assert.IsType(t, test.err, err)

g.GoldenFileName(t, test.name)
err = os.RemoveAll(filepath.Dir(g.GoldenFileName(t, test.name)))
assert.Nil(t, err)
}
}

func TestCompareTemplate(t *testing.T) {
data := struct {
Name string
}{
Name: "example",
}

tests := []struct {
name string
actualData []byte
expectedData []byte
data interface{}
update bool
err error
}{
{
name: "example",
actualData: []byte("abc example"),
expectedData: []byte("abc {{ .Name }}"),
data: data,
update: true,
err: nil,
},
{
name: "example",
actualData: []byte("abc example"),
expectedData: []byte("abc {{ .Name }}"),
data: nil,
update: false,
err: &errFixtureNotFound{},
},
{
name: "example",
actualData: []byte("bc example"),
expectedData: []byte("abc {{ .Name }}"),
data: data,
update: true,
err: &errFixtureMismatch{},
},
{
name: "example",
actualData: []byte("bc example"),
expectedData: []byte("abc {{ .Name }}"),
data: nil,
update: true,
err: &errMissingKey{},
}}

g := New(t)

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.update {
err := g.Update(t, test.name, test.expectedData)
assert.Nil(t, err)
}

err := g.compareTemplate(t, test.name, test.data, test.actualData)
assert.IsType(t, test.err, err)

err = os.RemoveAll(g.fixtureDir)
assert.Nil(t, err)
})
}
}

func TestNormalizeLF(t *testing.T) {
tests := map[string]struct {
input []byte
expectedD []byte
}{
"windows style": {[]byte("Hello\r\nWorld"), []byte("Hello\nWorld")},
"mac style": {[]byte("Hello\rWorld"), []byte("Hello\nWorld")},
"unix style": {[]byte("Hello\nWorld"), []byte("Hello\nWorld")},
"empty slice": {[]byte(""), []byte{}},
"nil input": {nil, nil},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(t, test.expectedD, normalizeLF(test.input))
})
}
}
Loading

0 comments on commit 5d28ee4

Please sign in to comment.