diff --git a/tests/web/api.tasks.complete-autoassign-recent.test.ts b/tests/web/api.tasks.complete-autoassign-recent.test.ts deleted file mode 100644 index 3830fad..0000000 --- a/tests/web/api.tasks.complete-autoassign-recent.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { describe, it, beforeAll, afterAll, expect } from 'bun:test'; -import { createTempDb } from './helpers/db'; -import { startWebServer, type RunningServer } from './helpers/server'; - -describe('API Web - complete auto-assign y recientes', () => { - const USER = '34600123456'; - const GROUP = 'g-1@g.us'; - let dbHandle: any; - let cleanupDb: () => void; - let server: RunningServer; - let baseUrl: 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(USER); - dbHandle - .prepare(`INSERT INTO groups (id, community_id, name, active) VALUES (?, 'comm-1', 'Test Group', 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, USER); - - const ins = dbHandle - .prepare(`INSERT INTO tasks (description, group_id, created_by) VALUES ('Hacer algo', ?, ?)`) - .run(GROUP, USER); - taskId = Number(ins.lastInsertRowid); - - server = await startWebServer({ - port: 19101, - env: { DB_PATH: tmp.path, DEV_DEFAULT_USER: USER, TZ: 'UTC' } - }); - baseUrl = server.baseUrl; - }); - - afterAll(async () => { - try { - await server.stop(); - } catch {} - try { - cleanupDb?.(); - } catch {} - }); - - it('completar una tarea sin responsables auto-asigna y aparece en recientes', async () => { - // Completar - const resComplete = await fetch(`${baseUrl}/api/tasks/${taskId}/complete`, { method: 'POST' }); - expect(resComplete.status).toBe(200); - const jsonComplete = await resComplete.json(); - expect(['updated', 'already']).toContain(jsonComplete?.status); - - // Recientes (24h) - const resRecent = await fetch(`${baseUrl}/api/me/tasks?status=recent&limit=50`); - expect(resRecent.status).toBe(200); - const recentBody = await resRecent.json(); - const items = Array.isArray(recentBody?.items) ? recentBody.items : []; - const found = items.find((it: any) => Number(it?.id) === taskId); - expect(found).toBeTruthy(); - expect(found.completed).toBe(true); - expect(typeof found.completed_at === 'string' && found.completed_at.length > 0).toBe(true); - expect(Array.isArray(found.assignees)).toBe(true); - expect(found.assignees.map(String)).toContain(USER); - - // Uncomplete inmediato permitido - const resUncomplete = await fetch(`${baseUrl}/api/tasks/${taskId}/uncomplete`, { method: 'POST' }); - expect(resUncomplete.status).toBe(200); - const jsonUnc = await resUncomplete.json(); - expect(jsonUnc?.status).toBe('updated'); - - // Ya no aparece en recientes tras reabrir - const resRecent2 = await fetch(`${baseUrl}/api/me/tasks?status=recent&limit=50`); - expect(resRecent2.status).toBe(200); - const recentBody2 = await resRecent2.json(); - const items2 = Array.isArray(recentBody2?.items) ? recentBody2.items : []; - const found2 = items2.find((it: any) => Number(it?.id) === taskId); - expect(found2).toBeFalsy(); - }); -}); diff --git a/tests/web/api.tasks.complete-concurrency.test.ts b/tests/web/api.tasks.complete-concurrency.test.ts deleted file mode 100644 index 16b04aa..0000000 --- a/tests/web/api.tasks.complete-concurrency.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -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); - }); -}); diff --git a/tests/web/api.tasks.uncomplete-window.test.ts b/tests/web/api.tasks.uncomplete-window.test.ts deleted file mode 100644 index d024448..0000000 --- a/tests/web/api.tasks.uncomplete-window.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { describe, it, beforeAll, afterAll, expect } from 'bun:test'; -import { createTempDb } from './helpers/db'; -import { startWebServer, type RunningServer } from './helpers/server'; - -describe('API Web - uncomplete dentro de ventana', () => { - const USER = '34600123456'; - const GROUP = 'g-2@g.us'; - let dbHandle: any; - let cleanupDb: () => void; - let server: RunningServer; - let baseUrl: 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(USER); - dbHandle - .prepare(`INSERT INTO groups (id, community_id, name, active) VALUES (?, 'comm-1', 'G2', 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, USER); - - const ins = dbHandle - .prepare(`INSERT INTO tasks (description, group_id, created_by) VALUES ('Probar ventana', ?, ?)`) - .run(GROUP, USER); - taskId = Number(ins.lastInsertRowid); - - server = await startWebServer({ - port: 19102, - env: { DB_PATH: tmp.path, DEV_DEFAULT_USER: USER, TZ: 'UTC' } - }); - baseUrl = server.baseUrl; - }); - - afterAll(async () => { - try { - await server.stop(); - } catch {} - try { - cleanupDb?.(); - } catch {} - }); - - it('uncomplete permitido inmediatamente después de completar y es idempotente', async () => { - // Completar - const resComplete = await fetch(`${baseUrl}/api/tasks/${taskId}/complete`, { method: 'POST' }); - expect(resComplete.status).toBe(200); - - // Uncomplete (dentro de ventana) - const resUncomplete = await fetch(`${baseUrl}/api/tasks/${taskId}/uncomplete`, { method: 'POST' }); - expect(resUncomplete.status).toBe(200); - const jsonUnc = await resUncomplete.json(); - expect(jsonUnc?.status).toBe('updated'); - - // Uncomplete de nuevo (idempotente → already) - const resUncomplete2 = await fetch(`${baseUrl}/api/tasks/${taskId}/uncomplete`, { method: 'POST' }); - expect(resUncomplete2.status).toBe(200); - const jsonUnc2 = await resUncomplete2.json(); - expect(jsonUnc2?.status).toBe('already'); - }); -});