mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 08:23:45 +00:00
feat: add deterministic allocation policy
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user