github.com/algorand/go-algorand-sdk@v1.24.0/test/utilities.go (about) 1 package test 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/hex" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io/ioutil" 11 "log" 12 "os" 13 "sort" 14 "strings" 15 16 sdk_json "github.com/algorand/go-algorand-sdk/encoding/json" 17 "github.com/algorand/go-algorand-sdk/encoding/msgpack" 18 ) 19 20 func VerifyResponse(expectedFile string, actual string) error { 21 jsonfile, err := os.Open(expectedFile) 22 if err != nil { 23 return err 24 } 25 fileBytes, err := ioutil.ReadAll(jsonfile) 26 if err != nil { 27 return err 28 } 29 30 var expectedString string 31 // No processing needed for json 32 if strings.HasSuffix(expectedFile, ".json") { 33 expectedString = string(fileBytes) 34 } 35 // Convert message pack to json for comparison 36 if strings.HasSuffix(expectedFile, ".base64") { 37 data, err := base64.StdEncoding.DecodeString(string(fileBytes)) 38 if err != nil { 39 return fmt.Errorf("failed to decode '%s' from base64: %v", expectedFile, err) 40 } 41 generic := make(map[string]interface{}) 42 err = msgpack.Decode(data, generic) 43 if err != nil { 44 return fmt.Errorf("failed to decode '%s' from message pack: %v", expectedFile, err) 45 } 46 expectedString = string(sdk_json.Encode(generic)) 47 } 48 49 err = EqualJson2(expectedString, actual) 50 if err != nil { 51 fmt.Printf("EXPECTED:\n%v\n", expectedString) 52 fmt.Printf("ACTUAL:\n%v\n", actual) 53 } 54 return err 55 } 56 57 // EqualJson2 compares two json strings. 58 // returns true if considered equal, false otherwise. 59 // The error returns the difference. 60 // For reference: j1 is the baseline, j2 is the test 61 func EqualJson2(j1, j2 string) (err error) { 62 var expected map[string]interface{} 63 json.Unmarshal([]byte(j1), &expected) 64 65 var actual map[string]interface{} 66 json.Unmarshal([]byte(j2), &actual) 67 68 err = recursiveCompare("root", expected, actual) 69 70 if err != nil { 71 log.Printf("expected:\n%s", j1) 72 log.Printf("actual:\n%s", j2) 73 } 74 return err 75 } 76 77 type ValueType int 78 79 const ( 80 OBJECT ValueType = iota 81 ARRAY 82 VALUE 83 NUMBER 84 BOOL 85 STRING 86 MISSING 87 ) 88 89 func getType(val interface{}) ValueType { 90 if val == nil { 91 return MISSING 92 } 93 switch val.(type) { 94 case map[string]interface{}: 95 return OBJECT 96 case []interface{}: 97 return ARRAY 98 case string: 99 return STRING 100 case bool: 101 return BOOL 102 case float64: 103 return NUMBER 104 case nil: 105 return MISSING 106 default: 107 return VALUE 108 } 109 } 110 111 // binaryOrStringEqual checks combinations of string / base64 decoded strings 112 // to see if the inputs are equal. 113 // The decoding process doesn't seem to distinguish between string and binary, but the encoding process 114 // does. So sometimes the string will be base64 encoded and need to compare against the decoded string 115 // value. 116 func binaryOrStringEqual(s1, s2 string) bool { 117 if s1 == s2 { 118 return true 119 } 120 if val, err := base64.StdEncoding.DecodeString(s1); err == nil { 121 if string(val) == s2 { 122 return true 123 } 124 } 125 if val, err := base64.StdEncoding.DecodeString(s2); err == nil { 126 if string(val) == s1 { 127 return true 128 } 129 } 130 return false 131 } 132 133 func sortArray(arr []interface{}, field string) { 134 sort.SliceStable(arr, func(i, j int) bool { 135 // literal type case 136 if field == "" { 137 vi := fmt.Sprintf("%v", arr[i]) 138 vj := fmt.Sprintf("%v", arr[j]) 139 return strings.Compare(vi, vj) < 0 140 } 141 // object case 142 vali := arr[i].(map[string]interface{})[field] 143 valj := arr[i].(map[string]interface{})[field] 144 vi := fmt.Sprintf("%v", vali) 145 vj := fmt.Sprintf("%v", valj) 146 return strings.Compare(vi, vj) < 0 147 }) 148 } 149 150 func getFirstField(ob interface{}) string { 151 if ob == nil || getType(ob) != OBJECT { 152 return "" 153 } 154 for k, _ := range ob.(map[string]interface{}) { 155 return k 156 } 157 return "" 158 } 159 160 func recursiveCompare(field string, expected, actual interface{}) error { 161 expectedType := getType(expected) 162 actualType := getType(actual) 163 164 // If both were nil, just return 165 if expectedType == MISSING && actualType == MISSING { 166 return nil 167 } 168 169 var keyType ValueType 170 171 if expectedType == MISSING || actualType == MISSING { 172 if expectedType == MISSING { 173 keyType = actualType 174 } 175 if actualType == MISSING { 176 keyType = expectedType 177 } 178 } else { 179 // If both are present, make sure they are the same 180 if expectedType != actualType { 181 return errors.New("Type mismatch") 182 } 183 keyType = expectedType 184 } 185 186 switch keyType { 187 case ARRAY: 188 var expectedArr []interface{} 189 var actualArr []interface{} 190 191 expectedSize := 0 192 if expectedType != MISSING { 193 expectedArr = expected.([]interface{}) 194 expectedSize = len(expectedArr) 195 } 196 actualSize := 0 197 if actualType != MISSING { 198 actualArr = actual.([]interface{}) 199 actualSize = len(actualArr) 200 } 201 202 if expectedSize != actualSize { 203 return fmt.Errorf("failed to match array sizes: %s", field) 204 } 205 206 //sortField := getFirstField(expected) 207 //sortArray(actualArr, sortField) 208 //sortArray(expectedArr, sortField) 209 210 // n^2 baby! Just make sure every element has a match somewhere in the other list. 211 // getting the sort function to work would be better... 212 var err error 213 for i := 0; i < expectedSize; i++ { 214 for j := 0; j < len(actualArr); j++ { 215 err = recursiveCompare(fmt.Sprintf("%s[%d]", field, i), expectedArr[i], actualArr[j]) 216 if err == nil { 217 break 218 } 219 } 220 if err != nil { 221 return err 222 } 223 } 224 return err 225 226 case OBJECT: 227 //log.Printf("%s{...} - object\n", field) 228 229 // Recursively compare each key value 230 // Pass nil's to the compare function to handle zero values on a type by type basis. 231 232 // Go happily creates complex zero value objects, so go ahead and recursively compare nil against defaults 233 234 // If they are both missing what are we even doing here. Return with no error. 235 if expectedType == MISSING && actualType == MISSING { 236 return nil 237 } 238 239 var expectedObject map[string]interface{} 240 var actualObject map[string]interface{} 241 242 keys := make(map[string]bool) 243 if expectedType != MISSING { 244 expectedObject = expected.(map[string]interface{}) 245 for k, _ := range expectedObject { 246 keys[k] = true 247 } 248 } 249 if actualType != MISSING { 250 actualObject = actual.(map[string]interface{}) 251 for k, _ := range actualObject { 252 keys[k] = true 253 } 254 } 255 for k, _ := range keys { 256 var err error 257 err = recursiveCompare(fmt.Sprintf("%s.%s", field, k), expectedObject[k], actualObject[k]) 258 if err != nil { 259 return err 260 } 261 } 262 263 case NUMBER: 264 // Compare numbers, if missing treat as zero 265 expectedNum := float64(0) 266 if expectedType != MISSING { 267 expectedNum = expected.(float64) 268 } 269 actualNum := float64(0) 270 if actualType != MISSING { 271 actualNum = actual.(float64) 272 } 273 //log.Printf("%s - number %f == %f\n", field, expectedNum, actualNum) 274 if expectedNum != actualNum { 275 return fmt.Errorf("failed to match field %s, %f != %f", field, expectedNum, actualNum) 276 } 277 278 case BOOL: 279 // Compare bools, if missing treat as false 280 expectedBool := false 281 if expectedType != MISSING { 282 expectedBool = expected.(bool) 283 } 284 actualBool := false 285 if actualType != MISSING { 286 actualBool = actual.(bool) 287 } 288 //log.Printf("%s - bool %t == %t\n", field, expectedBool, actualBool) 289 if expectedBool != actualBool { 290 return fmt.Errorf("failed to match field %s, %t != %t", field, expectedBool, actualBool) 291 } 292 293 case STRING: 294 // Compare strings, if missing treat as an empty string. 295 // Note: I think binary ends up in here, it may need some special handling. 296 expectedStr := "" 297 if expectedType != MISSING { 298 expectedStr = expected.(string) 299 } 300 actualStr := "" 301 if actualType != MISSING { 302 actualStr = actual.(string) 303 } 304 305 //log.Printf("%s - string %s == %s\n", field, expectedStr, actualStr) 306 if !binaryOrStringEqual(expectedStr, actualStr) { 307 return fmt.Errorf("failed to match field %s, %s != %s", field, expectedStr, actualStr) 308 } 309 310 default: 311 return fmt.Errorf("unhandled type %v at %s", keyType, field) 312 } 313 314 return nil 315 } 316 317 func sliceOfBytesEqual(expected [][]byte, actual [][]byte) error { 318 if len(expected) != len(actual) { 319 return fmt.Errorf("expected length (%d) does not match actual length (%d)", len(expected), len(actual)) 320 } 321 322 for i, expectedElement := range expected { 323 actualElement := actual[i] 324 if !bytes.Equal(expectedElement, actualElement) { 325 return fmt.Errorf("elements at index %d are unequal. Expected %s, got %s", i, hex.EncodeToString(expectedElement), hex.EncodeToString(actualElement)) 326 } 327 } 328 329 return nil 330 }