import { describe, it, beforeEach, afterEach, expect } from 'bun:test'; import Database from 'bun:sqlite'; import { initializeDatabase } from '../../../src/db'; import { TaskService } from '../../../src/tasks/service'; import { AllowedGroups } from '../../../src/services/allowed-groups'; function seedGroup(db: Database, groupId: string) { // Sembrado robusto: cubrir columnas NOT NULL sin valor por defecto const cols = db.query(`PRAGMA table_info(groups)`).all() as any[]; const values: Record = {}; const nowIso = new Date().toISOString().replace('T', ' ').replace('Z', ''); for (const c of cols) { const name = String(c.name); const type = String(c.type || '').toUpperCase(); const notnull = Number(c.notnull || 0) === 1; const hasDefault = c.dflt_value != null; if (name === 'id') { values[name] = groupId; continue; } // Preconfigurar algunos alias comunes if (name === 'name' || name === 'title' || name === 'subject') { values[name] = 'Test Group'; continue; } if (name === 'created_by') { values[name] = 'tester'; continue; } if (name.endsWith('_at')) { values[name] = nowIso; continue; } if (name === 'is_active' || name === 'active') { values[name] = 1; continue; } // Para columnas NOT NULL sin valor por defecto, asignar valores genéricos if (notnull && !hasDefault) { if (type.includes('INT')) values[name] = 1; else if (type.includes('REAL')) values[name] = 0; else values[name] = 'N/A'; } } // Asegurar que id esté siempre if (!('id' in values)) values['id'] = groupId; const colsList = Object.keys(values); const placeholders = colsList.map(() => '?').join(', '); const sql = `INSERT OR REPLACE INTO groups (${colsList.join(', ')}) VALUES (${placeholders})`; db.prepare(sql).run(...colsList.map(k => values[k])); } describe('TaskService - gating en creación con group_id (enforce)', () => { const envBackup = process.env; let memdb: Database; beforeEach(() => { process.env = { ...envBackup, NODE_ENV: 'test', GROUP_GATING_MODE: 'enforce' }; memdb = new Database(':memory:'); initializeDatabase(memdb); (TaskService as any).dbInstance = memdb; (AllowedGroups as any).dbInstance = memdb; }); afterEach(() => { process.env = envBackup; memdb.close(); }); it('fuerza group_id=null cuando el grupo no está allowed', () => { const gid = 'na@g.us'; seedGroup(memdb, gid); AllowedGroups.setStatus(gid, 'blocked'); const taskId = TaskService.createTask( { description: 'Probar gating', due_date: null, group_id: gid, created_by: '34600123456', }, [{ user_id: '34600123456', assigned_by: '34600123456' }] ); const row = memdb .query(`SELECT group_id FROM tasks WHERE id = ?`) .get(taskId) as any; expect(row?.group_id).toBeNull(); }); it('conserva group_id cuando el grupo está allowed', () => { const gid = 'ok@g.us'; seedGroup(memdb, gid); AllowedGroups.setStatus(gid, 'allowed'); const taskId = TaskService.createTask( { description: 'Tarea en grupo allowed', due_date: null, group_id: gid, created_by: '34600123456', }, [{ user_id: '34600123456', assigned_by: '34600123456' }] ); const row = memdb .query(`SELECT group_id FROM tasks WHERE id = ?`) .get(taskId) as any; expect(String(row?.group_id)).toBe(gid); }); });