import { beforeEach, afterEach, describe, expect, it } from 'bun:test'; import { createTempDb } from './helpers/db'; // Reutilizamos el estilo de import dinámico del handler como en otros tests web. describe('Web API - completar tarea (rutas de error y gating)', () => { let cleanup: () => void; let db: any; let path: string; const USER = '34600123456'; const GROUP_ID = '12345-67890@g.us'; beforeEach(() => { const tmp = createTempDb(); cleanup = tmp.cleanup; db = tmp.db; path = tmp.path; process.env.NODE_ENV = 'test'; process.env.DB_PATH = path; process.env.REACTIONS_ENABLED = 'true'; process.env.REACTIONS_SCOPE = 'groups'; process.env.REACTIONS_TTL_DAYS = '14'; process.env.GROUP_GATING_MODE = 'enforce'; }); afterEach(async () => { try { const { closeDb } = await import('../../apps/web/src/lib/server/db.ts'); closeDb(); } catch {} if (cleanup) cleanup(); delete process.env.DB_PATH; delete process.env.REACTIONS_ENABLED; delete process.env.REACTIONS_SCOPE; delete process.env.REACTIONS_TTL_DAYS; delete process.env.GROUP_GATING_MODE; }); it('401 si no hay session (userId)', async () => { const event: any = { locals: { userId: null }, params: { id: '1' }, request: new Request('http://localhost', { method: 'POST' }) }; const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(401); }); it('400 si id no es válido', async () => { const event: any = { locals: { userId: USER }, params: { id: 'abc' }, request: new Request('http://localhost', { method: 'POST' }) }; const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(400); }); it('404 si la tarea no existe', async () => { const event: any = { locals: { userId: USER }, params: { id: '999999' }, request: new Request('http://localhost', { method: 'POST' }) }; // Sembrar usuario para evitar FKs indirectas (no imprescindible aquí) db.prepare(`INSERT OR IGNORE INTO users (id) VALUES (?)`).run(USER); const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(404); }); it('403 si grupo no allowed', async () => { db.prepare(`INSERT OR IGNORE INTO users (id) VALUES (?)`).run(USER); // Asegurar fila del grupo para satisfacer FKs (sin marcarlo como allowed) db.prepare(`INSERT OR IGNORE INTO groups (id, community_id, name, active) VALUES (?, 'comm', 'G', 1)`).run(GROUP_ID); // Tarea con group_id; no insertamos en allowed_groups const taskId = Number( db.prepare(` INSERT INTO tasks (description, due_date, group_id, created_by, completed, completed_at) VALUES ('No allowed', NULL, ?, ?, 0, NULL) `).run(GROUP_ID, USER).lastInsertRowid ); // El usuario podría incluso ser miembro activo; sigue siendo 403 por falta de allowed db.prepare(`INSERT OR REPLACE INTO group_members (group_id, user_id, is_admin, is_active) VALUES (?, ?, 0, 1)`).run(GROUP_ID, USER); const event: any = { locals: { userId: USER }, params: { id: String(taskId) }, request: new Request('http://localhost', { method: 'POST' }) }; const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(403); }); it('403 si grupo allowed pero usuario no es miembro activo', async () => { db.prepare(`INSERT OR IGNORE INTO users (id) VALUES (?)`).run(USER); db.prepare(`INSERT OR IGNORE INTO groups (id, community_id, name, active) VALUES (?, 'comm', 'G', 1)`).run(GROUP_ID); db.prepare(`INSERT OR REPLACE INTO allowed_groups (group_id, label, status) VALUES (?, 'G', 'allowed')`).run(GROUP_ID); const taskId = Number( db.prepare(` INSERT INTO tasks (description, due_date, group_id, created_by, completed, completed_at) VALUES ('No active member', NULL, ?, ?, 0, NULL) `).run(GROUP_ID, USER).lastInsertRowid ); // No sembramos group_members activo para USER const event: any = { locals: { userId: USER }, params: { id: String(taskId) }, request: new Request('http://localhost', { method: 'POST' }) }; const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(403); }); it('403 en tarea personal si no está asignada al usuario', async () => { db.prepare(`INSERT OR IGNORE INTO users (id) VALUES (?)`).run(USER); const taskId = Number( db.prepare(` INSERT INTO tasks (description, due_date, group_id, created_by, completed, completed_at) VALUES ('Personal sin asignación', NULL, NULL, ?, 0, NULL) `).run(USER).lastInsertRowid ); // No insertamos task_assignments const event: any = { locals: { userId: USER }, params: { id: String(taskId) }, request: new Request('http://localhost', { method: 'POST' }) }; const { POST: completeHandler } = await import('../../apps/web/src/routes/api/tasks/[id]/complete/+server.ts'); const res = await completeHandler(event); expect(res.status).toBe(403); }); });