feat: añade onboarding A3 con onboarding_prompted_at y encolado

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
webui
brobert 2 weeks ago
parent 105b13a148
commit 7033c6149f

@ -375,5 +375,19 @@ export const migrations: Migration[] = [
}
} catch {}
}
},
{
version: 13,
name: 'groups-onboarding-prompted-at',
checksum: 'v13-groups-onboarding-2025-10-17',
up: (db: Database) => {
try {
const cols = db.query(`PRAGMA table_info(groups)`).all() as any[];
const hasCol = Array.isArray(cols) && cols.some((c: any) => String(c.name) === 'onboarding_prompted_at');
if (!hasCol) {
db.exec(`ALTER TABLE groups ADD COLUMN onboarding_prompted_at TEXT NULL;`);
}
} catch {}
}
}
];

@ -0,0 +1,83 @@
import { describe, it, beforeEach, afterEach, expect } from 'bun:test';
import Database from 'bun:sqlite';
import { initializeDatabase } from '../../../src/db';
import { GroupSyncService } from '../../../src/services/group-sync';
import { AllowedGroups } from '../../../src/services/allowed-groups';
const envBackup = { ...process.env } as NodeJS.ProcessEnv;
describe('GroupSyncService - onboarding A3', () => {
let memdb: Database;
beforeEach(() => {
process.env = {
...envBackup,
NODE_ENV: 'test',
ONBOARDING_ENABLE_IN_TEST: 'true',
ONBOARDING_PROMPTS_ENABLED: 'true',
ONBOARDING_GRACE_SECONDS: '0',
ONBOARDING_COOLDOWN_DAYS: '7',
ONBOARDING_COVERAGE_THRESHOLD: '1',
CHATBOT_PHONE_NUMBER: '555111222'
};
memdb = new Database(':memory:');
initializeDatabase(memdb);
(GroupSyncService as any).dbInstance = memdb;
(AllowedGroups as any).dbInstance = memdb;
// Sembrar grupo activo
memdb.prepare(`INSERT INTO groups (id, community_id, name, active, last_verified) VALUES (?,?,?,?, strftime('%Y-%m-%d %H:%M:%f','now'))`)
.run('g1@g.us', 'comm-1', 'Grupo 1', 1);
});
afterEach(() => {
memdb.close();
process.env = envBackup;
});
it('publica prompt cuando coverage < 100, grace cumplido y sin cooldown', () => {
// snapshot con un resoluble (dígitos) y uno no resoluble (alias sin mapeo)
const res = GroupSyncService.reconcileGroupMembers('g1@g.us', [
{ userId: '111', isAdmin: false },
{ userId: 'alias_lid', isAdmin: false }
], '2025-01-01 00:00:00.000');
expect(res).toEqual(expect.objectContaining({ added: 2 }));
const row = memdb.query(`SELECT recipient, message, status FROM response_queue ORDER BY id DESC LIMIT 1`).get() as any;
expect(row).toBeTruthy();
expect(row.recipient).toBe('g1@g.us');
expect(String(row.message)).toContain('https://wa.me/555111222');
const g = memdb.query(`SELECT onboarding_prompted_at FROM groups WHERE id = 'g1@g.us'`).get() as any;
expect(g).toBeTruthy();
expect(g.onboarding_prompted_at).toBeTruthy();
});
it('omite prompt cuando coverage = 100', () => {
// Previo: no debe existir prompt para este grupo
const before = memdb.query(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
expect(Number(before.c)).toBe(0);
// snapshot totalmente resoluble (dos dígitos)
GroupSyncService.reconcileGroupMembers('g1@g.us', [
{ userId: '111', isAdmin: false },
{ userId: '222', isAdmin: false }
], '2025-01-01 00:00:00.000');
const after = memdb.query(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
expect(Number(after.c)).toBe(0);
});
it('omite prompt en modo enforce si el grupo no está allowed', () => {
process.env.GROUP_GATING_MODE = 'enforce';
AllowedGroups.setStatus('g1@g.us', 'blocked');
GroupSyncService.reconcileGroupMembers('g1@g.us', [
{ userId: '111', isAdmin: false },
{ userId: 'alias_lid', isAdmin: false }
], '2025-01-01 00:00:00.000');
const count = memdb.query(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
expect(Number(count.c)).toBe(0);
});
});
Loading…
Cancel
Save