mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-15 03:42:04 +00:00
fix(multiplayer): harden reconnect lifecycle fencing
This commit is contained in:
@@ -26,19 +26,19 @@ func TestRankedReconnectReclaimsWithinGraceAndFencesOldGeneration(t *testing.T)
|
||||
if gen, err := r.Admit(auth, now); err != nil || gen != 1 {
|
||||
t.Fatalf("initial admit = %d, %v", gen, err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now); err != nil {
|
||||
if err := r.Disconnect("a", 1, now.Add(time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gen, err := r.Admit(auth, now.Add(RankedReconnectGrace)); err != nil || gen != 2 {
|
||||
if gen, err := r.Admit(auth, now.Add(time.Second+RankedReconnectGrace)); err != nil || gen != 2 {
|
||||
t.Fatalf("boundary reclaim = %d, %v", gen, err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now.Add(31*time.Second)); !errors.Is(err, ErrConnectionFenced) {
|
||||
if err := r.Disconnect("a", 1, now.Add(62*time.Second)); !errors.Is(err, ErrConnectionFenced) {
|
||||
t.Fatalf("old connection was not fenced: %v", err)
|
||||
}
|
||||
if err := r.Disconnect("a", 2, now.Add(31*time.Second)); err != nil {
|
||||
if err := r.Disconnect("a", 2, now.Add(62*time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gen, err := r.Admit(auth, now.Add(32*time.Second)); err != nil || gen != 3 {
|
||||
if gen, err := r.Admit(auth, now.Add(63*time.Second)); err != nil || gen != 3 {
|
||||
t.Fatalf("repeated reclaim with existing authorisation = %d, %v", gen, err)
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,9 @@ func TestRankedReconnectRejectsWrongBindingAndExpiredGrace(t *testing.T) {
|
||||
if _, err := r.Admit(wrongIdentity, now); !errors.Is(err, ErrJoinAuthorisation) {
|
||||
t.Fatalf("wrong SteamID accepted: %v", err)
|
||||
}
|
||||
if _, err := r.Admit(testRoster(now)[0], now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -67,6 +70,36 @@ func TestRankedReconnectRejectsWrongBindingAndExpiredGrace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRankedReconnectRejectsDuplicateAndTimeReversedLifecycle(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
r, err := NewRankedConnections("match-1", "server-1", "v1", testRoster(now))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
auth := testRoster(now)[0]
|
||||
if err := r.Disconnect("a", 1, now); !errors.Is(err, ErrConnectionFenced) {
|
||||
t.Fatalf("disconnect before admission error = %v", err)
|
||||
}
|
||||
if _, err := r.Admit(auth, time.Time{}); !errors.Is(err, ErrJoinAuthorisation) {
|
||||
t.Fatalf("zero-time admission error = %v", err)
|
||||
}
|
||||
if _, err := r.Admit(auth, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := r.Admit(auth, now.Add(time.Second)); !errors.Is(err, ErrConnectionFenced) {
|
||||
t.Fatalf("duplicate active admission error = %v", err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now.Add(2*time.Second)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now.Add(30*time.Second)); !errors.Is(err, ErrConnectionFenced) {
|
||||
t.Fatalf("duplicate disconnect error = %v", err)
|
||||
}
|
||||
if _, err := r.Admit(auth, now.Add(time.Second)); !errors.Is(err, ErrJoinAuthorisation) {
|
||||
t.Fatalf("time-reversed reclaim error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRankedRosterRejectsDuplicateSlots(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
roster := testRoster(now)
|
||||
@@ -91,6 +124,9 @@ func TestRankedAbandonCooldownUsesRollingSevenDayLadder(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := r.Admit(testRoster(now)[0], now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := r.Disconnect("a", 1, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user