import { describe, it, beforeAll, afterAll, expect } from 'bun:test'; import { createTempDb } from './helpers/db'; import { startWebServer, type RunningServer } from './helpers/server'; describe('API Web - carreras en complete con auto-asignación', () => { const U1 = '34600123456'; const U2 = '34600123457'; const GROUP = 'g-3@g.us'; let dbHandle: any; let cleanupDb: () => void; let serverA: RunningServer; let serverB: RunningServer; let baseA: string; let baseB: string; let taskId: number; beforeAll(async () => { const tmp = createTempDb(); dbHandle = tmp.db; cleanupDb = tmp.cleanup; // Semilla mínima dbHandle.prepare(`INSERT INTO users (id) VALUES (?) ON CONFLICT(id) DO NOTHING`).run(U1); dbHandle.prepare(`INSERT INTO users (id) VALUES (?) ON CONFLICT(id) DO NOTHING`).run(U2); dbHandle .prepare(`INSERT INTO groups (id, community_id, name, active) VALUES (?, 'comm-1', 'G3', 1)`) .run(GROUP); dbHandle.prepare(`INSERT INTO allowed_groups (group_id, status) VALUES (?, 'allowed')`).run(GROUP); dbHandle .prepare(`INSERT INTO group_members (group_id, user_id, is_admin, is_active) VALUES (?, ?, 0, 1)`) .run(GROUP, U1); dbHandle .prepare(`INSERT INTO group_members (group_id, user_id, is_admin, is_active) VALUES (?, ?, 0, 1)`) .run(GROUP, U2); const ins = dbHandle .prepare(`INSERT INTO tasks (description, group_id, created_by) VALUES ('Carrera complete', ?, ?)`) .run(GROUP, U1); taskId = Number(ins.lastInsertRowid); // Iniciar dos servidores contra la misma DB serverA = await startWebServer({ port: 19111, env: { DB_PATH: tmp.path, DEV_DEFAULT_USER: U1, TZ: 'UTC' } }); baseA = serverA.baseUrl; serverB = await startWebServer({ port: 19112, env: { DB_PATH: tmp.path, DEV_DEFAULT_USER: U2, TZ: 'UTC' } }); baseB = serverB.baseUrl; }); afterAll(async () => { try { await serverA.stop(); } catch {} try { await serverB.stop(); } catch {} try { cleanupDb?.(); } catch {} }); it('solo una actualización de completed; la asignación incluye al menos al completador', async () => { const [r1, r2] = await Promise.all([ fetch(`${baseA}/api/tasks/${taskId}/complete`, { method: 'POST' }), fetch(`${baseB}/api/tasks/${taskId}/complete`, { method: 'POST' }) ]); expect([200, 200]).toContain(r1.status); expect([200, 200]).toContain(r2.status); const row = dbHandle .prepare(`SELECT COALESCE(completed,0) as completed, completed_by FROM tasks WHERE id = ?`) .get(taskId) as any; expect(Number(row?.completed || 0)).toBe(1); const completedBy = String(row?.completed_by || ''); const assigns = dbHandle .prepare(`SELECT user_id FROM task_assignments WHERE task_id = ? ORDER BY assigned_at ASC`) .all(taskId) as any[]; // Debe existir al menos una asignación y contener al que completó expect(assigns.length >= 1 && assigns.length <= 2).toBe(true); const assignedUsers = assigns.map((r) => String(r.user_id)); expect([U1, U2]).toContain(completedBy); expect(assignedUsers).toContain(completedBy); }); });