import test from 'node:test'; import assert from 'node:assert/strict'; import { openDb, getSetting, seedSettings } from '../src/db.mjs'; import { createRouter } from '../src/server/router.mjs'; import { registerMetaRoutes } from '../src/server/routes/meta.mjs'; function setup({ whatsapp = { getStatus: () => ({ pendingSends: 0 }) } } = {}) { const db = openDb(':memory:'); seedSettings(db); const router = createRouter(); registerMetaRoutes(router, { db, jobs: { activeJobs: () => [] }, whatsapp }); return { db, router }; } async function call(router, method, pathname, { body, user } = {}) { const match = router.match(method, pathname); let response; await match.handler({}, {}, { ...match, body, user, json(status, data) { response = { status, data }; } }); return response; } test('somente admin altera configuracoes e apenas chaves permitidas', async () => { const { db, router } = setup(); const denied = await call(router, 'PUT', '/api/settings', { user: { role: 'comercial' }, body: { 'outreach.ativo': 'true' } }); assert.equal(denied.status, 403); assert.equal(getSetting(db, 'outreach.ativo'), 'false'); const unknown = await call(router, 'PUT', '/api/settings', { user: { role: 'admin' }, body: { 'chave.arbitraria': 'valor' } }); assert.equal(unknown.status, 400); assert.equal(getSetting(db, 'chave.arbitraria'), null); const allowed = await call(router, 'PUT', '/api/settings', { user: { role: 'admin' }, body: { 'outreach.ativo': 'true', 'outreach.mensagensPorDia': '5' } }); assert.equal(allowed.status, 200); assert.equal(getSetting(db, 'outreach.ativo'), 'true'); assert.equal(getSetting(db, 'outreach.mensagensPorDia'), '5'); db.close(); }); test('settings valida meta, limite e booleano estritamente', async () => { const { db, router } = setup(); for (const body of [ { meta_diaria: '0' }, { meta_diaria: '1.5' }, { 'outreach.mensagensPorDia': '1001' }, { 'outreach.ativo': 'sim' }, { 'outreach.ativo': true } ]) assert.equal((await call(router, 'PUT', '/api/settings', { user: { role: 'admin' }, body })).status, 400); assert.equal((await call(router, 'PUT', '/api/settings', { user: { role: 'admin' }, body: { meta_diaria: '500', 'outreach.mensagensPorDia': '20', 'outreach.ativo': 'false' } })).status, 200); assert.equal(getSetting(db, 'meta_diaria'), '500'); db.close(); }); test('reset exige senha configurada e bloqueia enquanto ha job ativo', async () => { const previous = process.env.APP_RESET_PASSWORD; delete process.env.APP_RESET_PASSWORD; const { db, router } = setup(); const admin = { role: 'admin' }; assert.equal((await call(router, 'POST', '/api/admin/reset', { user: admin, body: { senha: 'x' } })).status, 503); process.env.APP_RESET_PASSWORD = 'segredo'; const busyRouter = createRouter(); registerMetaRoutes(busyRouter, { db, jobs: { activeJobs: () => [{ jobId: '1' }] }, whatsapp: { getStatus: () => ({ pendingSends: 0 }) } }); assert.equal((await call(busyRouter, 'POST', '/api/admin/reset', { user: admin, body: { senha: 'segredo' } })).status, 409); if (previous === undefined) delete process.env.APP_RESET_PASSWORD; else process.env.APP_RESET_PASSWORD = previous; db.close(); }); test('reset bloqueia envio WhatsApp pendente e jobs persistentes fora do manager', async () => { const previous = process.env.APP_RESET_PASSWORD; process.env.APP_RESET_PASSWORD = 'segredo'; const admin = { role: 'admin' }; const sending = setup({ whatsapp: { getStatus: () => ({ pendingSends: 1 }) } }); assert.equal((await call(sending.router, 'POST', '/api/admin/reset', { user: admin, body: { senha: 'segredo' } })).status, 409); sending.db.close(); const persistent = setup(); persistent.db.prepare("INSERT INTO exports (tipo, status) VALUES ('excel', 'pending')").run(); assert.equal((await call(persistent.router, 'POST', '/api/admin/reset', { user: admin, body: { senha: 'segredo' } })).status, 409); assert.equal(persistent.db.prepare('SELECT COUNT(*) total FROM exports').get().total, 1); persistent.db.close(); if (previous === undefined) delete process.env.APP_RESET_PASSWORD; else process.env.APP_RESET_PASSWORD = previous; });