github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/test/must/json.go (about)

     1  package must
     2  
     3  import (
     4  	"encoding/json"
     5  	"github.com/v2pro/plz/test"
     6  	"github.com/v2pro/plz/test/testify/assert"
     7  	"runtime"
     8  	"reflect"
     9  	"strings"
    10  )
    11  
    12  func JsonEqual(expected string, actual interface{}) {
    13  	t := test.CurrentT()
    14  	test.Helper()
    15  	var expectedObj interface{}
    16  	err := json.Unmarshal([]byte(expected), &expectedObj)
    17  	if err != nil {
    18  		t.Fatal("expected json is invalid: " + err.Error())
    19  		return
    20  	}
    21  	var actualJson []byte
    22  	switch actualVal := actual.(type) {
    23  	case string:
    24  		actualJson = []byte(actualVal)
    25  	case []byte:
    26  		actualJson = actualVal
    27  	default:
    28  		actualJson, err = json.Marshal(actual)
    29  		t.Fatal("actual can not marshal to json: " + err.Error())
    30  		return
    31  	}
    32  	var actualObj interface{}
    33  	err = json.Unmarshal(actualJson, &actualObj)
    34  	if err != nil {
    35  		t.Log(string(actualJson))
    36  		t.Fatal("actual json is invalid: " + err.Error())
    37  		return
    38  	}
    39  	substituteVars(variables{}, expectedObj, actualObj)
    40  	if assert.Equal(t, expectedObj, actualObj) {
    41  		return
    42  	}
    43  	test.Helper()
    44  	_, file, line, ok := runtime.Caller(1)
    45  	if !ok {
    46  		t.Fatal("check failed")
    47  		return
    48  	}
    49  	t.Log(string(actualJson))
    50  	t.Fatal(test.ExtractFailedLines(file, line))
    51  }
    52  
    53  type variable struct {
    54  	value interface{}
    55  	subs  []func(value interface{})
    56  }
    57  
    58  type variables map[string]*variable
    59  
    60  func (vars variables) sub(varName string, sub func(value interface{})) {
    61  	v := vars[varName]
    62  	if v == nil {
    63  		vars[varName] = &variable{subs: []func(value interface{}){sub}}
    64  		return
    65  	}
    66  	if len(v.subs) == 0 {
    67  		sub(v.value)
    68  		return
    69  	}
    70  	v.subs = append(v.subs, sub)
    71  }
    72  
    73  func (vars variables) bind(varName string, varValue interface{}) {
    74  	v := vars[varName]
    75  	if v == nil {
    76  		vars[varName] = &variable{value: varValue}
    77  		return
    78  	}
    79  	if len(v.subs) > 0 {
    80  		for _, sub := range v.subs {
    81  			sub(varValue)
    82  		}
    83  		v.value = varValue
    84  		v.subs = nil
    85  		return
    86  	}
    87  	Equal(v.value, varValue)
    88  }
    89  
    90  func substituteVars(vars variables, expected interface{}, actual interface{}) {
    91  	switch reflect.TypeOf(expected).Kind() {
    92  	case reflect.Map:
    93  		if reflect.ValueOf(actual).Kind() != reflect.Map {
    94  			return
    95  		}
    96  		expectedVal := reflect.ValueOf(expected)
    97  		actualVal := reflect.ValueOf(actual)
    98  		keys := expectedVal.MapKeys()
    99  		for _, keyIter := range keys {
   100  			key := keyIter
   101  			varName, _ := key.Interface().(string)
   102  			if strings.HasPrefix(varName, "{") && strings.HasSuffix(varName, "}") {
   103  				vars.sub(varName, func(value interface{}) {
   104  					expectedElem := expectedVal.MapIndex(key)
   105  					actualElem := actualVal.MapIndex(reflect.ValueOf(value))
   106  					substituteVars(vars, expectedElem.Interface(), actualElem.Interface())
   107  					expectedVal.SetMapIndex(key, reflect.ValueOf(nil))
   108  					expectedVal.SetMapIndex(reflect.ValueOf(value), expectedElem)
   109  				})
   110  			}
   111  			expectedElem := expectedVal.MapIndex(key)
   112  			if !expectedElem.IsValid() {
   113  				continue
   114  			}
   115  			if reflect.TypeOf(expectedElem.Interface()).Kind() == reflect.String {
   116  				varName, _ = expectedElem.Interface().(string)
   117  				if varName == "{ANYTHING}" {
   118  					actualVal.SetMapIndex(key, reflect.ValueOf("{ANYTHING}"))
   119  					continue
   120  				}
   121  				actualElem := actualVal.MapIndex(key)
   122  				if !actualElem.IsValid() {
   123  					continue
   124  				}
   125  				if strings.HasPrefix(varName, "{") && strings.HasSuffix(varName, "}") {
   126  					expectedVal.SetMapIndex(key, actualElem)
   127  					vars.bind(varName, actualElem.Interface())
   128  					continue
   129  				}
   130  			}
   131  			actualElem := actualVal.MapIndex(key)
   132  			if !actualElem.IsValid() {
   133  				continue
   134  			}
   135  			substituteVars(vars, expectedElem.Interface(), actualElem.Interface())
   136  		}
   137  	case reflect.Slice:
   138  		if reflect.ValueOf(actual).Kind() != reflect.Slice {
   139  			return
   140  		}
   141  		expectedVal := reflect.ValueOf(expected)
   142  		actualVal := reflect.ValueOf(actual)
   143  		length := expectedVal.Len()
   144  		for i := 0; i < length; i++ {
   145  			actualElem := actualVal.Index(i)
   146  			if !actualElem.IsValid() {
   147  				continue
   148  			}
   149  			substituteVars(vars, expectedVal.Index(i).Interface(), actualElem.Interface())
   150  		}
   151  	}
   152  }