feat(02-02): capture, scrub, and strictly diff stateful flows
Resolve named placeholders from a private variable store, mask dates and ids after shape checks, and keep comparing independent steps. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
68
tide/diff.go
68
tide/diff.go
@@ -5,21 +5,85 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func compareBodies(want, got Response) []Diff {
|
||||
func compareBodies(want, got Response, step Step) []Diff {
|
||||
wantJSON := isJSONContentType(want.Headers)
|
||||
gotJSON := isJSONContentType(got.Headers)
|
||||
if wantJSON && gotJSON {
|
||||
return diffJSON([]byte(want.Body), []byte(got.Body))
|
||||
wantRaw, wantDiffs := normalizeJSON([]byte(want.Body), step)
|
||||
gotRaw, gotDiffs := normalizeJSON([]byte(got.Body), step)
|
||||
diffs := append([]Diff{}, wantDiffs...)
|
||||
diffs = append(diffs, gotDiffs...)
|
||||
diffs = append(diffs, diffJSON(wantRaw, gotRaw)...)
|
||||
return diffs
|
||||
}
|
||||
return diffBytes([]byte(want.Body), []byte(got.Body))
|
||||
}
|
||||
|
||||
var globalCompareHeaders = []string{
|
||||
"Content-Type",
|
||||
"X-Total-Count",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Expose-Headers",
|
||||
}
|
||||
|
||||
var extraCompareHeaders = []string{
|
||||
"Cache-Control",
|
||||
"Pragma",
|
||||
"WWW-Authenticate",
|
||||
"Content-Disposition",
|
||||
"Link",
|
||||
}
|
||||
|
||||
var neverCompareHeaders = []string{
|
||||
"Date",
|
||||
"Server",
|
||||
"X-Request-Id",
|
||||
"X-Request-ID",
|
||||
"X-Correlation-Id",
|
||||
}
|
||||
|
||||
func compareHeaders(want, got, extra map[string]string) []Diff {
|
||||
allow := map[string]bool{}
|
||||
for _, n := range globalCompareHeaders {
|
||||
allow[http.CanonicalHeaderKey(n)] = true
|
||||
}
|
||||
for _, n := range extraCompareHeaders {
|
||||
allow[http.CanonicalHeaderKey(n)] = true
|
||||
}
|
||||
for n := range extra {
|
||||
allow[http.CanonicalHeaderKey(n)] = true
|
||||
}
|
||||
for _, n := range neverCompareHeaders {
|
||||
delete(allow, http.CanonicalHeaderKey(n))
|
||||
}
|
||||
var diffs []Diff
|
||||
for k, wv := range want {
|
||||
ck := http.CanonicalHeaderKey(k)
|
||||
if !allow[ck] {
|
||||
continue
|
||||
}
|
||||
gv := headerValue(got, k)
|
||||
if gv == wv {
|
||||
continue
|
||||
}
|
||||
if gv == "" {
|
||||
gv = "<missing>"
|
||||
}
|
||||
diffs = append(diffs, Diff{Path: "header." + ck, Expected: wv, Actual: gv})
|
||||
}
|
||||
return diffs
|
||||
}
|
||||
|
||||
func isJSONContentType(headers map[string]string) bool {
|
||||
ct := headerValue(headers, "Content-Type")
|
||||
if ct == "" {
|
||||
|
||||
Reference in New Issue
Block a user