|
1 | 1 | package acctest |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "errors" |
5 | | - "fmt" |
6 | 4 | "net/url" |
7 | 5 | "reflect" |
8 | 6 | "sort" |
9 | 7 | "strings" |
10 | 8 | ) |
11 | 9 |
|
12 | | -var ( |
13 | | - ErrKeyIndex = errors.New("key index not found") |
14 | | - ErrValueIndex = errors.New("value index not found") |
15 | | - ErrNotComparable = errors.New("not comparable") |
16 | | - supportedMapKeys = []string{"key", "name"} |
17 | | - supportedMapValues = []string{"value", "values"} |
18 | | -) |
19 | | - |
20 | 10 | // compareJSONFields is the entry point for comparing two interface values |
21 | 11 | // handle string with special cases, map[string]interface{} and []interface{} or any other primitive type |
22 | 12 | func compareJSONFields(requestValue, cassetteValue interface{}) bool { |
@@ -201,115 +191,44 @@ func compareSlices(request, cassette []interface{}) bool { |
201 | 191 |
|
202 | 192 | return true |
203 | 193 | case map[string]interface{}: |
204 | | - // potential corner cases: |
205 | | - // - the maps have the same structure to identify key value pairs: we normalize and compare them |
206 | | - // - the maps have different structure: we recurse on compareJSONFields to individually compare them |
207 | | - requestMap, cassetteMap, err := normalizeMaps(request, cassette) |
208 | | - if err != nil { |
209 | | - for i, v := range request { |
210 | | - // remove keys that are not relevant for comparison |
211 | | - // like project_id, organization_id, etc. |
212 | | - for _, key := range BodyMatcherIgnore { |
213 | | - removeKeyRecursive(cassette[i].(map[string]interface{}), key) |
214 | | - removeKeyRecursive(v.(map[string]interface{}), key) |
215 | | - } |
216 | | - |
217 | | - if !compareJSONFields(v, cassette[i]) { |
218 | | - return false |
219 | | - } |
220 | | - } |
| 194 | + // compare list of maps[string]interface{} without order and without ignored keys |
| 195 | + matched := 0 |
221 | 196 |
|
222 | | - return true |
223 | | - } |
224 | | - |
225 | | - for k := range requestMap { |
226 | | - if _, ok := cassetteMap[k]; !ok { |
227 | | - return false |
228 | | - } |
| 197 | + reqVisited := make([]bool, len(request)) |
| 198 | + casVisited := make([]bool, len(cassette)) |
229 | 199 |
|
230 | | - if !compareJSONFields(requestMap[k], cassetteMap[k]) { |
231 | | - return false |
232 | | - } |
233 | | - } |
234 | | - |
235 | | - return true |
236 | | - default: |
237 | | - return reflect.DeepEqual(request, cassette) |
238 | | - } |
239 | | -} |
240 | | - |
241 | | -// normalizeMaps normalize 2 lists of simple maps |
242 | | -// from [{ "name": "foo", "value": "bar" }, { "name": "baz", "value": "qux" }] to { "foo": "bar", "baz": "qux" } |
243 | | -// returns KeyIndex and ValueIndex errors if the supported key or value are not found |
244 | | -func normalizeMaps(request, cassette []interface{}) (requestMap, cassetteMap map[string]interface{}, err error) { |
245 | | - requestMap = make(map[string]interface{}) |
246 | | - cassetteMap = make(map[string]interface{}) |
247 | | - |
248 | | - for _, v := range request { |
249 | | - rmap, ok := v.(map[string]interface{}) |
250 | | - if !ok { |
251 | | - return nil, nil, ErrNotComparable |
252 | | - } |
253 | | - |
254 | | - var keyIndex, key string |
255 | | - for _, key = range supportedMapKeys { |
256 | | - if keyIndex, ok = rmap[key].(string); ok { |
257 | | - break |
| 200 | + for i := range request { |
| 201 | + if reqVisited[i] { |
| 202 | + continue |
258 | 203 | } |
259 | | - } |
260 | 204 |
|
261 | | - if !ok { |
262 | | - return nil, nil, fmt.Errorf("%w: %s", ErrKeyIndex, key) |
263 | | - } |
264 | | - |
265 | | - var value interface{} |
266 | | - |
267 | | - var valueKey string |
268 | | - for _, valueKey = range supportedMapValues { |
269 | | - if value, ok = rmap[valueKey]; ok { |
270 | | - break |
| 205 | + // cleanup ignored keys |
| 206 | + for _, key := range BodyMatcherIgnore { |
| 207 | + removeKeyRecursive(request[i].(map[string]interface{}), key) |
271 | 208 | } |
272 | | - } |
273 | | - |
274 | | - if !ok { |
275 | | - return nil, nil, fmt.Errorf("%w: %s", ErrValueIndex, valueKey) |
276 | | - } |
277 | | - |
278 | | - requestMap[keyIndex] = value |
279 | | - } |
280 | 209 |
|
281 | | - for _, v := range cassette { |
282 | | - cmap, ok := v.(map[string]interface{}) |
283 | | - if !ok { |
284 | | - return nil, nil, ErrNotComparable |
285 | | - } |
286 | | - |
287 | | - var keyIndex, key string |
288 | | - for _, key = range supportedMapKeys { |
289 | | - if keyIndex, ok = cmap[key].(string); ok { |
290 | | - break |
291 | | - } |
292 | | - } |
| 210 | + for j := range cassette { |
| 211 | + if casVisited[j] { |
| 212 | + continue |
| 213 | + } |
293 | 214 |
|
294 | | - if !ok { |
295 | | - return nil, nil, fmt.Errorf("%w: %s", ErrKeyIndex, key) |
296 | | - } |
| 215 | + // cleanup ignored keys |
| 216 | + for _, key := range BodyMatcherIgnore { |
| 217 | + removeKeyRecursive(cassette[j].(map[string]interface{}), key) |
| 218 | + } |
297 | 219 |
|
298 | | - var value interface{} |
| 220 | + if compareJSONFields(request[i], cassette[j]) { |
| 221 | + matched++ |
| 222 | + reqVisited[i] = true |
| 223 | + casVisited[j] = true |
299 | 224 |
|
300 | | - var valueKey string |
301 | | - for _, valueKey = range supportedMapValues { |
302 | | - if value, ok = cmap[valueKey]; ok { |
303 | | - break |
| 225 | + break |
| 226 | + } |
304 | 227 | } |
305 | 228 | } |
306 | 229 |
|
307 | | - if !ok { |
308 | | - return nil, nil, fmt.Errorf("%w: %s", ErrValueIndex, valueKey) |
309 | | - } |
310 | | - |
311 | | - cassetteMap[keyIndex] = value |
| 230 | + return matched == len(request) |
| 231 | + default: |
| 232 | + return reflect.DeepEqual(request, cassette) |
312 | 233 | } |
313 | | - |
314 | | - return requestMap, cassetteMap, nil |
315 | 234 | } |
0 commit comments