github.com/hashicorp/vault/sdk@v0.11.0/helper/testhelpers/output.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package testhelpers 5 6 import ( 7 "crypto/sha256" 8 "fmt" 9 "reflect" 10 11 "github.com/mitchellh/go-testing-interface" 12 "github.com/mitchellh/mapstructure" 13 ) 14 15 // ToMap renders an input value of any type as a map. This is intended for 16 // logging human-readable data dumps in test logs, so it uses the `json` 17 // tags on struct fields: this makes it easy to exclude `"-"` values that 18 // are typically not interesting, respect omitempty, etc. 19 // 20 // We also replace any []byte fields with a hash of their value. 21 // This is usually sufficient for test log purposes, and is a lot more readable 22 // than a big array of individual byte values like Go would normally stringify a 23 // byte slice. 24 func ToMap(in any) (map[string]any, error) { 25 temp := make(map[string]any) 26 cfg := &mapstructure.DecoderConfig{ 27 TagName: "json", 28 IgnoreUntaggedFields: true, 29 Result: &temp, 30 } 31 md, err := mapstructure.NewDecoder(cfg) 32 if err != nil { 33 return nil, err 34 } 35 err = md.Decode(in) 36 if err != nil { 37 return nil, err 38 } 39 40 // mapstructure doesn't call the DecodeHook for each field when doing 41 // struct->map conversions, but it does for map->map, so call it a second 42 // time to convert each []byte field. 43 out := make(map[string]any) 44 md2, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 45 Result: &out, 46 DecodeHook: func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { 47 if from.Kind() != reflect.Slice || from.Elem().Kind() != reflect.Uint8 { 48 return data, nil 49 } 50 b := data.([]byte) 51 return fmt.Sprintf("%x", sha256.Sum256(b)), nil 52 }, 53 }) 54 if err != nil { 55 return nil, err 56 } 57 err = md2.Decode(temp) 58 if err != nil { 59 return nil, err 60 } 61 62 return out, nil 63 } 64 65 // ToString renders its input using ToMap, and returns a string containing the 66 // result or an error if that fails. 67 func ToString(in any) string { 68 m, err := ToMap(in) 69 if err != nil { 70 return err.Error() 71 } 72 return fmt.Sprintf("%v", m) 73 } 74 75 // StringOrDie renders its input using ToMap, and returns a string containing the 76 // result. If rendering yields an error, calls t.Fatal. 77 func StringOrDie(t testing.T, in any) string { 78 t.Helper() 79 m, err := ToMap(in) 80 if err != nil { 81 t.Fatal(err) 82 } 83 return fmt.Sprintf("%v", m) 84 }