forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New helper methods for generating readable loggable strings (hashicor…
- Loading branch information
Showing
2 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package testhelpers | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestToMap(t *testing.T) { | ||
type s struct { | ||
A string `json:"a"` | ||
B []byte `json:"b"` | ||
C map[string]string `json:"c"` | ||
D string `json:"-"` | ||
} | ||
type args struct { | ||
in s | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want string | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "basic", | ||
args: args{s{A: "a", B: []byte("bytes"), C: map[string]string{"k": "v"}, D: "d"}}, | ||
want: "map[a:a b:277089d91c0bdf4f2e6862ba7e4a07605119431f5d13f726dd352b06f1b206a9 c:map[k:v]]", | ||
wantErr: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
m, err := ToMap(&tt.args.in) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("ToMap() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
got := fmt.Sprintf("%s", m) | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("ToMap() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |