test(multiplayer): refresh proposal cooldown evidence

This commit is contained in:
Josh Creek
2026-09-01 21:10:27 +01:00
parent b01db32908
commit e83f84f527
+11 -18
View File
@@ -531,14 +531,11 @@ func TestPostgreSQLProposalClaimAndResponseAreAtomic(t *testing.T) {
}
}
// TestPostgreSQLProposalDeclineRequeuesEveryParticipant is a real, severe
// bug this session found by reading the code, not by a failing test: no
// path anywhere transitioned a PROPOSED ticket back to QUEUED after a
// decline. A stranded ticket is invisible to the matcher (which only reads
// state='QUEUED'), still counts as the player's one active ticket (blocking
// a fresh queue_create), and is renewable forever by an ordinary heartbeat
// -- a player proposed a match with someone who declines had no way back
// into matchmaking without realising they had to manually cancel first.
// TestPostgreSQLProposalDeclineRequeuesEveryParticipant protects the durable
// decline boundary: every ticket returns to QUEUED, while the decliner's
// separate penalty prevents an immediate replacement queue ticket. Without
// the requeue, tickets are invisible to the matcher and remain trapped in
// PROPOSED despite the proposal having closed.
func TestPostgreSQLProposalDeclineRequeuesEveryParticipant(t *testing.T) {
db := openIntegrationPostgres(t)
applyIntegrationMigrations(t, db)
@@ -582,7 +579,7 @@ func TestPostgreSQLProposalDeclineRequeuesEveryParticipant(t *testing.T) {
t.Fatal(err)
}
if stateA != "QUEUED" {
t.Fatalf("decliner's own ticket state = %s, want QUEUED (no cooldown mechanism exists yet to justify leaving it stuck)", stateA)
t.Fatalf("decliner's own ticket state = %s, want QUEUED while cooldown is recorded separately", stateA)
}
if stateB != "QUEUED" {
t.Fatalf("uninvolved participant's ticket state = %s, want QUEUED -- they must not be stranded by someone else's decline", stateB)
@@ -606,15 +603,11 @@ func TestPostgreSQLProposalDeclineRequeuesEveryParticipant(t *testing.T) {
}
}
// TestPostgreSQLProposalTimeoutRequeuesEveryParticipant is the timeout
// sibling of the decline test above: a proposal that simply times out (no
// explicit decline, nobody ever responds) hits the exact same
// ProposalExpireSQL/ProposalParticipantExpireSQL path with the exact same
// gap -- neither ever touched queue_tickets, so this is the same severe
// stranding bug reached a different way. Uses GetProposal (the recovery/read
// path) rather than RespondToProposal, since a real client that just missed
// the expiry event and comes back later to check on it is exactly the
// scenario this path exists for.
// TestPostgreSQLProposalTimeoutRequeuesEveryParticipant protects the timeout
// sibling of the decline path: expiry must requeue every ticket and record a
// timeout cooldown for each participant who failed to respond. It uses
// GetProposal, the recovery/read path, to exercise a client returning after
// it missed the expiry event.
func TestPostgreSQLProposalTimeoutRequeuesEveryParticipant(t *testing.T) {
db := openIntegrationPostgres(t)
applyIntegrationMigrations(t, db)