From e97532ccd54a6aacc597e9db1648e580e2936132 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Fri, 24 Jul 2026 20:03:25 +0300 Subject: [PATCH] test(db): guard OpenProposal branch SQL against core.ProposalBranch drift (spec-wcr #1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenProposal allocates a proposal's id and branch in one INSERT, so it cannot call core.ProposalBranch — it spells the name out in SQL as BranchPrefix || id::text. That is a third derivation of the branch name sharing only the prefix constant with the canonical function; a change to how core.ProposalBranch formats the id would leave the SQL silently producing a different name. Pin the coupling with a pure unit test so the divergence fails loudly instead of in production. Part of spec-wcr (loose ends). Pure test, no behavior change. --- db/proposal_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/db/proposal_test.go b/db/proposal_test.go index 436ad87b6dd597a59e749facfc27550ac9d1eb55..795573286b5197dc17cb62841bea1396375a9703 100644 --- a/db/proposal_test.go +++ b/db/proposal_test.go @@ -3,11 +3,40 @@ package db import ( "context" "errors" + "strconv" "testing" "sourcecraft.dev/bigbes/sr-ht-spec/core" ) +// TestOpenProposalBranchMatchesCanonical guards the third derivation of a +// proposal's branch name. OpenProposal cannot call core.ProposalBranch: it +// allocates the id and writes the branch in one INSERT, so it spells the name +// out in SQL as `branch = BranchPrefix || id::text`. That replicates the +// formula core.ProposalBranch uses (prefix + decimal id) but not the function, +// and the two share only the prefix constant — so a change to how +// core.ProposalBranch formats the id (zero-padding, a different join) would +// leave the SQL silently producing a different name for the same row. +// +// This pins the coupling as a pure unit test: for representative ids, the SQL's +// concatenation must equal the canonical function. If it ever fails, the +// `$5::text || next.id::text` expression in OpenProposal must be updated in +// lockstep with core.ProposalBranch. +func TestOpenProposalBranchMatchesCanonical(t *testing.T) { + for _, id := range []int64{1, 2, 42, 100, 999999} { + fromSQL := BranchPrefix + strconv.FormatInt(id, 10) // BranchPrefix || id::text + canonical, err := core.ProposalBranch(id) + if err != nil { + t.Fatalf("core.ProposalBranch(%d): %v", id, err) + } + if fromSQL != canonical { + t.Errorf("id %d: OpenProposal SQL builds %q, core.ProposalBranch says %q — "+ + "the SQL concatenation in OpenProposal has drifted from the canonical function", + id, fromSQL, canonical) + } + } +} + func TestProposalOpenAndRead(t *testing.T) { s, _, cleanup := newTestStore(t) defer cleanup()