feat: añadir TaskService con dbInstance y tests unitarios
Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>pull/1/head
							parent
							
								
									94f6813cb2
								
							
						
					
					
						commit
						7b9928937b
					
				| @ -0,0 +1,170 @@ | ||||
| import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; | ||||
| import Database from 'better-sqlite3'; | ||||
| 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'; | ||||
| 
 | ||||
|     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('hace rollback si una asignación viola FK (usuario inexistente)', () => { | ||||
|     const creator = ensureUserExists('555010101@s.whatsapp.net', memDb)!; | ||||
| 
 | ||||
|     expect(() => | ||||
|       TaskService.createTask( | ||||
|         { | ||||
|           description: 'Tarea con fallo', | ||||
|           created_by: creator, | ||||
|           due_date: null, | ||||
|           group_id: null, | ||||
|         }, | ||||
|         [ | ||||
|           // Usuario no existente: no llamamos ensureUserExists
 | ||||
|           { user_id: '555099999', assigned_by: creator }, | ||||
|         ] | ||||
|       ) | ||||
|     ).toThrow(); | ||||
| 
 | ||||
|     const counts = memDb | ||||
|       .prepare( | ||||
|         `SELECT 
 | ||||
|            (SELECT COUNT(*) FROM tasks) AS tasks_count, | ||||
|            (SELECT COUNT(*) FROM task_assignments) AS assigns_count` | ||||
|       ) | ||||
|       .get() as any; | ||||
| 
 | ||||
|     expect(counts.tasks_count).toBe(0); | ||||
|     expect(counts.assigns_count).toBe(0); | ||||
|   }); | ||||
| 
 | ||||
|   it('lanza error si created_by no existe (FK) y no inserta la tarea', () => { | ||||
|     // No llamamos a ensureUserExists para este created_by
 | ||||
|     const nonExisting = '555123123'; | ||||
| 
 | ||||
|     expect(() => | ||||
|       TaskService.createTask( | ||||
|         { | ||||
|           description: 'No debería insertarse', | ||||
|           created_by: nonExisting, | ||||
|           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); | ||||
|   }); | ||||
| }); | ||||
					Loading…
					
					
				
		Reference in New Issue