feat(12-01): record multipart uploads and match Winter upload URLs

- attach.PublicURL and (*File).URL build Winter File::getPath() URLs; the
  thumbnailer decodes webp via golang.org/x/image v0.46.0 and checks the
  image size from the header before decoding
- tide requests carry multipart parts (files beside the fixture pinned by
  sha256) encoded with the fixed MultipartBoundary, so PHP and Go receive
  byte-identical bodies
- tide masks the random partition, disk name and file id of url/thumb_url
  upload URLs while still diffing prefix, size, mode and extension, and
  NormalizePublications masks Carbon dates in the published album
This commit is contained in:
Jakub Zych
2026-10-02 11:33:42 +02:00
parent f9b7f2ea33
commit e06e0cc8bf
19 changed files with 870 additions and 34 deletions

View File

@@ -44,6 +44,10 @@ func ReplayFlow(ctx context.Context, flow Flow, cfg ReplayConfig) (Result, error
skipRest = true
continue
}
req, err = prepareRequest(req, cfg.BaseDir)
if err != nil {
return result, fmt.Errorf("tide: replay step %s: %w", step.ID, err)
}
got, err := doStep(ctx, client, cfg.Target, req, limit)
if err != nil {
return result, fmt.Errorf("tide: replay step %s: %w", step.ID, err)
@@ -71,7 +75,7 @@ func ReplayFlow(ctx context.Context, flow Flow, cfg ReplayConfig) (Result, error
skipRest = true
continue
}
sr.Diffs = append(sr.Diffs, compareStep(want, live.Response)...)
sr.Diffs = append(sr.Diffs, compareStepWith(want, live.Response, maskOptions{uploadPrefix: cfg.UploadPrefix})...)
if len(sr.Diffs) > 0 {
sr.OK = false
result.OK = false
@@ -90,6 +94,10 @@ func mustMemoryStore() *Store {
}
func compareStep(want Step, got Response) []Diff {
return compareStepWith(want, got, maskOptions{})
}
func compareStepWith(want Step, got Response, opts maskOptions) []Diff {
var diffs []Diff
if want.Response.Status != 0 && got.Status != want.Response.Status {
diffs = append(diffs, Diff{
@@ -99,6 +107,6 @@ func compareStep(want Step, got Response) []Diff {
})
}
diffs = append(diffs, compareHeaders(want.Response.Headers, got.Headers, want.Headers)...)
diffs = append(diffs, compareBodies(want.Response, got, want)...)
diffs = append(diffs, compareBodiesWith(want.Response, got, want, opts)...)
return diffs
}