mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
test: cover PostgreSQL proposal transactions
This commit is contained in:
@@ -5,6 +5,7 @@ package store
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -159,6 +160,89 @@ func TestPostgreSQLAssignmentPersistenceIsPlayerScopedAndExpiryBound(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgreSQLProposalClaimAndResponseAreAtomic(t *testing.T) {
|
||||
db := openIntegrationPostgres(t)
|
||||
applyIntegrationMigrations(t, db)
|
||||
|
||||
now := time.Now().UTC().Truncate(time.Microsecond)
|
||||
ctx := context.Background()
|
||||
for _, player := range []string{"proposal-player-a", "proposal-player-b"} {
|
||||
if _, err := db.ExecContext(ctx, `INSERT INTO identities (player_id, steam_id) VALUES ($1, $1)`, player); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for i, player := range []string{"proposal-player-a", "proposal-player-b"} {
|
||||
if _, err := db.ExecContext(ctx, `INSERT INTO queue_tickets (ticket_id, player_id, playlist, state, client_build, protocol_version, enqueued_at, expires_at) VALUES ($1, $2, 'casual', 'QUEUED', 'integration-build', 1, $3, $4)`, fmt.Sprintf("proposal-ticket-%d", i), player, now, now.Add(time.Minute)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
proposal, err := domain.NewProposal("proposal-integration", domain.Casual, []string{"proposal-player-a", "proposal-player-b"}, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := CreateProposal(ctx, db, proposal, map[string]string{"proposal-player-a": "proposal-ticket-0", "proposal-player-b": "proposal-ticket-1"}, now); err != nil {
|
||||
t.Fatalf("create proposal: %v", err)
|
||||
}
|
||||
var proposed int
|
||||
if err := db.QueryRow(`SELECT count(*) FROM queue_tickets WHERE state = 'PROPOSED'`).Scan(&proposed); err != nil || proposed != 2 {
|
||||
t.Fatalf("proposed queue tickets = %d, err = %v", proposed, err)
|
||||
}
|
||||
recovered, err := GetProposal(ctx, db, "proposal-player-a", proposal.ProposalID, now)
|
||||
if err != nil {
|
||||
t.Fatalf("recover proposal: %v", err)
|
||||
}
|
||||
if len(recovered.Participants) != 2 || recovered.Revision != 0 {
|
||||
t.Fatalf("unexpected recovered proposal: %+v", recovered)
|
||||
}
|
||||
accepted, err := RespondToProposal(ctx, db, "proposal-player-a", proposal.ProposalID, "proposal-response-a-0001", true, 0, now)
|
||||
if err != nil {
|
||||
t.Fatalf("first proposal acceptance: %v", err)
|
||||
}
|
||||
if accepted.Revision != 1 || accepted.State != domain.Open {
|
||||
t.Fatalf("unexpected first acceptance: %+v", accepted)
|
||||
}
|
||||
accepted, err = RespondToProposal(ctx, db, "proposal-player-b", proposal.ProposalID, "proposal-response-b-0001", true, 1, now)
|
||||
if err != nil {
|
||||
t.Fatalf("second proposal acceptance: %v", err)
|
||||
}
|
||||
if accepted.State != domain.Accepted || accepted.Revision != 2 {
|
||||
t.Fatalf("proposal did not close after unanimous acceptance: %+v", accepted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgreSQLProposalCreationRollsBackPartialClaims(t *testing.T) {
|
||||
db := openIntegrationPostgres(t)
|
||||
applyIntegrationMigrations(t, db)
|
||||
now := time.Now().UTC().Truncate(time.Microsecond)
|
||||
ctx := context.Background()
|
||||
if _, err := db.ExecContext(ctx, `INSERT INTO identities (player_id, steam_id) VALUES ('rollback-player', 'rollback-steam')`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, `INSERT INTO queue_tickets (ticket_id, player_id, playlist, state, client_build, protocol_version, enqueued_at, expires_at) VALUES ('rollback-ticket', 'rollback-player', 'casual', 'QUEUED', 'integration-build', 1, $1, $2)`, now, now.Add(time.Minute)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proposal, err := domain.NewProposal("rollback-proposal", domain.Casual, []string{"rollback-player", "missing-player"}, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := CreateProposal(ctx, db, proposal, map[string]string{"rollback-player": "rollback-ticket"}, now); err == nil {
|
||||
t.Fatal("proposal with missing ticket mapping was accepted")
|
||||
}
|
||||
var proposals, participants, proposed int
|
||||
if err := db.QueryRow(`SELECT count(*) FROM proposals WHERE proposal_id = 'rollback-proposal'`).Scan(&proposals); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.QueryRow(`SELECT count(*) FROM proposal_participants WHERE proposal_id = 'rollback-proposal'`).Scan(&participants); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.QueryRow(`SELECT count(*) FROM queue_tickets WHERE ticket_id = 'rollback-ticket' AND state = 'PROPOSED'`).Scan(&proposed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if proposals != 0 || participants != 0 || proposed != 0 {
|
||||
t.Fatalf("partial proposal claim was not rolled back: proposals=%d participants=%d proposed=%d", proposals, participants, proposed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgreSQLMigrationsAreForwardExecutable(t *testing.T) {
|
||||
db := openIntegrationPostgres(t)
|
||||
applyIntegrationMigrations(t, db)
|
||||
|
||||
Reference in New Issue
Block a user