runner/run.go
func (r *Runner) assertJSONIsEqualOrSubset(v1 interface{}, v2 interface{}) (bool, error) {
	if (v1 == nil) && (v2 == nil) {
		return true, nil
	}

	// check if both are non nil and that type matches
	if ((v1 == nil) != (v2 == nil)) ||
		(reflect.ValueOf(v1).Type() != reflect.ValueOf(v2).Type()) {
		return false, fmt.Errorf("types do not match")
	}

	switch v1.(type) {
	case map[string]interface{}:
		// recursively check maps
		// v2 is of same type as v1 as check in early return
		v2map := v2.(map[string]interface{})
		for objK, objV := range v1.(map[string]interface{}) {
			if v2map[objK] == nil {
				return false, fmt.Errorf("missing key '%s'", objK)
			}
			equalForKey, errForKey := r.assertJSONIsEqualOrSubset(objV, v2map[objK])
			if !equalForKey {
				return false, fmt.Errorf("mismatch for key '%s': %s", objK, errForKey)
			}
		}

		return true, nil
	default:
		// all non-map types must be deep equal
		if !reflect.DeepEqual(v1, v2) {
			return false, fmt.Errorf("values do not match - %s != %s", v1, v2)
		}
		return true, nil
	}
}
func (r *Runner) assertJSONIsEqualOrSubset(v1 interface{}, v2 interface{}) (bool, error) {
	if (v1 == nil) && (v2 == nil) {
		return true, nil
	}

	// check if both are non nil and that type matches
	if ((v1 == nil) != (v2 == nil)) ||
		(reflect.ValueOf(v1).Type() != reflect.ValueOf(v2).Type()) {
		return false, fmt.Errorf("types do not match")
	}

	switch v1.(type) {
	case map[string]interface{}:
		// recursively check maps
		// v2 is of same type as v1 as check in early return
		v2map := v2.(map[string]interface{})
		for objK, objV := range v1.(map[string]interface{}) {
			if v2map[objK] == nil {
				return false, fmt.Errorf("missing key '%s'", objK)
			}
			equalForKey, errForKey := r.assertJSONIsEqualOrSubset(objV, v2map[objK])
			if !equalForKey {
				return false, fmt.Errorf("mismatch for key '%s': %s", objK, errForKey)
			}
		}

		return true, nil
	default:
		// all non-map types must be deep equal
		if !reflect.DeepEqual(v1, v2) {
			return false, fmt.Errorf("values do not match - %s != %s", v1, v2)
		}
		return true, nil
	}
}
func ( r * Runner ) assertJSONIsEqualOrSubset ( v1 interface { } , v2 interface { } ) ( bool , error ) { if ( v1 == nil ) && ( v2 == nil ) { return true , nil } // check if both are non nil and that type matches if ( ( v1 == nil ) != ( v2 == nil ) ) || ( reflect . ValueOf ( v1 ) . Type ( ) != reflect . ValueOf ( v2 ) . Type ( ) ) { return false , fmt . Errorf ( " " ) } switch v1 . ( type ) { case map [ string ] interface { } : // recursively check maps // v2 is of same type as v1 as check in early return v2map := v2 . ( map [ string ] interface { } ) for objK , objV := range v1 . ( map [ string ] interface { } ) { if v2map [ objK ] == nil { return false , fmt . Errorf ( " " , objK ) } equalForKey , errForKey := r . assertJSONIsEqualOrSubset ( objV , v2map [ objK ] ) if ! equalForKey { return false , fmt . Errorf ( " " , objK , errForKey ) } } return true , nil default : // all non-map types must be deep equal if ! reflect . DeepEqual ( v1 , v2 ) { return false , fmt . Errorf ( " " , v1 , v2 ) } return true , nil } }
// assertJSONIsEqualOrSubset returns true if v1 and v2 are equal in value
// or if both are maps (of type map[string]interface{}) and v1 is a subset of v2, where
// all keys that are present in v1 are present with the same value in v2.
assertJSONIsEqualOrSubset returns true if v1 and v2 are equal in value or if both are maps ( of type map [ string ] interface {} ) and v1 is a subset of v2 where all keys that are present in v1 are present with the same value in v2 .