package allocator import ( "context" "errors" "testing" "time" "github.com/cosmic-clash/cosmic-clash/server/agones" "github.com/cosmic-clash/cosmic-clash/server/domain" ) type providerSpy struct { calls int result agones.AllocatedServer err error } func (p *providerSpy) Allocate(_ context.Context, _ domain.AllocationRequest, _ map[string]string, _ time.Time) (agones.AllocatedServer, error) { p.calls++ return p.result, p.err } type durableSpy struct { calls int allocation domain.Allocation err error } type rosterSpy struct { calls int err error } func (r *rosterSpy) PublishRoster(_ context.Context, _ domain.Assignment, _ []domain.SignedJoinAuthorisation, _ func([]byte, []byte) bool) error { r.calls++ return r.err } func (d *durableSpy) RecordProviderAllocation(_ context.Context, allocation domain.Allocation, _ time.Time) (domain.Allocation, error) { d.calls++ d.allocation = allocation return allocation, d.err } func TestServiceDurablyRecordsProviderAllocationBeforeReturning(t *testing.T) { provider := &providerSpy{result: agones.AllocatedServer{Allocation: domain.Allocation{AllocationID: "a", MatchID: "m", ServerID: "gs", State: domain.ServerAllocated}, Endpoint: "127.0.0.1:7777"}} durable := &durableSpy{} service := Service{Provider: provider, Durable: durable, Now: func() time.Time { return time.Unix(1000, 0) }} result, err := service.Allocate(context.Background(), domain.AllocationRequest{AllocationID: "a", MatchID: "m", Region: "EU", Build: "b", Protocol: 1, Transport: "enet"}, map[string]string{"region": "EU"}) if err != nil || result.Endpoint == "" || durable.calls != 1 || durable.allocation.ServerID != "gs" { t.Fatalf("result=%+v err=%v durable=%+v", result, err, durable) } } func TestServiceDoesNotReturnProviderResultAfterDurableFailure(t *testing.T) { provider := &providerSpy{result: agones.AllocatedServer{Allocation: domain.Allocation{AllocationID: "a", State: domain.ServerAllocated}, Endpoint: "127.0.0.1:7777"}} durable := &durableSpy{err: errors.New("database unavailable")} service := Service{Provider: provider, Durable: durable, Now: func() time.Time { return time.Unix(1000, 0) }} result, err := service.Allocate(context.Background(), domain.AllocationRequest{AllocationID: "a", MatchID: "m", Region: "EU", Build: "b", Protocol: 1, Transport: "enet"}, map[string]string{"region": "EU"}) if err == nil || result.Endpoint != "" || durable.calls != 1 { t.Fatalf("result=%+v err=%v calls=%d", result, err, durable.calls) } } func TestServicePublishesRosterOnlyForAllocatedAssignment(t *testing.T) { roster := &rosterSpy{} service := Service{Roster: roster} assignment := domain.Assignment{Allocation: domain.Allocation{State: domain.ServerAllocated}, Endpoint: "127.0.0.1:7777"} if err := service.PublishRoster(context.Background(), assignment, []domain.SignedJoinAuthorisation{{Signature: []byte("sig")}}, func([]byte, []byte) bool { return true }); err != nil || roster.calls != 1 { t.Fatalf("publish err=%v calls=%d", err, roster.calls) } assignment.Allocation.State = domain.ServerReady if err := service.PublishRoster(context.Background(), assignment, nil, nil); err != domain.ErrManifestRejected || roster.calls != 1 { t.Fatalf("premature publish err=%v calls=%d", err, roster.calls) } }