feat: backend service
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestCacheGetAddRemove(t *testing.T) {
|
||||
c := NewCache()
|
||||
if c.Ready() {
|
||||
t.Fatalf("fresh cache should not be Ready before Warm")
|
||||
}
|
||||
if c.Size() != 0 {
|
||||
t.Fatalf("fresh cache size = %d, want 0", c.Size())
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
uid := uuid.New()
|
||||
s := Session{DeviceSessionID: id, UserID: uid, Status: SessionStatusActive}
|
||||
c.Add(s)
|
||||
if c.Size() != 1 {
|
||||
t.Fatalf("size after Add = %d, want 1", c.Size())
|
||||
}
|
||||
got, ok := c.Get(id)
|
||||
if !ok || got.DeviceSessionID != id {
|
||||
t.Fatalf("Get after Add: ok=%v session=%+v", ok, got)
|
||||
}
|
||||
|
||||
c.Remove(id)
|
||||
if c.Size() != 0 {
|
||||
t.Fatalf("size after Remove = %d, want 0", c.Size())
|
||||
}
|
||||
if _, ok := c.Get(id); ok {
|
||||
t.Fatalf("Get after Remove returned a hit")
|
||||
}
|
||||
|
||||
// Remove on already-evicted entry is a no-op.
|
||||
c.Remove(id)
|
||||
}
|
||||
|
||||
func TestCacheRemoveByUser(t *testing.T) {
|
||||
c := NewCache()
|
||||
uid := uuid.New()
|
||||
other := uuid.New()
|
||||
c.Add(Session{DeviceSessionID: uuid.New(), UserID: uid, Status: SessionStatusActive})
|
||||
c.Add(Session{DeviceSessionID: uuid.New(), UserID: uid, Status: SessionStatusActive})
|
||||
c.Add(Session{DeviceSessionID: uuid.New(), UserID: other, Status: SessionStatusActive})
|
||||
|
||||
removed := c.RemoveByUser(uid)
|
||||
if len(removed) != 2 {
|
||||
t.Fatalf("RemoveByUser removed %d, want 2", len(removed))
|
||||
}
|
||||
if c.Size() != 1 {
|
||||
t.Fatalf("size after RemoveByUser = %d, want 1", c.Size())
|
||||
}
|
||||
if got := c.RemoveByUser(uid); got != nil {
|
||||
t.Fatalf("RemoveByUser on empty user returned %v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheWarmFlipsReady(t *testing.T) {
|
||||
// Constructing a Cache and calling Warm against a Store without a real
|
||||
// database is awkward — the e2e test exercises Warm against Postgres.
|
||||
// Here we manually populate to confirm Ready toggles.
|
||||
c := NewCache()
|
||||
if c.Ready() {
|
||||
t.Fatalf("Ready before Warm")
|
||||
}
|
||||
// Simulate a successful Warm by setting ready and inserting via Add.
|
||||
c.ready.Store(true)
|
||||
if !c.Ready() {
|
||||
t.Fatalf("Ready did not flip after store")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheConcurrentGetAddRemove(t *testing.T) {
|
||||
c := NewCache()
|
||||
|
||||
const writers = 4
|
||||
const readers = 4
|
||||
const opsPerWorker = 1000
|
||||
|
||||
uid := uuid.New()
|
||||
ids := make([]uuid.UUID, opsPerWorker)
|
||||
for i := range ids {
|
||||
ids[i] = uuid.New()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var stop atomic.Bool
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for range writers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range opsPerWorker {
|
||||
if stop.Load() {
|
||||
return
|
||||
}
|
||||
c.Add(Session{DeviceSessionID: ids[i], UserID: uid, Status: SessionStatusActive})
|
||||
c.Remove(ids[i])
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for range readers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range opsPerWorker {
|
||||
if stop.Load() {
|
||||
return
|
||||
}
|
||||
_, _ = c.Get(ids[i%len(ids)])
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() { wg.Wait(); close(done) }()
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
stop.Store(true)
|
||||
<-done
|
||||
t.Fatalf("cache concurrency test timed out")
|
||||
}
|
||||
|
||||
// After all goroutines finish, the cache must be empty (every Add
|
||||
// is paired with a Remove).
|
||||
if c.Size() != 0 {
|
||||
t.Fatalf("cache size after concurrent run = %d, want 0", c.Size())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user