feat: aplicar gating por AllowedGroups en tareas y recordatorios
Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>pull/1/head
parent
c51cb3f124
commit
db9f71abaa
@ -0,0 +1,75 @@
|
||||
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 { RemindersService } from '../../../src/services/reminders';
|
||||
import { AllowedGroups } from '../../../src/services/allowed-groups';
|
||||
import { ResponseQueue } from '../../../src/services/response-queue';
|
||||
|
||||
describe('RemindersService - gating por grupos en modo enforce', () => {
|
||||
const envBackup = process.env;
|
||||
let memdb: Database;
|
||||
let originalAdd: any;
|
||||
let sent: any[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...envBackup, NODE_ENV: 'test', GROUP_GATING_MODE: 'enforce', TZ: 'Europe/Madrid' };
|
||||
memdb = new Database(':memory:');
|
||||
initializeDatabase(memdb);
|
||||
(TaskService as any).dbInstance = memdb;
|
||||
(RemindersService as any).dbInstance = memdb;
|
||||
(AllowedGroups as any).dbInstance = memdb;
|
||||
|
||||
// Stub de ResponseQueue
|
||||
originalAdd = (ResponseQueue as any).add;
|
||||
(ResponseQueue as any).add = async (msgs: any[]) => { sent.push(...msgs); };
|
||||
sent = [];
|
||||
|
||||
// Preferencias del usuario receptor
|
||||
memdb.exec(`
|
||||
INSERT INTO user_preferences (user_id, reminder_freq, reminder_time, last_reminded_on, updated_at)
|
||||
VALUES ('34600123456', 'daily', '00:00', NULL, strftime('%Y-%m-%d %H:%M:%f','now'))
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
reminder_freq = excluded.reminder_freq,
|
||||
reminder_time = excluded.reminder_time,
|
||||
last_reminded_on = NULL,
|
||||
updated_at = excluded.updated_at
|
||||
`);
|
||||
|
||||
// Sembrar grupos y estados
|
||||
memdb.exec(`INSERT OR IGNORE INTO groups (id) VALUES ('ok@g.us')`);
|
||||
memdb.exec(`INSERT OR IGNORE INTO groups (id) VALUES ('na@g.us')`);
|
||||
AllowedGroups.setStatus('ok@g.us', 'allowed', 'OK');
|
||||
AllowedGroups.setStatus('na@g.us', 'allowed', 'NA'); // inicialmente allowed para que las tareas se creen con group_id
|
||||
|
||||
// Crear dos tareas, una en cada grupo, asignadas al usuario
|
||||
TaskService.createTask(
|
||||
{ description: 'Tarea OK', created_by: '34600123456', group_id: 'ok@g.us', due_date: null },
|
||||
[{ user_id: '34600123456', assigned_by: '34600123456' }]
|
||||
);
|
||||
TaskService.createTask(
|
||||
{ description: 'Tarea NA', created_by: '34600123456', group_id: 'na@g.us', due_date: null },
|
||||
[{ user_id: '34600123456', assigned_by: '34600123456' }]
|
||||
);
|
||||
|
||||
// Cambiar a bloqueado uno de los grupos antes de correr los recordatorios
|
||||
AllowedGroups.setStatus('na@g.us', 'blocked', 'NA');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(ResponseQueue as any).add = originalAdd;
|
||||
memdb.close();
|
||||
process.env = envBackup;
|
||||
});
|
||||
|
||||
it('omite tareas de grupos no allowed en los recordatorios', async () => {
|
||||
await RemindersService.runOnce(new Date());
|
||||
|
||||
expect(sent.length).toBe(1);
|
||||
const msg = String(sent[0].message);
|
||||
|
||||
// Debe mencionar el grupo allowed y omitir el bloqueado
|
||||
expect(msg).toContain('ok@g.us');
|
||||
expect(msg).not.toContain('na@g.us');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,84 @@
|
||||
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) {
|
||||
// Intento genérico de seed para la tabla groups con columnas comunes
|
||||
const cols = db.query(`PRAGMA table_info(groups)`).all() as any[];
|
||||
const colNames = cols.map(c => String(c.name));
|
||||
const values: Record<string, any> = {};
|
||||
for (const c of colNames) {
|
||||
if (c === 'id') values[c] = groupId;
|
||||
else if (c === 'name' || c === 'title' || c === 'subject') values[c] = 'Test Group';
|
||||
else if (c === 'is_active' || c === 'active') values[c] = 1;
|
||||
else if (c.endsWith('_at')) values[c] = new Date().toISOString().replace('T', ' ').replace('Z', '');
|
||||
else if (c === 'created_by') values[c] = 'tester';
|
||||
// Para otras columnas dejaremos NULL si lo permite
|
||||
}
|
||||
const colsList = Object.keys(values);
|
||||
const placeholders = colsList.map(() => '?').join(', ');
|
||||
const sql = `INSERT OR IGNORE 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);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue