diff --git a/apps/aelis-backend/src/session/user-session-manager.test.ts b/apps/aelis-backend/src/session/user-session-manager.test.ts index 214d8b2..ca9078e 100644 --- a/apps/aelis-backend/src/session/user-session-manager.test.ts +++ b/apps/aelis-backend/src/session/user-session-manager.test.ts @@ -225,4 +225,35 @@ describe("UserSessionManager", () => { expect(session1).toBe(session2) expect(callCount).toBe(1) }) + + test("remove during in-flight getOrCreate prevents session from being stored", async () => { + let resolveProvider: () => void + const providerGate = new Promise((r) => { + resolveProvider = r + }) + + const manager = new UserSessionManager({ + providers: [ + async () => { + await providerGate + return new LocationSource() + }, + ], + }) + + const sessionPromise = manager.getOrCreate("user-1") + + // remove() while provider is still resolving + manager.remove("user-1") + + // Let the provider finish + resolveProvider!() + + await expect(sessionPromise).rejects.toThrow("removed during creation") + + // A fresh getOrCreate should produce a new session, not the cancelled one + const freshSession = await manager.getOrCreate("user-1") + expect(freshSession).toBeDefined() + expect(freshSession.engine).toBeDefined() + }) }) diff --git a/apps/aelis-backend/src/session/user-session-manager.ts b/apps/aelis-backend/src/session/user-session-manager.ts index c5146a8..df82908 100644 --- a/apps/aelis-backend/src/session/user-session-manager.ts +++ b/apps/aelis-backend/src/session/user-session-manager.ts @@ -32,6 +32,12 @@ export class UserSessionManager { this.pending.set(userId, promise) try { const session = await promise + // If remove() was called while we were awaiting, it clears the + // pending entry. Detect that and destroy the session immediately. + if (!this.pending.has(userId)) { + session.destroy() + throw new Error(`Session for user ${userId} was removed during creation`) + } this.sessions.set(userId, session) return session } finally { @@ -45,6 +51,8 @@ export class UserSessionManager { session.destroy() this.sessions.delete(userId) } + // Cancel any in-flight creation so getOrCreate won't store the session + this.pending.delete(userId) } private async createSession(userId: string): Promise {