2020-07-15 20:10:54 +00:00
|
|
|
package assertions
|
2020-07-13 15:19:52 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
2020-08-13 16:22:25 +00:00
|
|
|
|
2020-10-03 01:51:58 +00:00
|
|
|
"github.com/d4l3k/messagediff"
|
2020-08-13 16:22:25 +00:00
|
|
|
"github.com/sirupsen/logrus/hooks/test"
|
2020-07-13 15:19:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// AssertionTestingTB exposes enough testing.TB methods for assertions.
|
|
|
|
type AssertionTestingTB interface {
|
|
|
|
Errorf(format string, args ...interface{})
|
|
|
|
Fatalf(format string, args ...interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type assertionLoggerFn func(string, ...interface{})
|
|
|
|
|
|
|
|
// Equal compares values using comparison operator.
|
2020-07-20 01:36:28 +00:00
|
|
|
func Equal(loggerFn assertionLoggerFn, expected, actual interface{}, msg ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
if expected != actual {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Values are not equal", msg...)
|
2020-07-13 17:20:18 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
2020-07-21 00:47:53 +00:00
|
|
|
loggerFn("%s:%d %s, want: %[4]v (%[4]T), got: %[5]v (%[5]T)", filepath.Base(file), line, errMsg, expected, actual)
|
2020-07-13 15:19:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-25 16:53:41 +00:00
|
|
|
// NotEqual compares values using comparison operator.
|
|
|
|
func NotEqual(loggerFn assertionLoggerFn, expected, actual interface{}, msg ...interface{}) {
|
|
|
|
if expected == actual {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Values are equal", msg...)
|
2020-07-25 16:53:41 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
|
|
|
loggerFn("%s:%d %s, both values are equal: %[4]v (%[4]T)", filepath.Base(file), line, errMsg, expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 15:19:52 +00:00
|
|
|
// DeepEqual compares values using DeepEqual.
|
2020-07-20 01:36:28 +00:00
|
|
|
func DeepEqual(loggerFn assertionLoggerFn, expected, actual interface{}, msg ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Values are not equal", msg...)
|
2020-07-13 17:20:18 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
2020-10-03 01:51:58 +00:00
|
|
|
diff, _ := messagediff.PrettyDiff(expected, actual)
|
|
|
|
loggerFn("%s:%d %s, want: %#v, got: %#v, diff: %s", filepath.Base(file), line, errMsg, expected, actual, diff)
|
2020-07-13 15:19:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-24 10:14:13 +00:00
|
|
|
// DeepNotEqual compares values using DeepEqual.
|
|
|
|
func DeepNotEqual(loggerFn assertionLoggerFn, expected, actual interface{}, msg ...interface{}) {
|
|
|
|
if reflect.DeepEqual(expected, actual) {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Values are equal", msg...)
|
2020-08-24 10:14:13 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
|
|
|
loggerFn("%s:%d %s, want: %#v, got: %#v", filepath.Base(file), line, errMsg, expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 15:19:52 +00:00
|
|
|
// NoError asserts that error is nil.
|
2020-07-20 01:36:28 +00:00
|
|
|
func NoError(loggerFn assertionLoggerFn, err error, msg ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
if err != nil {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Unexpected error", msg...)
|
2020-07-13 17:20:18 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
2020-07-13 15:19:52 +00:00
|
|
|
loggerFn("%s:%d %s: %v", filepath.Base(file), line, errMsg, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrorContains asserts that actual error contains wanted message.
|
2020-07-20 01:36:28 +00:00
|
|
|
func ErrorContains(loggerFn assertionLoggerFn, want string, err error, msg ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), want) {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Expected error not returned", msg...)
|
2020-07-13 17:20:18 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
2020-07-13 15:19:52 +00:00
|
|
|
loggerFn("%s:%d %s, got: %v, want: %s", filepath.Base(file), line, errMsg, err, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NotNil asserts that passed value is not nil.
|
2020-07-20 01:36:28 +00:00
|
|
|
func NotNil(loggerFn assertionLoggerFn, obj interface{}, msg ...interface{}) {
|
2020-09-28 21:32:58 +00:00
|
|
|
if isNil(obj) {
|
2020-08-24 15:15:39 +00:00
|
|
|
errMsg := parseMsg("Unexpected nil value", msg...)
|
2020-07-13 17:20:18 +00:00
|
|
|
_, file, line, _ := runtime.Caller(2)
|
2020-07-13 15:19:52 +00:00
|
|
|
loggerFn("%s:%d %s", filepath.Base(file), line, errMsg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-28 21:32:58 +00:00
|
|
|
// isNil checks that underlying value of obj is nil.
|
|
|
|
func isNil(obj interface{}) bool {
|
|
|
|
if obj == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
value := reflect.ValueOf(obj)
|
|
|
|
switch value.Kind() {
|
|
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
|
|
|
|
return value.IsNil()
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-08-13 16:22:25 +00:00
|
|
|
// LogsContain checks whether a given substring is a part of logs. If flag=false, inverse is checked.
|
|
|
|
func LogsContain(loggerFn assertionLoggerFn, hook *test.Hook, want string, flag bool, msg ...interface{}) {
|
|
|
|
_, file, line, _ := runtime.Caller(2)
|
|
|
|
entries := hook.AllEntries()
|
|
|
|
var logs []string
|
|
|
|
match := false
|
|
|
|
for _, e := range entries {
|
|
|
|
msg, err := e.String()
|
|
|
|
if err != nil {
|
|
|
|
loggerFn("%s:%d Failed to format log entry to string: %v", filepath.Base(file), line, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if strings.Contains(msg, want) {
|
|
|
|
match = true
|
|
|
|
}
|
|
|
|
for _, field := range e.Data {
|
|
|
|
fieldStr, ok := field.(string)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if strings.Contains(fieldStr, want) {
|
|
|
|
match = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logs = append(logs, msg)
|
|
|
|
}
|
|
|
|
var errMsg string
|
|
|
|
if flag && !match {
|
|
|
|
errMsg = parseMsg("Expected log not found", msg...)
|
|
|
|
} else if !flag && match {
|
|
|
|
errMsg = parseMsg("Unexpected log found", msg...)
|
|
|
|
}
|
|
|
|
if errMsg != "" {
|
|
|
|
loggerFn("%s:%d %s: %v\nSearched logs:\n%v", filepath.Base(file), line, errMsg, want, logs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-20 01:36:28 +00:00
|
|
|
func parseMsg(defaultMsg string, msg ...interface{}) string {
|
|
|
|
if len(msg) >= 1 {
|
|
|
|
msgFormat, ok := msg[0].(string)
|
|
|
|
if !ok {
|
|
|
|
return defaultMsg
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(msgFormat, msg[1:]...)
|
2020-07-13 15:19:52 +00:00
|
|
|
}
|
2020-07-20 01:36:28 +00:00
|
|
|
return defaultMsg
|
2020-07-13 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
2020-07-15 20:10:54 +00:00
|
|
|
// TBMock exposes enough testing.TB methods for assertions.
|
|
|
|
type TBMock struct {
|
2020-07-13 15:19:52 +00:00
|
|
|
ErrorfMsg string
|
|
|
|
FatalfMsg string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errorf writes testing logs to ErrorfMsg.
|
2020-07-15 20:10:54 +00:00
|
|
|
func (tb *TBMock) Errorf(format string, args ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
tb.ErrorfMsg = fmt.Sprintf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fatalf writes testing logs to FatalfMsg.
|
2020-07-15 20:10:54 +00:00
|
|
|
func (tb *TBMock) Fatalf(format string, args ...interface{}) {
|
2020-07-13 15:19:52 +00:00
|
|
|
tb.FatalfMsg = fmt.Sprintf(format, args...)
|
|
|
|
}
|