mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
107 lines
3.7 KiB
Go
107 lines
3.7 KiB
Go
package matcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cosmic-clash/cosmic-clash/server/domain"
|
|
)
|
|
|
|
type creatorSpy struct {
|
|
calls int
|
|
err error
|
|
last domain.Proposal
|
|
ids map[string]string
|
|
}
|
|
|
|
func (c *creatorSpy) CreateProposal(_ context.Context, proposal domain.Proposal, ids map[string]string, _ time.Time) error {
|
|
c.calls++
|
|
c.last = proposal
|
|
c.ids = ids
|
|
return c.err
|
|
}
|
|
|
|
func candidates() []domain.Candidate {
|
|
now := time.Unix(1000, 0).UTC()
|
|
result := make([]domain.Candidate, 4)
|
|
for i := range result {
|
|
result[i] = domain.Candidate{TicketID: "ticket-" + string(rune('1'+i)), PlayerID: "player-" + string(rune('1'+i)), Playlist: domain.Casual, ProtocolVersion: 1, EnqueuedAt: now.Add(time.Duration(i) * time.Second), PredictedRTT: map[string]float64{"EU": 20}}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func workerFor(source CandidateSource, creator ProposalCreator) Worker {
|
|
return Worker{Source: source, Creator: creator, Playlist: domain.Casual, Size: 4, Now: func() time.Time { return time.Unix(1000, 0).UTC() }, NextID: func() string { return "proposal-1234567890123456" }, Prepare: func(id string, playlist domain.Playlist, formation domain.MatchFormation, now time.Time) (domain.PreparedProposal, error) {
|
|
return domain.PrepareProposal(id, playlist, formation, nil, domain.RankedArena{}, now)
|
|
}}
|
|
}
|
|
|
|
func TestRunOnceDelegatesFinalClaimAndBindsTickets(t *testing.T) {
|
|
creator := &creatorSpy{}
|
|
worker := workerFor(func(context.Context, time.Time, domain.Playlist, int) ([]domain.Candidate, error) {
|
|
return candidates(), nil
|
|
}, creator)
|
|
formed, err := worker.RunOnce(context.Background())
|
|
if err != nil || !formed || creator.calls != 1 {
|
|
t.Fatalf("formed=%v err=%v calls=%d", formed, err, creator.calls)
|
|
}
|
|
if len(creator.ids) != 4 || creator.ids["player-1"] != "ticket-1" {
|
|
t.Fatalf("ticket bindings=%v", creator.ids)
|
|
}
|
|
}
|
|
|
|
func TestRunOnceFailsClosedOnSourceOrDurableClaimFailure(t *testing.T) {
|
|
creator := &creatorSpy{err: errors.New("serialization conflict")}
|
|
worker := workerFor(func(context.Context, time.Time, domain.Playlist, int) ([]domain.Candidate, error) {
|
|
return nil, errors.New("redis unavailable")
|
|
}, creator)
|
|
if _, err := worker.RunOnce(context.Background()); err == nil {
|
|
t.Fatal("source failure was swallowed")
|
|
}
|
|
worker.Source = func(context.Context, time.Time, domain.Playlist, int) ([]domain.Candidate, error) {
|
|
return candidates(), nil
|
|
}
|
|
if _, err := worker.RunOnce(context.Background()); err == nil {
|
|
t.Fatal("durable claim failure was swallowed")
|
|
}
|
|
if creator.calls != 1 {
|
|
t.Fatalf("creator calls=%d", creator.calls)
|
|
}
|
|
}
|
|
|
|
func TestRunOnceDoesNotClaimAnIncompleteBatch(t *testing.T) {
|
|
creator := &creatorSpy{}
|
|
worker := workerFor(func(context.Context, time.Time, domain.Playlist, int) ([]domain.Candidate, error) {
|
|
return candidates()[:3], nil
|
|
}, creator)
|
|
formed, err := worker.RunOnce(context.Background())
|
|
if err != nil || formed || creator.calls != 0 {
|
|
t.Fatalf("formed=%v err=%v calls=%d", formed, err, creator.calls)
|
|
}
|
|
}
|
|
|
|
func TestRunOnceRejectsMixedPlaylistAndDuplicateIdentityBatches(t *testing.T) {
|
|
creator := &creatorSpy{}
|
|
worker := workerFor(func(_ context.Context, _ time.Time, _ domain.Playlist, _ int) ([]domain.Candidate, error) {
|
|
batch := candidates()
|
|
batch[1].Playlist = domain.Ranked
|
|
return batch, nil
|
|
}, creator)
|
|
if _, err := worker.RunOnce(context.Background()); err == nil {
|
|
t.Fatal("mixed playlist was accepted")
|
|
}
|
|
worker.Source = func(_ context.Context, _ time.Time, _ domain.Playlist, _ int) ([]domain.Candidate, error) {
|
|
batch := candidates()
|
|
batch[1].PlayerID = batch[0].PlayerID
|
|
return batch, nil
|
|
}
|
|
if _, err := worker.RunOnce(context.Background()); err == nil {
|
|
t.Fatal("duplicate identity was accepted")
|
|
}
|
|
if creator.calls != 0 {
|
|
t.Fatalf("creator calls=%d", creator.calls)
|
|
}
|
|
}
|