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 }