fix(02-03): re-record on --update and honor case capture rules

Ordered PHP self-replay needs regenerated share tokens captured into
vars, and --update must overwrite already-recorded route fixtures.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-17 13:00:14 +02:00
parent 5b947c7454
commit 39ddf787ce
2 changed files with 70 additions and 1 deletions

View File

@@ -65,6 +65,7 @@ type RouteCase struct {
Headers map[string]string `yaml:"headers,omitempty"`
Fixture string `yaml:"fixture,omitempty"`
Request *Request `yaml:"request,omitempty"`
Capture []CaptureRule `yaml:"capture,omitempty"`
}
// ManifestConfig drives recording or replay of a manifest.
@@ -296,7 +297,7 @@ func RecordManifest(ctx context.Context, m Manifest, cfg ManifestConfig) (Covera
break
}
}
if allRecorded {
if allRecorded && !cfg.Update {
cov.markRecorded(route)
continue
}
@@ -396,6 +397,7 @@ func recordRoute(ctx context.Context, route Route, cfg ManifestConfig) error {
ID: c.ID,
RouteID: route.ID,
Request: caseRequest(route, c),
Capture: c.Capture,
Normalize: route.Normalize,
Headers: mergeHeaders(route.Headers, c.Headers),
}},

View File

@@ -9,6 +9,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
)
@@ -406,3 +407,69 @@ func TestRecordSeedResolvesRelativeSpec(t *testing.T) {
t.Fatalf("existing seed must not re-hit PHP: %d", hits)
}
}
func TestRecordManifestUpdateRerecords(t *testing.T) {
fixtures := t.TempDir()
hits := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits++
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"n":` + strconv.Itoa(hits) + `}`))
}))
t.Cleanup(srv.Close)
m := Manifest{
Version: 1,
AuthGroups: []string{"public"},
Routes: []Route{{
ID: "GET /x public", Method: http.MethodGet, Path: "/x",
AuthGroup: "public", Status: StatusPending,
Cases: []RouteCase{{
ID: "ok", Status: 200, Fixture: "routes/x.yaml",
Request: &Request{Method: http.MethodGet, Path: "/x"},
}},
}},
}
if _, err := RecordManifest(context.Background(), m, ManifestConfig{Target: srv.URL, Fixtures: fixtures, Resume: true}); err != nil {
t.Fatal(err)
}
if _, err := RecordManifest(context.Background(), m, ManifestConfig{Target: srv.URL, Fixtures: fixtures, Resume: true, Update: true}); err != nil {
t.Fatal(err)
}
if hits != 2 {
t.Fatalf("update must recapture, hits=%d", hits)
}
}
func TestRouteCaseCaptureUpdatesStore(t *testing.T) {
fixtures := t.TempDir()
vars := filepath.Join(t.TempDir(), "vars.yaml")
store, err := OpenStore(vars)
if err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"data":{"token":"shareTokValue99"}}`))
}))
t.Cleanup(srv.Close)
m := Manifest{
Version: 1,
AuthGroups: []string{"public"},
Routes: []Route{{
ID: "POST /share public", Method: http.MethodPost, Path: "/share",
AuthGroup: "public", Status: StatusPending,
Cases: []RouteCase{{
ID: "ok", Status: 200, Fixture: "routes/share.yaml",
Request: &Request{Method: http.MethodPost, Path: "/share"},
Capture: []CaptureRule{{From: "response.json", Path: "$.data.token", As: "share:collection"}},
}},
}},
}
if _, err := RecordManifest(context.Background(), m, ManifestConfig{Target: srv.URL, Fixtures: fixtures, Store: store}); err != nil {
t.Fatal(err)
}
got, ok := store.Get("share:collection")
if !ok || got != "shareTokValue99" {
t.Fatalf("capture: ok=%v val=%q", ok, got)
}
}