import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; import { Database } from 'bun:sqlite'; import { initializeDatabase, ensureUserExists } from '../../../src/db'; import { TaskService } from '../../../src/tasks/service'; let memDb: Database; beforeEach(() => { memDb = new Database(':memory:'); initializeDatabase(memDb); TaskService.dbInstance = memDb; }); afterEach(() => { try { memDb.close(); } catch {} }); describe('TaskService.createTask', () => { it('crea una tarea mínima con created_by y sin due_date ni group_id', () => { const creatorRaw = '555111222@s.whatsapp.net'; const createdBy = ensureUserExists(creatorRaw, memDb); expect(createdBy).toBeTruthy(); const id = TaskService.createTask( { description: 'Comprar agua', created_by: createdBy!, due_date: null, group_id: null, }, [] // sin asignaciones ); expect(typeof id).toBe('number'); const task = memDb .prepare( `SELECT id, description, due_date, group_id, created_by, completed FROM tasks WHERE id = ?` ) .get(id) as any; expect(task).toBeTruthy(); expect(task.description).toBe('Comprar agua'); expect(task.due_date).toBeNull(); expect(task.group_id).toBeNull(); expect(task.created_by).toBe(createdBy); expect(task.completed).toBe(0); // BOOLEAN en SQLite como 0/1 }); it('guarda due_date como cadena YYYY-MM-DD y group_id como JID completo', () => { const creatorRaw = '555333444@s.whatsapp.net'; const createdBy = ensureUserExists(creatorRaw, memDb)!; const due = '2025-09-10'; const groupId = '12345@g.us'; // Sembrar el grupo para satisfacer la FK de tasks.group_id -> groups.id memDb .prepare(`INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)`) .run(groupId, 'test-community', 'Grupo de prueba'); const id = TaskService.createTask( { description: 'Pagar servicios', created_by: createdBy, due_date: due, group_id: groupId, }, [] ); const row = memDb .prepare( `SELECT description, due_date, group_id, created_by FROM tasks WHERE id = ?` ) .get(id) as any; expect(row.due_date).toBe(due); expect(row.group_id).toBe(groupId); expect(row.created_by).toBe(createdBy); }); it('inserta asignaciones y evita duplicados por usuario', () => { const creator = ensureUserExists('555000001@s.whatsapp.net', memDb)!; const assigneeA = ensureUserExists('555000002@s.whatsapp.net', memDb)!; const assigneeB = ensureUserExists('555000003@s.whatsapp.net', memDb)!; const id = TaskService.createTask( { description: 'Organizar reunión', created_by: creator, due_date: null, group_id: null, }, [ { user_id: assigneeA, assigned_by: creator }, { user_id: assigneeA, assigned_by: creator }, // duplicado { user_id: assigneeB, assigned_by: creator }, ] ); const count = memDb .prepare( `SELECT COUNT(*) AS c FROM task_assignments WHERE task_id = ?` ) .get(id) as any; expect(count.c).toBe(2); const users = memDb .prepare( `SELECT user_id FROM task_assignments WHERE task_id = ? ORDER BY user_id` ) .all(id) as any[]; expect(users.map(u => u.user_id)).toEqual([assigneeA, assigneeB].sort()); }); it('asegura usuarios inexistentes en asignaciones y no viola FK', () => { const creator = ensureUserExists('555010101@s.whatsapp.net', memDb)!; const id = TaskService.createTask( { description: 'Tarea con asignado nuevo', created_by: creator, due_date: null, group_id: null, }, [ // Usuario no existente: TaskService debe asegurar y crear { user_id: '555099999', assigned_by: creator }, ] ); const counts = memDb .prepare( `SELECT (SELECT COUNT(*) FROM tasks) AS tasks_count, (SELECT COUNT(*) FROM task_assignments WHERE task_id = ?) AS assigns_count` ) .get(id) as any; expect(counts.tasks_count).toBe(1); expect(counts.assigns_count).toBe(1); }); it('lanza error si created_by es inválido (no normalizable) y no inserta la tarea', () => { const invalidCreator = 'invalid-id!'; expect(() => TaskService.createTask( { description: 'No debería insertarse', created_by: invalidCreator, due_date: null, group_id: null, }, [] ) ).toThrow(); const count = memDb .prepare(`SELECT COUNT(*) AS c FROM tasks`) .get() as any; expect(count.c).toBe(0); }); });