105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
package runtime
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"neuralhunt/internal/data"
|
|
)
|
|
|
|
func TestPresenceSingleSession(t *testing.T) {
|
|
s := New()
|
|
lease1, err := s.AcquirePresence("c", "s1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.AcquirePresence("c", "s2"); err == nil {
|
|
t.Fatal("expected conflict")
|
|
}
|
|
if !s.HasPresence("c", "s1") {
|
|
t.Fatal("missing presence")
|
|
}
|
|
s.ReleasePresence("c", "s1", lease1)
|
|
if s.ConnectedCount() != 0 {
|
|
t.Fatal("presence not released")
|
|
}
|
|
}
|
|
|
|
func TestPresenceReconnectOldReleaseCannotDeleteNewLease(t *testing.T) {
|
|
s := New()
|
|
oldLease, err := s.AcquirePresence("c", "same-session")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
newLease, err := s.AcquirePresence("c", "same-session")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if oldLease == newLease {
|
|
t.Fatal("expected a new lease generation")
|
|
}
|
|
|
|
// This is the race that caused periodic HTTP 409 responses: the old
|
|
// websocket's defer ran after the replacement websocket had connected.
|
|
s.ReleasePresence("c", "same-session", oldLease)
|
|
if !s.HasPresence("c", "same-session") {
|
|
t.Fatal("stale websocket release removed the replacement presence")
|
|
}
|
|
|
|
s.ReleasePresence("c", "same-session", newLease)
|
|
if s.ConnectedCount() != 0 {
|
|
t.Fatal("new lease was not released")
|
|
}
|
|
}
|
|
|
|
func TestGuessHotPathAndReroll(t *testing.T) {
|
|
s := New()
|
|
task := data.Task{ID: "t", PublicSeed: "a", Revision: 1}
|
|
s.InitGuess(task, "c", GuessState{})
|
|
r, err := s.Accept(task, "c", 0, 12.5, time.Millisecond)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !r.Improved || r.State.NextSeq != 1 || r.State.GuessCount != 1 {
|
|
t.Fatalf("bad state: %+v", r)
|
|
}
|
|
if _, err = s.Accept(task, "c", 1, 10, time.Hour); err != ErrRateLimited {
|
|
t.Fatalf("want rate limit, got %v", err)
|
|
}
|
|
// Same seed/admin revision preserves sequence.
|
|
task.Revision = 2
|
|
g := s.InitGuess(task, "c", GuessState{})
|
|
if g.NextSeq != 1 {
|
|
t.Fatalf("sequence lost: %d", g.NextSeq)
|
|
}
|
|
// New seed is a reroll and reloads the persisted reset.
|
|
task.PublicSeed = "b"
|
|
g = s.InitGuess(task, "c", GuessState{})
|
|
if g.NextSeq != 0 || g.BestScore != 0 {
|
|
t.Fatalf("reroll not reset: %+v", g)
|
|
}
|
|
}
|
|
|
|
func TestLotterySkipConsumesSequenceWithoutCountingAcceptedGuess(t *testing.T) {
|
|
s := New()
|
|
task := data.Task{ID: "lottery-task", PublicSeed: "seed", Revision: 1}
|
|
s.InitGuess(task, "client", GuessState{})
|
|
if err := s.CanSubmit(task, "client", 0, 0); err != nil {
|
|
t.Fatalf("eligible guess rejected: %v", err)
|
|
}
|
|
next, err := s.SkipLottery(task, "client", 0, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if next != 1 {
|
|
t.Fatalf("next seq = %d, want 1", next)
|
|
}
|
|
g, ok := s.Current(task, "client")
|
|
if !ok {
|
|
t.Fatal("missing runtime state")
|
|
}
|
|
if g.NextSeq != 1 || g.GuessCount != 0 || g.LastGuess.IsZero() {
|
|
t.Fatalf("unexpected lottery skip state: %+v", g)
|
|
}
|
|
}
|