fix(multiplayer): repair allocated compose verification

This commit is contained in:
Josh Creek
2026-09-04 16:38:10 +01:00
parent e6733bd6cb
commit d64920b0f9
11 changed files with 150 additions and 48 deletions
+6 -1
View File
@@ -81,9 +81,14 @@ func PromoteStoredAcceptedProposal(ctx context.Context, db *sql.DB, proposalID s
return fmt.Errorf("invalid stored proposal promotion arguments")
}
plan := AcceptedMatchPlan{MatchID: "match-" + proposalID, ProposalID: proposalID}
if err := db.QueryRowContext(ctx, StoredProposalMatchPlanSQL, proposalID).Scan(&plan.Region, &plan.Protocol, &plan.ArenaPath); err != nil {
// Casual proposals intentionally persist no arena path. Scan it as nullable
// here just as the in-transaction promotion path does, so an API retry after
// the atomic promotion does not turn a successful acceptance into a 503.
var arenaPath sql.NullString
if err := db.QueryRowContext(ctx, StoredProposalMatchPlanSQL, proposalID).Scan(&plan.Region, &plan.Protocol, &arenaPath); err != nil {
return err
}
plan.ArenaPath = arenaPath.String
rows, err := db.QueryContext(ctx, StoredProposalMatchPlayersSQL, proposalID)
if err != nil {
return err
@@ -778,6 +778,12 @@ func TestPostgreSQLProposalClaimAndResponseAreAtomic(t *testing.T) {
if accepted.State != domain.Accepted || accepted.Revision != 2 {
t.Fatalf("proposal did not close after unanimous acceptance: %+v", accepted)
}
// The API's post-commit recovery promoter must accept the nullable casual
// arena path left by the atomic response transaction and converge on the
// already-created match.
if err := PromoteStoredAcceptedProposal(ctx, db, proposal.ProposalID, now.Add(time.Second)); err != nil {
t.Fatalf("replay persisted casual promotion: %v", err)
}
var matchState string
if err := db.QueryRow(`SELECT state FROM matches WHERE match_id = 'match-proposal-integration'`).Scan(&matchState); err != nil {
t.Fatalf("atomic accepted match: %v", err)