diff --git a/src/db/locator.ts b/src/db/locator.ts index 9a1be67..d46f085 100644 --- a/src/db/locator.ts +++ b/src/db/locator.ts @@ -1,4 +1,5 @@ import type { Database } from 'bun:sqlite'; +import { AsyncLocalStorage } from 'node:async_hooks'; /** * Error específico cuando se intenta acceder a la DB sin haberla configurado. @@ -12,11 +13,18 @@ export class DbNotConfiguredError extends Error { let currentDb: Database | null = null; +// AsyncLocalStorage para aislar DB por contexto en tests (paralelismo seguro) +let testScope: AsyncLocalStorage | null = null; + /** * Establece la instancia global de DB. * Se permite sobrescribir (útil en tests). */ export function setDb(db: Database): void { + if (process.env.NODE_ENV === 'test') { + if (!testScope) testScope = new AsyncLocalStorage(); + try { testScope.enterWith(db); } catch {} + } currentDb = db; } @@ -24,6 +32,11 @@ export function setDb(db: Database): void { * Obtiene la instancia global de DB o lanza si no está configurada. */ export function getDb(): Database { + if (process.env.NODE_ENV === 'test') { + if (!testScope) testScope = new AsyncLocalStorage(); + const scoped = testScope.getStore(); + if (scoped) return scoped; + } if (currentDb) return currentDb; throw new DbNotConfiguredError('Database has not been configured. Call setDb(db) before using getDb().'); } @@ -33,6 +46,10 @@ export function getDb(): Database { */ export function resetDb(): void { currentDb = null; + if (process.env.NODE_ENV === 'test') { + // Re-inicializar el almacenamiento para limpiar el store del contexto actual + testScope = new AsyncLocalStorage(); + } } /** @@ -40,6 +57,10 @@ export function resetDb(): void { */ export function clearDb(): void { currentDb = null; + if (process.env.NODE_ENV === 'test') { + // Re-inicializar el almacenamiento para limpiar el store del contexto actual + testScope = new AsyncLocalStorage(); + } } /** diff --git a/tests/unit/services/command.gating.test.ts b/tests/unit/services/command.gating.test.ts index 261ebc0..734d103 100644 --- a/tests/unit/services/command.gating.test.ts +++ b/tests/unit/services/command.gating.test.ts @@ -6,15 +6,18 @@ import { setDb, resetDb } from '../../../src/db/locator'; describe('CommandService - gating en modo enforce', () => { const envBackup = process.env; + let memdb: any; beforeEach(() => { process.env = { ...envBackup, NODE_ENV: 'test', GROUP_GATING_MODE: 'enforce' }; - const memdb = makeMemDb(); + memdb = makeMemDb(); setDb(memdb); + try { AllowedGroups.resetForTests(); } catch {} }); afterEach(() => { process.env = envBackup; + try { resetDb(); memdb.close(); } catch {} }); it('bloquea comandos en grupo no permitido (desconocido)', async () => { diff --git a/tests/unit/services/group-sync.fetch-members.test.ts b/tests/unit/services/group-sync.fetch-members.test.ts index 5533397..936effc 100644 --- a/tests/unit/services/group-sync.fetch-members.test.ts +++ b/tests/unit/services/group-sync.fetch-members.test.ts @@ -14,6 +14,7 @@ describe('GroupSyncService - fetchGroupMembersFromAPI (parsing y fallbacks)', () EVOLUTION_API_INSTANCE: 'instance-1', EVOLUTION_API_KEY: 'apikey' }; + try { GroupSyncService.activeGroupsCache.clear(); } catch {} }); afterEach(() => { diff --git a/tests/unit/services/group-sync.members.test.ts b/tests/unit/services/group-sync.members.test.ts index edfc75b..65341be 100644 --- a/tests/unit/services/group-sync.members.test.ts +++ b/tests/unit/services/group-sync.members.test.ts @@ -20,6 +20,8 @@ describe('GroupSyncService - reconcileGroupMembers', () => { }); beforeEach(() => { + setDb(memdb); + try { GroupSyncService.activeGroupsCache.clear(); } catch {} // Limpiar tablas relevantes entre tests memdb.exec('DELETE FROM group_members'); memdb.exec('DELETE FROM users'); diff --git a/tests/unit/services/group-sync.scheduler.test.ts b/tests/unit/services/group-sync.scheduler.test.ts index bd28410..b3fa076 100644 --- a/tests/unit/services/group-sync.scheduler.test.ts +++ b/tests/unit/services/group-sync.scheduler.test.ts @@ -17,6 +17,7 @@ describe('GroupSyncService - scheduler de miembros', () => { beforeEach(() => { originalSyncMembers = GroupSyncService.syncMembersForActiveGroups; originalSyncGroups = GroupSyncService.syncGroups; + try { GroupSyncService.activeGroupsCache.clear(); } catch {} }); afterEach(() => { diff --git a/tests/unit/services/group-sync.sync-members.test.ts b/tests/unit/services/group-sync.sync-members.test.ts index e597eaf..9f3f107 100644 --- a/tests/unit/services/group-sync.sync-members.test.ts +++ b/tests/unit/services/group-sync.sync-members.test.ts @@ -23,6 +23,8 @@ describe('GroupSyncService - syncMembersForActiveGroups (agregado por grupos)', beforeEach(() => { process.env = { ...envBackup, NODE_ENV: 'development' }; // evitar early return + setDb(memdb as any); + try { GroupSyncService.activeGroupsCache.clear(); } catch {} // Reset tablas memdb.exec('DELETE FROM group_members'); memdb.exec('DELETE FROM users');