a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
211 lines
4.9 KiB
Go
211 lines
4.9 KiB
Go
package streamable
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMemoryStore_CreateAndGet(t *testing.T) {
|
|
s := NewMemoryStore(time.Minute)
|
|
defer s.Close()
|
|
|
|
id, err := s.Create(SessionState{ClientName: "claude"})
|
|
if err != nil {
|
|
t.Fatalf("Create: %v", err)
|
|
}
|
|
if id == "" {
|
|
t.Fatal("Create returned empty id")
|
|
}
|
|
|
|
got, ok := s.Get(id)
|
|
if !ok {
|
|
t.Fatalf("Get(%q): not found", id)
|
|
}
|
|
if got.ClientName != "claude" {
|
|
t.Errorf("ClientName = %q, want claude", got.ClientName)
|
|
}
|
|
if got.ID != id {
|
|
t.Errorf("ID = %q, want %q", got.ID, id)
|
|
}
|
|
if got.CreatedAt.IsZero() || got.LastUsed.IsZero() {
|
|
t.Error("timestamps not set on create")
|
|
}
|
|
if s.Len() != 1 {
|
|
t.Errorf("Len() = %d, want 1", s.Len())
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_GetUnknownReturnsNotOK(t *testing.T) {
|
|
s := NewMemoryStore(time.Minute)
|
|
defer s.Close()
|
|
if _, ok := s.Get("nonexistent"); ok {
|
|
t.Error("Get of unknown id returned ok=true")
|
|
}
|
|
if _, ok := s.Get(""); ok {
|
|
t.Error("Get(\"\") returned ok=true")
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_UpdatePersists(t *testing.T) {
|
|
s := NewMemoryStore(time.Minute)
|
|
defer s.Close()
|
|
id, _ := s.Create(SessionState{})
|
|
got, _ := s.Get(id)
|
|
got.ClientName = "cursor"
|
|
got.Initialized = true
|
|
if err := s.Update(got); err != nil {
|
|
t.Fatalf("Update: %v", err)
|
|
}
|
|
back, ok := s.Get(id)
|
|
if !ok || back.ClientName != "cursor" || !back.Initialized {
|
|
t.Errorf("Update didn't persist: %+v", back)
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_UpdateUnknownIsNoop(t *testing.T) {
|
|
s := NewMemoryStore(time.Minute)
|
|
defer s.Close()
|
|
if err := s.Update(SessionState{ID: "never_seen"}); err != nil {
|
|
t.Errorf("Update of unknown id returned err: %v", err)
|
|
}
|
|
if s.Len() != 0 {
|
|
t.Errorf("Update of unknown id added entry: Len() = %d", s.Len())
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_DeleteIsIdempotent(t *testing.T) {
|
|
s := NewMemoryStore(time.Minute)
|
|
defer s.Close()
|
|
id, _ := s.Create(SessionState{})
|
|
s.Delete(id)
|
|
s.Delete(id)
|
|
s.Delete("never_seen")
|
|
s.Delete("")
|
|
if s.Len() != 0 {
|
|
t.Errorf("Len() = %d, want 0", s.Len())
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_TTLEviction(t *testing.T) {
|
|
s := NewMemoryStore(50 * time.Millisecond)
|
|
defer s.Close()
|
|
id, _ := s.Create(SessionState{})
|
|
// Sleep past the TTL so the next Get falls into the eviction path.
|
|
time.Sleep(80 * time.Millisecond)
|
|
if _, ok := s.Get(id); ok {
|
|
t.Error("Get returned an expired session")
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_SweepNowEvictsStale(t *testing.T) {
|
|
s := NewMemoryStore(50 * time.Millisecond)
|
|
defer s.Close()
|
|
id1, _ := s.Create(SessionState{})
|
|
id2, _ := s.Create(SessionState{})
|
|
if s.Len() != 2 {
|
|
t.Fatalf("Len() = %d, want 2", s.Len())
|
|
}
|
|
// Force every session to look stale by sweeping with a far-future "now".
|
|
evicted := s.SweepNow(time.Now().Add(1 * time.Hour))
|
|
if evicted != 2 {
|
|
t.Errorf("SweepNow evicted = %d, want 2", evicted)
|
|
}
|
|
if _, ok := s.Get(id1); ok {
|
|
t.Error("id1 should have been evicted")
|
|
}
|
|
if _, ok := s.Get(id2); ok {
|
|
t.Error("id2 should have been evicted")
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_GetRefreshesLastUsed(t *testing.T) {
|
|
s := NewMemoryStore(time.Hour)
|
|
defer s.Close()
|
|
id, _ := s.Create(SessionState{})
|
|
first, _ := s.Get(id)
|
|
time.Sleep(10 * time.Millisecond)
|
|
second, _ := s.Get(id)
|
|
if !second.LastUsed.After(first.LastUsed) {
|
|
t.Errorf("LastUsed didn't advance: first=%v second=%v", first.LastUsed, second.LastUsed)
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_ConcurrentCreate(t *testing.T) {
|
|
s := NewMemoryStore(time.Hour)
|
|
defer s.Close()
|
|
const N = 100
|
|
var wg sync.WaitGroup
|
|
ids := make(chan string, N)
|
|
for i := 0; i < N; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
id, err := s.Create(SessionState{})
|
|
if err != nil {
|
|
t.Errorf("Create: %v", err)
|
|
return
|
|
}
|
|
ids <- id
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
close(ids)
|
|
seen := make(map[string]bool, N)
|
|
for id := range ids {
|
|
if seen[id] {
|
|
t.Errorf("duplicate id: %s", id)
|
|
}
|
|
seen[id] = true
|
|
}
|
|
if s.Len() != N {
|
|
t.Errorf("Len() = %d, want %d", s.Len(), N)
|
|
}
|
|
}
|
|
|
|
func TestMemoryStore_CloseIsIdempotent(t *testing.T) {
|
|
s := NewMemoryStore(time.Hour)
|
|
s.Close()
|
|
s.Close() // would panic on a double-close channel without the once gate.
|
|
}
|
|
|
|
func TestStatelessStore_AllOpsAreNoops(t *testing.T) {
|
|
s := StatelessStore{}
|
|
id, err := s.Create(SessionState{ClientName: "ignored"})
|
|
if err != nil || id != "" {
|
|
t.Errorf("Create returned (%q, %v), want (\"\", nil)", id, err)
|
|
}
|
|
if _, ok := s.Get("anything"); ok {
|
|
t.Error("Get returned ok=true")
|
|
}
|
|
if err := s.Update(SessionState{ID: "x"}); err != nil {
|
|
t.Errorf("Update: %v", err)
|
|
}
|
|
s.Delete("y")
|
|
if s.Len() != 0 {
|
|
t.Errorf("Len() = %d, want 0", s.Len())
|
|
}
|
|
}
|
|
|
|
func TestNewSessionID_IsRandomAndHex(t *testing.T) {
|
|
a, err := newSessionID()
|
|
if err != nil {
|
|
t.Fatalf("newSessionID: %v", err)
|
|
}
|
|
b, err := newSessionID()
|
|
if err != nil {
|
|
t.Fatalf("newSessionID: %v", err)
|
|
}
|
|
if a == b {
|
|
t.Errorf("got two identical ids: %q", a)
|
|
}
|
|
if len(a) != 32 {
|
|
t.Errorf("len(id) = %d, want 32", len(a))
|
|
}
|
|
for _, c := range a {
|
|
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
|
|
t.Errorf("id contains non-hex byte %q", c)
|
|
}
|
|
}
|
|
}
|