@@ -9,36 +9,46 @@ import (
99
1010// compareJSONFields is the entry point for comparing two interface values
1111// handle string with special cases, map[string]interface{} and []interface{} or any other primitive type
12- func compareJSONFields (requestValue , cassetteValue interface {} ) bool {
12+ func compareJSONFields (requestValue , cassetteValue any , strict bool ) bool {
1313 switch requestValue := requestValue .(type ) {
1414 case string :
1515 return compareFieldsStrings (requestValue , cassetteValue .(string ))
16- case map [string ]interface {} :
17- return compareJSONBodies (requestValue , cassetteValue .(map [string ]interface {}) )
18- case []interface {} :
19- return compareSlices (requestValue , cassetteValue .([]interface {} ))
16+ case map [string ]any :
17+ return compareJSONBodies (requestValue , cassetteValue .(map [string ]any ), strict )
18+ case []any :
19+ return compareSlices (requestValue , cassetteValue .([]any ))
2020 default :
2121 return reflect .DeepEqual (requestValue , cassetteValue )
2222 }
2323}
2424
2525// compareJSONBodies compare two given maps that represent json bodies
2626// returns true if both json are equivalent
27- func compareJSONBodies (request , cassette map [string ]interface {} ) bool {
27+ func compareJSONBodies (request , cassette map [string ]any , strict bool ) bool {
2828 for key , requestValue := range request {
2929 cassetteValue , ok := cassette [key ]
3030 if ! ok {
31- // Actual request may contain a field that does not exist in cassette
32- // New fields can appear in requests with new api features
33- // We do not want to generate new cassettes for each new features
31+ if strict {
32+ return false
33+ }
34+
3435 continue
3536 }
3637
3738 if reflect .TypeOf (cassetteValue ) != reflect .TypeOf (requestValue ) {
3839 return false
3940 }
4041
41- if ! compareJSONFields (requestValue , cassetteValue ) {
42+ if ! compareJSONFields (requestValue , cassetteValue , strict ) {
43+ return false
44+ }
45+ }
46+
47+ for key , cassetteValue := range cassette {
48+ if _ , ok := request [key ]; ! ok && cassetteValue != nil {
49+ // Fails match if cassettes contains a field not in actual requests
50+ // Fields should not disappear from requests unless a sdk breaking change
51+ // We ignore if field is nil in cassette as it could be an old deprecated and unused field
4252 return false
4353 }
4454 }
@@ -141,7 +151,7 @@ func compareStringSlices(request, cassette []string) bool {
141151
142152// compareSlices compares two slices of interface{}
143153// in case of slice of map[string]interface{}, it will attempt to find a match in the other slice without taking into account the order
144- func compareSlices (request , cassette []interface {} ) bool {
154+ func compareSlices (request , cassette []any ) bool {
145155 if len (request ) != len (cassette ) {
146156 return false
147157 }
@@ -178,10 +188,31 @@ func compareSlices(request, cassette []interface{}) bool {
178188 }
179189
180190 return true
181- case map [string ]interface {} :
182- // compare list of maps[string]interface{} without order and without ignored keys
191+ case map [string ]any :
192+ // first compare assuming that the order is the same, tolerating missing keys in the cassette
183193 matched := 0
184194
195+ for i := range request {
196+ // cleanup ignored keys
197+ for _ , key := range BodyMatcherIgnore {
198+ removeKeyRecursive (request [i ].(map [string ]any ), key )
199+ }
200+
201+ for _ , key := range BodyMatcherIgnore {
202+ removeKeyRecursive (cassette [i ].(map [string ]any ), key )
203+ }
204+
205+ if compareJSONFields (request [i ], cassette [i ], false ) {
206+ matched ++
207+ }
208+ }
209+
210+ if matched == len (request ) {
211+ return true
212+ }
213+
214+ // if no match try to compare out of order
215+ matched = 0
185216 reqVisited := make ([]bool , len (request ))
186217 casVisited := make ([]bool , len (cassette ))
187218
@@ -190,22 +221,12 @@ func compareSlices(request, cassette []interface{}) bool {
190221 continue
191222 }
192223
193- // cleanup ignored keys
194- for _ , key := range BodyMatcherIgnore {
195- removeKeyRecursive (request [i ].(map [string ]interface {}), key )
196- }
197-
198224 for j := range cassette {
199225 if casVisited [j ] {
200226 continue
201227 }
202228
203- // cleanup ignored keys
204- for _ , key := range BodyMatcherIgnore {
205- removeKeyRecursive (cassette [j ].(map [string ]interface {}), key )
206- }
207-
208- if compareJSONFields (request [i ], cassette [j ]) {
229+ if compareJSONFields (request [i ], cassette [j ], true ) {
209230 matched ++
210231 reqVisited [i ] = true
211232 casVisited [j ] = true
0 commit comments