82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package testhelpers
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/mitchellh/go-testing-interface"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
// ToMap renders an input value of any type as a map. This is intended for
|
|
// logging human-readable data dumps in test logs, so it uses the `json`
|
|
// tags on struct fields: this makes it easy to exclude `"-"` values that
|
|
// are typically not interesting, respect omitempty, etc.
|
|
//
|
|
// We also replace any []byte fields with a hash of their value.
|
|
// This is usually sufficient for test log purposes, and is a lot more readable
|
|
// than a big array of individual byte values like Go would normally stringify a
|
|
// byte slice.
|
|
func ToMap(in any) (map[string]any, error) {
|
|
temp := make(map[string]any)
|
|
cfg := &mapstructure.DecoderConfig{
|
|
TagName: "json",
|
|
IgnoreUntaggedFields: true,
|
|
Result: &temp,
|
|
}
|
|
md, err := mapstructure.NewDecoder(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = md.Decode(in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// mapstructure doesn't call the DecodeHook for each field when doing
|
|
// struct->map conversions, but it does for map->map, so call it a second
|
|
// time to convert each []byte field.
|
|
out := make(map[string]any)
|
|
md2, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
Result: &out,
|
|
DecodeHook: func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) {
|
|
if from.Kind() != reflect.Slice || from.Elem().Kind() != reflect.Uint8 {
|
|
return data, nil
|
|
}
|
|
b := data.([]byte)
|
|
return fmt.Sprintf("%x", sha256.Sum256(b)), nil
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = md2.Decode(temp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// ToString renders its input using ToMap, and returns a string containing the
|
|
// result or an error if that fails.
|
|
func ToString(in any) string {
|
|
m, err := ToMap(in)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return fmt.Sprintf("%v", m)
|
|
}
|
|
|
|
// StringOrDie renders its input using ToMap, and returns a string containing the
|
|
// result. If rendering yields an error, calls t.Fatal.
|
|
func StringOrDie(t testing.T, in any) string {
|
|
t.Helper()
|
|
m, err := ToMap(in)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return fmt.Sprintf("%v", m)
|
|
}
|