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,10 +3,39 @@ 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)