Files
CosmicClash/server/store/assignment_sql_test.go
T

91 lines
4.8 KiB
Go

package store
import (
"testing"
"time"
"github.com/cosmic-clash/cosmic-clash/server/domain"
)
func TestAssignmentSQLBindsPlayerAndPreservesIdenticalReplay(t *testing.T) {
for query, fragments := range map[string][]string{
AssignmentUpsertSQL: {"ON CONFLICT (match_id, player_id)", "WHERE assignments.allocation_id = EXCLUDED.allocation_id", "join_authorisation", "manifest_digest"},
AssignmentSelectSQL: {"a.match_id = $1", "a.player_id = $2", "a.expires_at > $3", "JOIN matches", "ASSIGNMENT_READY", "m.server_id = a.server_id"},
AssignmentExpectedRosterSQL: {"match_participants", "identities", "allocations", "m.allocation_id = $2", "m.server_id = $3", "a.state = 'ALLOCATED'", "participation_active", "FOR UPDATE OF mp"},
} {
for _, fragment := range fragments {
if !contains(query, fragment) {
t.Fatalf("query %q missing %q", query, fragment)
}
}
}
}
func TestAssignmentBatchRejectsMixedAuthorityAndDuplicateSlots(t *testing.T) {
base := DurableAssignment{MatchID: "match-1", PlayerID: "player-1", AllocationID: "allocation-1", ServerID: "server-1", Slot: 0, Region: "EU", ClientBuild: "build-1", ProtocolVersion: 1, Transport: "enet", Endpoint: "127.0.0.1:1", JoinAuthorisation: "join-1", ManifestDigest: []byte("digest"), ExpiresAt: time.Unix(1001, 0), Revision: 1}
other := base
other.PlayerID = "player-2"
other.JoinAuthorisation = "join-2"
if err := validateAssignmentBatch([]DurableAssignment{base, other}); err == nil {
t.Fatal("duplicate slot accepted")
}
other.Slot = 3
other.ServerID = "server-2"
if err := validateAssignmentBatch([]DurableAssignment{base, other}); err == nil {
t.Fatal("mixed server batch accepted")
}
other.ServerID = base.ServerID
if err := validateAssignmentBatch([]DurableAssignment{base, other}); err != nil {
t.Fatalf("valid assignment batch rejected: %v", err)
}
}
func TestAssignmentStoreRejectsInvalidRecoveryAndManifestInputs(t *testing.T) {
if _, err := GetAssignment(nil, nil, "player-1", "match-1", time.Unix(1000, 0)); err == nil {
t.Fatal("nil database accepted")
}
if err := SaveAssignment(nil, nil, DurableAssignment{MatchID: "match-1", PlayerID: "player-1", ExpiresAt: time.Unix(1000, 0)}); err == nil {
t.Fatal("incomplete assignment accepted")
}
if err := validateDurableAssignment(DurableAssignment{MatchID: "match-1", PlayerID: "player-1", AllocationID: "allocation-1", ServerID: "server-1", Slot: 6, Region: "EU", ClientBuild: "build-1", ProtocolVersion: 1, Transport: "enet", Endpoint: "127.0.0.1:1", JoinAuthorisation: "join", ManifestDigest: []byte("digest"), ExpiresAt: time.Unix(1001, 0)}); err == nil {
t.Fatal("out-of-range slot accepted")
}
}
func TestAssignmentStoreRejectsInvalidBatches(t *testing.T) {
if err := SaveAssignments(nil, nil, nil); err == nil {
t.Fatal("nil database/empty batch accepted")
}
if err := SaveAssignments(nil, nil, []DurableAssignment{{MatchID: "match-1", PlayerID: "player-1"}}); err == nil {
t.Fatal("invalid assignment batch accepted")
}
if err := SaveVerifiedAssignmentRoster(nil, nil, domain.Assignment{}, nil, nil); err == nil {
t.Fatal("empty verified roster accepted")
}
}
func TestSignedRosterRequiresCryptographicVerification(t *testing.T) {
now := time.Unix(1000, 0)
assignment := domain.Assignment{Allocation: domain.Allocation{AllocationID: "allocation-1", MatchID: "match-1", ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", State: domain.ServerAllocated}, Manifest: domain.AllocationManifest{AllocationID: "allocation-1", MatchID: "match-1", ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", RosterDigest: "roster-1"}, Endpoint: "127.0.0.1:7777"}
auth := domain.JoinAuthorisation{MatchID: "match-1", ServerID: "server-1", PlayerID: "player-1", SteamID: "steam-1", Slot: 0, Team: 0, Protocol: "1", Generation: 1, ExpiresAt: now.Add(time.Minute)}
signed := domain.SignedJoinAuthorisation{Authorisation: auth, Signature: []byte("signature")}
if err := validateSignedRosterEntry(assignment, signed, func([]byte, []byte) bool { return false }); err == nil {
t.Fatal("forged signature accepted")
}
if err := validateSignedRosterEntry(assignment, signed, func(message, signature []byte) bool {
return string(message) == string(domain.JoinAuthorisationBytes(auth)) && string(signature) == "signature"
}); err != nil {
t.Fatalf("valid signature rejected: %v", err)
}
wrongTeam := signed
wrongTeam.Authorisation.Slot = 3
wrongTeam.Authorisation.Team = 0
if err := validateSignedRosterEntry(assignment, wrongTeam, func([]byte, []byte) bool { return true }); err == nil {
t.Fatal("team/slot mismatch accepted")
}
duplicate := signed
if err := SaveVerifiedAssignmentRoster(nil, nil, assignment, []domain.SignedJoinAuthorisation{signed, duplicate}, func([]byte, []byte) bool { return true }); err == nil {
t.Fatal("duplicate roster player accepted")
}
}