From 2b15280d03cb3eb78a2918957817ebb662283b19 Mon Sep 17 00:00:00 2001 From: Jan Novak Date: Thu, 7 May 2026 23:23:38 +0200 Subject: [PATCH] =?UTF-8?q?fix(go):=20exclude=20/api/version=20from=20pari?= =?UTF-8?q?ty=20diff=20=E2=80=94=20identity,=20not=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /api/version returns each binary's own tag/commit/build_date, which differs by design between independently built backends. Diffing it always produces a false positive. Drop it from allRoutes; the route remains reachable via `make parity ARGS="-route /api/version"`. Also remove the vestigial `build_meta` allowlist entry (Python returns the build dict as the top-level response body, not nested under build_meta, so the scrubber never matched anything). Co-Authored-By: Claude Opus 4.7 --- CHANGELOG.md | 2 +- .../2026-05-03-2349-go-backend-rewrite-progress.md | 1 + go/cmd/parity/main.go | 14 +++++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2764b9..856a8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ## 2026-05-07 22:55 CEST — feat(go): M5.4 — parity diff binary + `make parity` -- `go/cmd/parity/main.go`: new standalone binary that GETs `/api/version`, `/api/adults`, `/api/juniors`, `/api/payments` from both Python (:5001) and Go (:8080) backends, scrubs an allowlist (`render_time.total`, `build_meta`), and prints `cmp.Diff` for any remaining differences. Exits 0 on full match, 1 on diffs, 2 on fetch/parse errors — CI-friendly for M7.2. +- `go/cmd/parity/main.go`: new standalone binary that GETs `/api/adults`, `/api/juniors`, `/api/payments` from both Python (:5001) and Go (:8080) backends, scrubs an allowlist (`render_time.total`), and prints `cmp.Diff` for any remaining differences. Exits 0 on full match, 1 on diffs, 2 on fetch/parse errors — CI-friendly for M7.2. `/api/version` is excluded by design (returns binary identity — tag/commit/build_date — which differs between independently built backends); still accessible via `make parity ARGS="-route /api/version"`. - `go/cmd/parity/scrub_test.go`: 4 unit tests covering top-level delete, nested delete, missing path, and non-map parent. - `go/go.mod`: `github.com/google/go-cmp` promoted to direct dependency. - `Makefile`: `parity` target added (`.PHONY`, help, `cd go && go run ./cmd/parity`). diff --git a/docs/plans/2026-05-03-2349-go-backend-rewrite-progress.md b/docs/plans/2026-05-03-2349-go-backend-rewrite-progress.md index c231e42..ead897e 100644 --- a/docs/plans/2026-05-03-2349-go-backend-rewrite-progress.md +++ b/docs/plans/2026-05-03-2349-go-backend-rewrite-progress.md @@ -154,6 +154,7 @@ Goal: Go is the one true backend. (Add entries as you go. Format: `YYYY-MM-DD — short note`.) +- 2026-05-07 — `/api/version` excluded from parity diff by design. Each binary's tag/commit/build_date is identity, not a data contract — diffing it would always flag a diff between independently built backends. Route remains reachable via `make parity ARGS="-route /api/version"` for manual inspection. - 2026-05-04 — Plan approved. Versioning policy: latest stable for Go and all libs at the time M1 starts. Frontends explicitly allowed to diverge between Python and Go; only the JSON API contract is parity-locked. No reverse proxy — both backends run on different ports via `make web-py` / `make web-go`. - 2026-05-07 — M4 complete. Chose fakes-only unit tests (no live integration tests) and CSV-via-public-URL for attendance (no Sheets API auth required for read-only). golangci-lint gofumpt extra-rules differ slightly from standalone gofumpt; used `golangci-lint run --fix --enable-only gofumpt` to auto-resolve formatting. - 2026-05-04 — M1 complete. Dockerfile base changed from `distroless/static:nonroot` → `alpine:3` for debuggability (can tighten later). CLI dispatcher uses stdlib `flag`; module path `fuj-management/go`. golangci-lint v1 embedded gofumpt merges all imports into one group (no stdlib/local split) — accepted as the project style. diff --git a/go/cmd/parity/main.go b/go/cmd/parity/main.go index 643a9a2..f6ff8d0 100644 --- a/go/cmd/parity/main.go +++ b/go/cmd/parity/main.go @@ -14,13 +14,17 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" ) -var allRoutes = []string{"/api/version", "/api/adults", "/api/juniors", "/api/payments"} +// /api/version is intentionally excluded — it returns each binary's own build +// identity (tag/commit/build_date), which differs by design between independently +// built backends. Pass -route /api/version to inspect it manually. +var allRoutes = []string{"/api/adults", "/api/juniors", "/api/payments"} // defaultAllowlist holds dotted key paths to strip before diffing. -// These fields are expected to differ between backends (e.g. build tags, timing) -// or may appear on one side only. Today both are absent from the JSON — this is -// forward-compatible insurance for if either is added later. -var defaultAllowlist = []string{"render_time.total", "build_meta"} +// render_time.total is forward-compatible insurance: today it lives in the Jinja +// template context only (app.py inject_render_time) and is logged via +// middleware/timer.go on the Go side, so it isn't in any JSON response. If either +// side ever surfaces it under a render_time envelope, the scrubber handles it. +var defaultAllowlist = []string{"render_time.total"} func main() { pyURL := flag.String("py", "http://localhost:5001", "Python backend base URL")