From b80cc3ea3c4d7c66f31c823f7b93b1049caef8c2 Mon Sep 17 00:00:00 2001 From: borja Date: Fri, 5 Sep 2025 18:03:19 +0200 Subject: [PATCH] feat: crear tabla response_queue, actualizar tests DB y README Co-authored-by: aider (openrouter/openai/gpt-5) --- README.md | 2 +- src/db.ts | 20 ++++++++++++++++++++ tests/unit/db.test.ts | 10 +++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac2d281..cc402f9 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ graph TD ## Arquitectura de la cola persistente (MVP) - Estados: queued | processing | sent | failed. -- Campos mínimos por mensaje: id (PK), recipient, type (text), payload_text, status, attempts (0), last_error (nullable), created_at, updated_at. +- Campos mínimos por mensaje: id (PK), recipient, type (text), message, status, attempts (0), last_error (nullable), created_at, updated_at. - Índices recomendados: (status, created_at) para seleccionar pendientes rápidamente. - Sin orden estricto por chat; el envío puede intercalarse entre destinatarios. - Concurrencia: N workers globales operando en bucle, cada uno toma mensajes en estado queued y los marca processing. diff --git a/src/db.ts b/src/db.ts index 25d4445..54428a3 100644 --- a/src/db.ts +++ b/src/db.ts @@ -72,6 +72,26 @@ export function initializeDatabase(instance: Database) { FOREIGN KEY (assigned_by) REFERENCES users(id) ON DELETE CASCADE ); `); + + // Create response_queue table (persistent outbox for replies) + instance.exec(` + CREATE TABLE IF NOT EXISTS response_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + recipient TEXT NOT NULL, + message TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','processing','sent','failed')), + attempts INTEGER NOT NULL DEFAULT 0, + last_error TEXT NULL, + created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now')), + updated_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now')) + ); + `); + + // Index to fetch pending items efficiently + instance.exec(` + CREATE INDEX IF NOT EXISTS idx_response_queue_status_created_at + ON response_queue (status, created_at); + `); } /** diff --git a/tests/unit/db.test.ts b/tests/unit/db.test.ts index 45fff44..0f3180f 100644 --- a/tests/unit/db.test.ts +++ b/tests/unit/db.test.ts @@ -38,7 +38,7 @@ describe('Database', () => { .map((t: any) => t.name); // Order matters if foreign keys are involved during creation, though SQLite is flexible - const expectedTables = ['users', 'groups', 'tasks', 'task_assignments']; + const expectedTables = ['users', 'groups', 'tasks', 'task_assignments', 'response_queue']; const userTables = tables.filter(t => !t.startsWith('sqlite_')); // Check if all expected tables exist, order might vary slightly depending on execution expect(userTables).toHaveLength(expectedTables.length); @@ -82,6 +82,14 @@ describe('Database', () => { const group = testDb.query("SELECT active FROM groups WHERE id = 'test-group'").get(); expect(group.active).toBe(1); // SQLite uses 1 for TRUE }); + + test('response_queue table should have required columns', () => { + const columns = testDb + .query("PRAGMA table_info(response_queue)") + .all() + .map((c: any) => c.name); + expect(columns).toEqual(['id', 'recipient', 'message', 'status', 'attempts', 'last_error', 'created_at', 'updated_at']); + }); }); describe('Foreign Keys', () => {