import { describe, it, expect, beforeAll, beforeEach } from 'bun:test'; import { Database } from 'bun:sqlite'; import { initializeDatabase, ensureUserExists } from '../../../src/db'; import { TaskService } from '../../../src/tasks/service'; import { CommandService } from '../../../src/services/command'; describe('CommandService - /t tomar y /t soltar', () => { let memdb: Database; beforeAll(() => { memdb = new Database(':memory:'); initializeDatabase(memdb); TaskService.dbInstance = memdb; CommandService.dbInstance = memdb; }); beforeEach(() => { memdb.exec(` DELETE FROM task_assignments; DELETE FROM tasks; DELETE FROM users; `); }); function createTask(description: string, creator: string, due?: string | null, assignees: string[] = []): number { const createdBy = ensureUserExists(creator, memdb)!; const taskId = TaskService.createTask( { description, due_date: due ?? null, group_id: null, created_by: createdBy, }, assignees.map(uid => ({ user_id: ensureUserExists(uid, memdb)!, assigned_by: createdBy })) ); return taskId; } function getDisplayCode(id: number): number { const row = memdb.prepare('SELECT display_code FROM tasks WHERE id = ?').get(id) as any; return Number(row?.display_code || 0); } function code4(n: number): string { return '`' + String(n).padStart(4, '0') + '`'; } const ctx = (sender: string, message: string) => ({ sender, groupId: '', // DM o vacío; sin relevancia para tomar/soltar message, mentions: [] as string[], }); it('tomar: uso inválido (sin id)', async () => { const res = await CommandService.handle(ctx('111', '/t tomar')); expect(res).toHaveLength(1); expect(res[0].recipient).toBe('111'); expect(res[0].message).toContain('Uso: `/t tomar 26`'); }); it('tomar: not_found', async () => { const res = await CommandService.handle(ctx('111', '/t tomar 99999')); expect(res[0].message).toContain('no encontrada'); }); it('tomar: happy y luego already', async () => { const taskId = createTask('Desc tomar', '999', '2025-09-12'); const dc = getDisplayCode(taskId); const r1 = await CommandService.handle(ctx('111', `/t tomar ${dc}`)); expect(r1[0].message).toContain('Has tomado'); expect(r1[0].message).toContain(code4(dc)); expect(r1[0].message).toContain('Desc tomar'); expect(r1[0].message).toContain('📅'); // formato dd/MM const r2 = await CommandService.handle(ctx('111', `/t tomar ${dc}`)); expect(r2[0].message).toContain('ya la tenías'); }); it('tomar: completed', async () => { const taskId = createTask('Tarea completa', '111', '2025-10-10'); const comp = TaskService.completeTask(taskId, '111'); expect(comp.status).toBe('updated'); const dc = getDisplayCode(taskId); const res = await CommandService.handle(ctx('222', `/t tomar ${dc}`)); expect(res[0].message).toContain('ya estaba completada'); }); it('soltar: uso inválido (sin id)', async () => { const res = await CommandService.handle(ctx('111', '/t soltar')); expect(res[0].message).toContain('Uso: `/t soltar 26`'); }); it('soltar: not_found', async () => { const res = await CommandService.handle(ctx('111', '/t soltar 123456')); expect(res[0].message).toContain('no encontrada'); }); it('soltar: personal única asignación → denegado', async () => { const taskId = createTask('Desc soltar', '999', '2025-09-12', ['111']); const dc = getDisplayCode(taskId); const res = await CommandService.handle(ctx('111', `/t soltar ${dc}`)); expect(res[0].message).toContain('No puedes soltar una tarea personal. Márcala como completada para eliminarla'); }); it('soltar: not_assigned muestra mensaje informativo', async () => { const taskId = createTask('Nunca asignada a 111', '999', null, ['222']); const dc = getDisplayCode(taskId); const res = await CommandService.handle(ctx('111', `/t soltar ${dc}`)); expect(res[0].message).toContain('no la tenías asignada'); }); it('soltar: completed', async () => { const taskId = createTask('Completa y soltar', '111', null, ['111']); const comp = TaskService.completeTask(taskId, '111'); expect(comp.status).toBe('updated'); const dc = getDisplayCode(taskId); const res = await CommandService.handle(ctx('111', `/t soltar ${dc}`)); expect(res[0].message).toContain('ya estaba completada'); }); });