package admin import ( "context" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" ) func newTestRedis(t *testing.T) (*redis.Client, *miniredis.Miniredis) { t.Helper() mr := miniredis.RunT(t) rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) t.Cleanup(func() { rdb.Close() }) return rdb, mr } func TestSessionStore_Lifecycle(t *testing.T) { rdb, _ := newTestRedis(t) store := NewSessionStore(rdb, 30*time.Minute) ctx := context.Background() sid, sess, err := store.Create(ctx, 42, "alice") if err != nil { t.Fatal(err) } if sess.AdminID != 42 || sess.Username != "alice" || sess.CSRFToken == "" { t.Fatalf("unexpected session %+v", sess) } got, err := store.Get(ctx, sid) if err != nil { t.Fatal(err) } if got == nil || got.AdminID != 42 { t.Fatalf("Get returned %+v", got) } // CSRF check. if !got.ValidCSRF(sess.CSRFToken) { t.Error("valid CSRF token rejected") } if got.ValidCSRF("bogus") { t.Error("bogus CSRF token accepted") } // Delete. if err := store.Delete(ctx, sid); err != nil { t.Fatal(err) } got, _ = store.Get(ctx, sid) if got != nil { t.Error("session still present after delete") } } func TestSessionStore_SlidingExpiry(t *testing.T) { rdb, mr := newTestRedis(t) store := NewSessionStore(rdb, 30*time.Minute) ctx := context.Background() sid, _, err := store.Create(ctx, 1, "bob") if err != nil { t.Fatal(err) } // Advance 20 min, access (slides TTL forward). mr.FastForward(20 * time.Minute) if got, _ := store.Get(ctx, sid); got == nil { t.Fatal("session expired prematurely") } // Another 20 min: still alive because the previous Get slid the TTL. mr.FastForward(20 * time.Minute) if got, _ := store.Get(ctx, sid); got == nil { t.Fatal("sliding TTL did not extend session") } // Idle past the full TTL: gone. mr.FastForward(31 * time.Minute) if got, _ := store.Get(ctx, sid); got != nil { t.Error("session survived past idle timeout") } }