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) } }