1import { describe, expect, it } from "vitest"; 2import { SessionManager, type Session, type SessionStore } from "./sessions.js"; 3 4class MemSessions implements SessionStore { 5 map = new Map<string, Session>(); 6 listAll?: () => Promise<Session[]>; 7 async put(s: Session) { 8 this.map.set(s.id, s); 9 } 10 async get(id: string) { 11 return this.map.get(id) ?? null; 12 } 13 async listByUser(userId: string) { 14 return [...this.map.values()].filter((s) => s.userId === userId); 15 } 16 async delete(id: string) { 17 this.map.delete(id); 18 } 19} 20 21const base = { userId: "usr_1", refreshTokenHash: "h1", familyId: "f1" }; 22 23describe("SessionManager", () => { 24 it("creates with defaults (30d)", async () => { 25 const m = new SessionManager(new MemSessions()); 26 const s = await m.create({ ...base, nowMs: 1000 }); 27 expect(s.id.startsWith("sess_")).toBe(true); 28 expect(s.expiresAt).toBe(1000 + 30 * 86_400_000); 29 expect(s.lastActiveAt).toBe(1000); 30 }); 31 it("touches only live sessions", async () => { 32 const store = new MemSessions(); 33 const m = new SessionManager(store); 34 const s = await m.create({ ...base, nowMs: 1000 }); 35 const touched = await m.touch(s.id, 2000); 36 expect(touched?.lastActiveAt).toBe(2000); 37 await m.revoke(s.id, 3000); 38 expect(await m.touch(s.id, 4000)).toBeNull(); 39 }); 40 it("revokes all for a user", async () => { 41 const m = new SessionManager(new MemSessions()); 42 await m.create({ ...base, nowMs: 1 }); 43 await m.create({ ...base, refreshTokenHash: "h2", familyId: "f2", nowMs: 2 }); 44 expect(await m.revokeAllForUser("usr_1", 100)).toBe(2); 45 expect(await m.revokeAllForUser("usr_1", 101)).toBe(0); 46 }); 47 it("prunes revoked/expired when store supports listAll", async () => { 48 const store = new MemSessions(); 49 store.listAll = async () => [...store.map.values()]; 50 const m = new SessionManager(store); 51 const s = await m.create({ ...base, nowMs: 1, ttlMs: 100 }); 52 await m.revoke(s.id, 50); 53 expect(await m.pruneExpired(1000)).toBe(1); 54 expect(store.map.size).toBe(0); 55 }); 56}); 57