Repository navigation
test(purchase): cover the 4-eyes fail-closed branch for an unresolvable creator - #390
Conversation
…le creator
Add TestManager_ApproveExecution_FourEyesOn_UnresolvableCreator_Denied
with two subtests driving checkDifferentApprover's tier-2 (email) path
through the real ApproveExecution entry point:
- GetUserEmailByID returning ("", nil), mirroring what the Postgres
store returns on pgx.ErrNoRows for a deleted creator row
- GetUserEmailByID returning an error
Both assert the approval is denied with the fail-closed error message
and that TransitionExecutionStatus is never called. Every existing
stub in this package returned a real email address, so neither branch
had any coverage; a refactor dropping or inverting the
creatorEmail == "" check would have silently let dual control pass for
any execution whose creator was deleted.
Confirmed the new test has teeth: dropped the creatorEmail == "" guard
in checkDifferentApprover and re-ran the empty-address subtest, which
panics on an unexpected TransitionExecutionStatus mock call instead of
denying. Restored the guard and confirmed both subtests pass again.
Closes #256
|
Warning Review limit reached
This review includes 1 billable file and costs up to $0.25.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 15 minutes for your next included review. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Your 36 included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour. Review configuration: ⚙️ Run configurationConfiguration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml Review profile: CHILL Plan: Essentials Run ID: 📒 Files selected for processing (1)
Comment |
|
@coderabbitai review |
|
|
Independent review + local verification: MERGE at ddc57e7. Test-only; two mutants killed (empty-creator guard disabled; lookup error swallowed with a fabricated identity); both subtests mirror the real store contract ("", nil on ErrNoRows, and a DB error). |
Summary
Adds regression tests for the fail-closed branches in the 4-eyes tier-2
(email) identity check when the purchase creator's account cannot be
resolved, closing the A16-001 gap described in #256.
Root cause
checkDifferentApprover's tier-2 (email) path resolves the creator's emailvia
GetUserEmailByIDand must deny the approval when that lookup errors orreturns an empty address (
internal/purchase/approvals.go:279-288). Neitherbranch had a test: every stub in
internal/purchasereturned a realaddress. The empty-address case is not hypothetical: the Postgres store
returns
("", nil)onpgx.ErrNoRows(
internal/config/store_postgres.go:1587-1588), so a deleted creator rowlands exactly there. Without a test on this branch, a refactor that drops or
inverts the
creatorEmail == ""check would leavestrings.EqualFold("", actorEmail)false for every actor, silently passingdual control for any execution whose creator was deleted, while the suite
stayed green.
Fix
Test-only change: added
TestManager_ApproveExecution_FourEyesOn_UnresolvableCreator_Deniedininternal/purchase/approvals_test.go, with two subtests on theApproveExecution(tier-2, email) call shape used by the SQS/token workerpath:
GetUserEmailByIDreturning("", nil)-- mirrors the real Postgresstore's result for a deleted creator row
GetUserEmailByIDreturning an errorBoth assert the fail-closed error message and
AssertNotCalledonTransitionExecutionStatus. No production code changed; the guard wasalready correct.
Regression test proof (mutation check)
Confirmed the new test has teeth by temporarily dropping the guard:
With that mutation, the empty-address subtest panics on an unexpected
TransitionExecutionStatusmock call (the approval proceeds instead ofbeing denied) instead of asserting cleanly:
Reverted the mutation and confirmed both subtests pass again (
3 passed in 1 packages, including the existing SQS-actor-equals-creator test in thesame
t.Rungroup).Verification
All run with
GOTOOLCHAIN=go1.26.6 GOWORK=offfrom the repo root:go build ./...- okgo vet ./internal/purchase/...- okgo test -race -short ./internal/purchase/...-316 passed in 1 packagesgo mod tidy -diff- emptygolangci-lint run --timeout=10m ./internal/purchase/...at the exactCI-pinned version (v2.10.1) -
0 issuesCloses #256