mirror of
https://github.com/foomo/keel.git
synced 2025-10-16 12:35:34 +00:00
Merge pull request #151 from foomo/feature/inline-assertions
feat: add inline utility and assertions
This commit is contained in:
commit
d523809c10
1
go.mod
1
go.mod
@ -15,6 +15,7 @@ require (
|
||||
github.com/sony/gobreaker v0.5.0
|
||||
github.com/spf13/viper v1.12.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tidwall/pretty v1.0.0
|
||||
github.com/tinylib/msgp v1.1.6
|
||||
go.mongodb.org/mongo-driver v1.10.2
|
||||
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.32.0
|
||||
|
||||
39
test/assert/assert.go
Normal file
39
test/assert/assert.go
Normal file
@ -0,0 +1,39 @@
|
||||
package keelassert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/keel/log"
|
||||
keeltestutil "github.com/foomo/keel/test/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/pretty"
|
||||
)
|
||||
|
||||
func InlineEqual(t *testing.T, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
expected, ok := keeltestutil.Inline(t, 2, "%v", actual)
|
||||
if ok {
|
||||
return assert.Equal(t, expected, fmt.Sprintf("%v", actual), msgAndArgs...)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func InlineJSONEq(t *testing.T, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
// marshal value
|
||||
actualBytes, err := json.Marshal(actual)
|
||||
if err != nil {
|
||||
t.Fatal("failed to marshal json", log.FError(err))
|
||||
}
|
||||
|
||||
expected, ok := keeltestutil.Inline(t, 2, string(actualBytes))
|
||||
if ok {
|
||||
return assert.Equal(t, string(pretty.Pretty([]byte(expected))), string(pretty.Pretty(actualBytes)), msgAndArgs...)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
63
test/assert/assert_test.go
Normal file
63
test/assert/assert_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package keelassert_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
keelassert "github.com/foomo/keel/test/assert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEqualInline(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
when func(t *testing.T) bool
|
||||
}{
|
||||
{
|
||||
name: "equal int",
|
||||
when: func(t *testing.T) bool { //nolint:thelper
|
||||
return keelassert.InlineEqual(t, 15) // INLINE: 15
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "equal bool",
|
||||
when: func(t *testing.T) bool { //nolint:thelper
|
||||
return keelassert.InlineEqual(t, true) // INLINE: true
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "equal string",
|
||||
when: func(t *testing.T) bool { //nolint:thelper
|
||||
return keelassert.InlineEqual(t, "foo bar") // INLINE: foo bar
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.True(t, tt.when(t))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqualInlineJSONEq(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
when func(t *testing.T) bool
|
||||
}{
|
||||
{
|
||||
name: "equal json",
|
||||
when: func(t *testing.T) bool { //nolint:thelper
|
||||
x := struct {
|
||||
Foo string `json:"foo"`
|
||||
}{
|
||||
Foo: "bar",
|
||||
}
|
||||
return keelassert.InlineJSONEq(t, x) // INLINE: {"foo":"bar"}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.True(t, tt.when(t))
|
||||
})
|
||||
}
|
||||
}
|
||||
28
test/assert/assertions.go
Normal file
28
test/assert/assertions.go
Normal file
@ -0,0 +1,28 @@
|
||||
package keelassert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Assertions provides assertion methods around the
|
||||
// TestingT interface.
|
||||
type Assertions struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// New makes a new Assertions object for the specified TestingT.
|
||||
func New(t *testing.T) *Assertions { //nolint:thelper
|
||||
return &Assertions{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Assertions) InlineEqual(actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
a.t.Helper()
|
||||
return InlineEqual(a.t, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
func (a *Assertions) InlineJSONEq(actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
a.t.Helper()
|
||||
return InlineJSONEq(a.t, actual, msgAndArgs...)
|
||||
}
|
||||
28
test/require/assertions.go
Normal file
28
test/require/assertions.go
Normal file
@ -0,0 +1,28 @@
|
||||
package keelrequire
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Assertions provides assertion methods around the
|
||||
// TestingT interface.
|
||||
type Assertions struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// New makes a new Assertions object for the specified TestingT.
|
||||
func New(t *testing.T) *Assertions { //nolint:thelper
|
||||
return &Assertions{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Assertions) InlineEqual(actual interface{}, msgAndArgs ...interface{}) {
|
||||
a.t.Helper()
|
||||
InlineEqual(a.t, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
func (a *Assertions) InlineJSONEq(actual interface{}, msgAndArgs ...interface{}) {
|
||||
a.t.Helper()
|
||||
InlineJSONEq(a.t, actual, msgAndArgs...)
|
||||
}
|
||||
37
test/require/require.go
Normal file
37
test/require/require.go
Normal file
@ -0,0 +1,37 @@
|
||||
package keelrequire
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/keel/log"
|
||||
keeltestutil "github.com/foomo/keel/test/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/pretty"
|
||||
)
|
||||
|
||||
func InlineEqual(t *testing.T, actual interface{}, msgAndArgs ...interface{}) {
|
||||
t.Helper()
|
||||
if t.Failed() {
|
||||
return
|
||||
}
|
||||
if expected, ok := keeltestutil.Inline(t, 2, "%v", actual); ok {
|
||||
require.Equal(t, expected, fmt.Sprintf("%v", actual), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
func InlineJSONEq(t *testing.T, actual interface{}, msgAndArgs ...interface{}) {
|
||||
t.Helper()
|
||||
if t.Failed() {
|
||||
return
|
||||
}
|
||||
// marshal value
|
||||
actualBytes, err := json.Marshal(actual)
|
||||
if err != nil {
|
||||
t.Fatal("failed to marshal json", log.FError(err))
|
||||
}
|
||||
if expected, ok := keeltestutil.Inline(t, 2, string(actualBytes)); ok {
|
||||
require.Equal(t, string(pretty.Pretty([]byte(expected))), string(pretty.Pretty(actualBytes)), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
86
test/util/inline.go
Normal file
86
test/util/inline.go
Normal file
@ -0,0 +1,86 @@
|
||||
package keeltestutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/keel/log"
|
||||
)
|
||||
|
||||
func Inline(t *testing.T, skip int, msgAndArgs ...interface{}) (string, bool) {
|
||||
t.Helper()
|
||||
|
||||
// retrieve caller info
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
t.Fatal("failed to retrieve caller")
|
||||
}
|
||||
|
||||
// read file
|
||||
fileBytes, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal("failed to read caller file", log.FError(err))
|
||||
}
|
||||
fileLines := strings.Split(string(fileBytes), "\n")
|
||||
fileLine := fileLines[line-1]
|
||||
fileLineParts := strings.Split(strings.TrimSpace(fileLine), " // INLINE: ")
|
||||
|
||||
// compare
|
||||
if len(fileLineParts) == 2 {
|
||||
return fileLineParts[1], true
|
||||
} else if len(msgAndArgs) == 0 {
|
||||
t.Fatal("missing inline message")
|
||||
} else if msg, ok := msgAndArgs[0].(string); !ok {
|
||||
t.Fatal("invalid inline message")
|
||||
} else if value := fmt.Sprintf(msg, msgAndArgs[1:]...); len(value) == 0 {
|
||||
t.Fatal("missing inline message")
|
||||
} else {
|
||||
fileLines[line-1] = fmt.Sprintf("%s // INLINE: %s", fileLine, value)
|
||||
if err := os.WriteFile(file, []byte(strings.Join(fileLines, "\n")), 0644); err != nil {
|
||||
t.Fatal("failed to write inline", log.FError(err))
|
||||
}
|
||||
t.Errorf("wrote inline for %s:%d", file, line)
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func InlineInt(t *testing.T, skip int) (int, bool) {
|
||||
t.Helper()
|
||||
|
||||
if inline, ok := Inline(t, skip+1); !ok {
|
||||
return 0, false
|
||||
} else if value, err := strconv.Atoi(inline); err != nil {
|
||||
t.Fatal("failed to parse int", log.FError(err))
|
||||
return 0, false
|
||||
} else {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
|
||||
func InlineFloat64(t *testing.T, skip int) (float64, bool) {
|
||||
t.Helper()
|
||||
|
||||
if inline, ok := Inline(t, skip+1); !ok {
|
||||
return 0, false
|
||||
} else if value, err := strconv.ParseFloat(inline, 64); err != nil {
|
||||
t.Fatal("failed to parse int", log.FError(err))
|
||||
return 0, false
|
||||
} else {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
|
||||
func InlineJSON(t *testing.T, skip int, target interface{}) {
|
||||
t.Helper()
|
||||
|
||||
if inline, ok := Inline(t, skip+1); ok {
|
||||
if err := json.Unmarshal([]byte(inline), target); err != nil {
|
||||
t.Fatal("failed to unmarshal json", log.FError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
42
test/util/inline_test.go
Normal file
42
test/util/inline_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package keeltestutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
keeltestutil "github.com/foomo/keel/test/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInline(t *testing.T) {
|
||||
t.Run("read inline", func(t *testing.T) {
|
||||
value, ok := keeltestutil.Inline(t, 1) // INLINE: hello world
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "hello world", value)
|
||||
})
|
||||
|
||||
t.Run("read inline int", func(t *testing.T) {
|
||||
value, ok := keeltestutil.InlineInt(t, 1) // INLINE: 1
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1, value)
|
||||
})
|
||||
|
||||
t.Run("read inline float", func(t *testing.T) {
|
||||
value, ok := keeltestutil.InlineFloat64(t, 1) // INLINE: 1.5
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1.5, value)
|
||||
})
|
||||
|
||||
t.Run("read inline json", func(t *testing.T) {
|
||||
var x struct {
|
||||
Foo string `json:"foo"`
|
||||
}
|
||||
keeltestutil.InlineJSON(t, 1, &x) // INLINE: {"foo":"bar"}
|
||||
assert.Equal(t, "bar", x.Foo)
|
||||
})
|
||||
|
||||
t.Run("write inline", func(t *testing.T) {
|
||||
value, ok := keeltestutil.Inline(t, 1, "hello %s", "world") // INLINE: hello world
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "hello world", value)
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user