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:
80
tide/flow_test.go
Normal file
80
tide/flow_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package tide
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFlowMismatchContinuesAndCaptureSkipsRest(t *testing.T) {
|
||||
var hits atomic.Int32
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
hits.Add(1)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch r.URL.Path {
|
||||
case "/one":
|
||||
_, _ = w.Write([]byte(`{"data":"no"}`))
|
||||
default:
|
||||
_, _ = w.Write([]byte(`{"data":"ok"}`))
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
mismatch := Flow{
|
||||
Version: 1,
|
||||
Name: "mismatch-continue",
|
||||
Steps: []Step{
|
||||
{
|
||||
ID: "a",
|
||||
Request: Request{Method: http.MethodGet, Path: "/one"},
|
||||
Response: Response{Status: 200, Headers: jsonCT(), Body: Body(`{"data":"yes"}`)},
|
||||
},
|
||||
{
|
||||
ID: "b",
|
||||
Request: Request{Method: http.MethodGet, Path: "/two"},
|
||||
Response: Response{Status: 200, Headers: jsonCT(), Body: Body(`{"data":"ok"}`)},
|
||||
},
|
||||
},
|
||||
}
|
||||
res, err := ReplayFlow(context.Background(), mismatch, ReplayConfig{Target: srv.URL})
|
||||
if err == nil {
|
||||
t.Fatal("mismatch must error")
|
||||
}
|
||||
if hits.Load() != 2 {
|
||||
t.Fatalf("mismatch should continue, hits=%d", hits.Load())
|
||||
}
|
||||
if len(res.Steps) != 2 || res.Steps[1].Skipped || !res.Steps[1].OK {
|
||||
t.Fatalf("step b should run and pass: %+v", res.Steps)
|
||||
}
|
||||
|
||||
hits.Store(0)
|
||||
captureFail := Flow{
|
||||
Version: 1,
|
||||
Name: "capture-skip",
|
||||
Steps: []Step{
|
||||
{
|
||||
ID: "a",
|
||||
Request: Request{Method: http.MethodGet, Path: "/two"},
|
||||
Response: Response{Status: 200, Headers: jsonCT(), Body: Body(`{"data":"ok"}`)},
|
||||
Capture: []CaptureRule{{From: "response.json", Path: "$.token", As: "jwt:alice"}},
|
||||
},
|
||||
{
|
||||
ID: "b",
|
||||
Request: Request{Method: http.MethodGet, Path: "/two"},
|
||||
Response: Response{Status: 200, Headers: jsonCT(), Body: Body(`{"data":"ok"}`)},
|
||||
},
|
||||
},
|
||||
}
|
||||
res, err = ReplayFlow(context.Background(), captureFail, ReplayConfig{Target: srv.URL})
|
||||
if err == nil {
|
||||
t.Fatal("failed capture must error")
|
||||
}
|
||||
if hits.Load() != 1 {
|
||||
t.Fatalf("capture fail should skip rest, hits=%d", hits.Load())
|
||||
}
|
||||
if len(res.Steps) != 2 || !res.Steps[1].Skipped {
|
||||
t.Fatalf("step b should skip: %+v", res.Steps)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user