package domain import ( "errors" "sync" "testing" "time" ) func TestAllocatorFiltersAndAtomicallyClaimsCompatibleReadyServer(t *testing.T) { a, err := NewAllocator([]ReadyServer{ {ServerID: "server-b", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", State: ServerReady}, {ServerID: "server-a", Region: "NA", Build: "build-1", Protocol: 1, Transport: "enet", State: ServerReady}, {ServerID: "server-c", Region: "EU", Build: "build-2", Protocol: 1, Transport: "enet", State: ServerReady}, }) if err != nil { t.Fatal(err) } request := AllocationRequest{AllocationID: "allocation-1", MatchID: "match-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet"} got, err := a.Allocate(request, time.Unix(1000, 0)) if err != nil || got.ServerID != "server-b" || got.State != ServerAllocated { t.Fatalf("allocation = %+v err=%v", got, err) } if _, err := a.Allocate(AllocationRequest{AllocationID: "allocation-2", MatchID: "match-2", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet"}, time.Unix(1001, 0)); !errors.Is(err, ErrNoCapacity) { t.Fatalf("claimed server was reused: %v", err) } } func TestAllocatorIsIdempotentAndRejectsConflictingReplay(t *testing.T) { a, _ := NewAllocator([]ReadyServer{{ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "steam_sdr", State: ServerReady}}) request := AllocationRequest{AllocationID: "allocation-1", MatchID: "match-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "steam_sdr"} first, err := a.Allocate(request, time.Unix(1000, 0)) if err != nil { t.Fatal(err) } replay, err := a.Allocate(request, time.Unix(2000, 0)) if err != nil || replay != first { t.Fatalf("replay = %+v err=%v", replay, err) } request.MatchID = "match-2" if _, err := a.Allocate(request, time.Unix(2000, 0)); !errors.Is(err, ErrConflict) { t.Fatalf("conflicting replay = %v", err) } } func TestAllocatorRejectsInvalidServerAndNoCompatibleCapacity(t *testing.T) { if _, err := NewAllocator([]ReadyServer{{ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "udp", State: ServerReady}}); !errors.Is(err, ErrAllocationInput) { t.Fatalf("invalid server accepted: %v", err) } a, _ := NewAllocator(nil) if _, err := a.Allocate(AllocationRequest{AllocationID: "a", MatchID: "m", Region: "EU", Build: "b", Protocol: 1, Transport: "enet"}, time.Unix(1000, 0)); !errors.Is(err, ErrNoCapacity) { t.Fatalf("empty allocator error = %v", err) } } func TestAllocatorRejectsUnregisteredArenaPath(t *testing.T) { a, err := NewAllocator([]ReadyServer{{ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", State: ServerReady}}) if err != nil { t.Fatal(err) } for _, path := range []string{"res://scenes/arena_01_elevated.tscn", "res://forged.tscn"} { request := AllocationRequest{AllocationID: "allocation-" + path, MatchID: "match-1", Region: "EU", Build: "build-1", Protocol: 1, ArenaPath: path, Transport: "enet"} if _, err := a.Allocate(request, time.Unix(1000, 0)); !errors.Is(err, ErrAllocationInput) { t.Fatalf("arena path %q returned %v, want ErrAllocationInput", path, err) } } } func TestAllocatorConcurrentClaimsCannotDoubleAllocateOneServer(t *testing.T) { a, _ := NewAllocator([]ReadyServer{{ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", State: ServerReady}}) requests := []AllocationRequest{ {AllocationID: "allocation-a", MatchID: "match-a", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet"}, {AllocationID: "allocation-b", MatchID: "match-b", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet"}, } var wg sync.WaitGroup results := make(chan error, len(requests)) for _, request := range requests { wg.Add(1) go func(request AllocationRequest) { defer wg.Done() _, err := a.Allocate(request, time.Unix(1000, 0)) results <- err }(request) } wg.Wait() close(results) wins := 0 for err := range results { if err == nil { wins++ } else if !errors.Is(err, ErrNoCapacity) { t.Fatalf("unexpected concurrent claim error: %v", err) } } if wins != 1 { t.Fatalf("concurrent claims succeeded %d times", wins) } } func TestAllocatorPublishesOnlyVerifiedAssignmentAndReplaysIdentically(t *testing.T) { a, err := NewAllocator([]ReadyServer{{ServerID: "server-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet", State: ServerReady}}) if err != nil { t.Fatal(err) } allocation, err := a.Allocate(AllocationRequest{AllocationID: "allocation-1", MatchID: "match-1", Region: "EU", Build: "build-1", Protocol: 1, Transport: "enet"}, time.Unix(1000, 0)) if err != nil { t.Fatal(err) } manifest := AllocationManifest{AllocationID: allocation.AllocationID, MatchID: allocation.MatchID, ServerID: allocation.ServerID, Region: allocation.Region, Build: allocation.Build, Protocol: allocation.Protocol, Transport: allocation.Transport, RosterDigest: "roster-1"} digest := ManifestDigest(manifest) verify := func(payload, signature []byte) bool { return string(payload) == string(manifestBytes(manifest)) && string(signature) == string(digest[:]) } if _, err := a.PublishAssignment("unknown", manifest, "127.0.0.1:30001", digest[:], verify); !errors.Is(err, ErrAllocationNotFound) { t.Fatalf("unknown allocation error = %v", err) } bad := manifest bad.Build = "build-2" if _, err := a.PublishAssignment(allocation.AllocationID, bad, "127.0.0.1:30001", digest[:], verify); !errors.Is(err, ErrManifestRejected) { t.Fatalf("tampered assignment error = %v", err) } first, err := a.PublishAssignment(allocation.AllocationID, manifest, "127.0.0.1:30001", digest[:], verify) if err != nil { t.Fatal(err) } replay, err := a.PublishAssignment(allocation.AllocationID, manifest, "127.0.0.1:30001", digest[:], verify) if err != nil || replay != first { t.Fatalf("assignment replay = %+v err=%v", replay, err) } if _, err := a.PublishAssignment(allocation.AllocationID, manifest, "127.0.0.1:30002", digest[:], verify); !errors.Is(err, ErrConflict) { t.Fatalf("endpoint mutation error = %v", err) } }