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  }