import test from 'node:test'; import assert from 'node:assert/strict'; import { openDb, setSetting } from '../src/db.mjs'; import { zproConfigured, createZproClient } from '../src/outreach/zpro-client.mjs'; test('zproConfigured e false sem credenciais na tela nem no .env', () => { const db = openDb(':memory:'); delete process.env.ZPRO_BASE_URL; delete process.env.ZPRO_API_ID; delete process.env.ZPRO_BEARER_TOKEN; assert.equal(zproConfigured(db), false); db.close(); }); test('zproConfigured fica true com credenciais salvas na tela (settings)', () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'exemplo.zpro.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-secreto'); assert.equal(zproConfigured(db), true); db.close(); }); test('zpro.bearerToken vazio na tela cai pro ZPRO_BEARER_TOKEN do .env', () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'exemplo.zpro.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); process.env.ZPRO_BEARER_TOKEN = 'token-do-env'; assert.equal(zproConfigured(db), true); delete process.env.ZPRO_BEARER_TOKEN; db.close(); }); test('valor preenchido na tela tem prioridade sobre o .env', () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'exemplo.zpro.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); process.env.ZPRO_BEARER_TOKEN = 'token-do-env'; assert.equal(zproConfigured(db), true); delete process.env.ZPRO_BEARER_TOKEN; db.close(); }); test('baseUrl colado com https:// na frente nao vira URL duplicada (https://https://...)', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'https://apigchat.exemplo.com.br/'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; let urlChamada; global.fetch = async (url) => { urlChamada = String(url); return { ok: true, text: async () => '{}' }; }; try { await createZproClient(db).listTags(); } finally { global.fetch = originalFetch; } assert.equal(urlChamada, 'https://apigchat.exemplo.com.br/v2/api/external/abc123/listTags?isActive=true'); db.close(); }); test('token colado com Bearer e normalizado antes de enviar', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'Bearer token-da-tela'); const originalFetch = global.fetch; let authorization; global.fetch = async (url, options) => { authorization = options.headers.authorization; return { ok: true, text: async () => '{}' }; }; try { await createZproClient(db).listTags(); } finally { global.fetch = originalFetch; } assert.equal(authorization, 'Bearer token-da-tela'); db.close(); }); test('erro de auth do Z-PRO vira mensagem acionavel', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; global.fetch = async () => ({ ok: false, status: 403, text: async () => '{"error":"ERR_SESSION_NOT_AUTH_TOKEN"}' }); try { await assert.rejects( () => createZproClient(db).listTags(), /Token e o API ID sao os de Admin > API/ ); } finally { global.fetch = originalFetch; } db.close(); }); test('createTicket extrai ticketId de resposta com JSON embutido em message', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; global.fetch = async () => ({ ok: true, status: 200, text: async () => JSON.stringify({ success: true, data: { message: 'Ticket create successfully {"id":444202,"status":"pending"}', error: false } }) }); try { const resposta = await createZproClient(db).createTicket({ number: '5511999990001', message: 'oi', externalKey: 'teste', userId: 15, channelId: 207 }); assert.equal(resposta.ticketId, '444202'); } finally { global.fetch = originalFetch; } db.close(); }); test('createTicket tenta sem channelId quando Z-PRO rejeita o canal informado', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; const bodies = []; global.fetch = async (url, options) => { const body = JSON.parse(options.body); bodies.push(body); if (body.channelId) { return { ok: false, status: 404, text: async () => '{"success":false,"error":true}' }; } return { ok: true, status: 200, text: async () => JSON.stringify({ success: true, data: { message: 'Ticket create successfully {"id":444206}' } }) }; }; try { const resposta = await createZproClient(db).createTicket({ number: '5511999990001', message: 'oi', externalKey: 'teste', userId: 15, channelId: 207 }); assert.equal(resposta.ticketId, '444206'); } finally { global.fetch = originalFetch; } assert.equal(bodies.length, 2); assert.equal(bodies[0].channelId, 207); assert.equal('channelId' in bodies[1], false); db.close(); }); test('sendTemplateWaba inclui multiplas variaveis no components.body.parameters', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; let payload; global.fetch = async (url, options) => { payload = JSON.parse(options.body); return { ok: true, status: 200, text: async () => '{"success":true,"data":{"ticketId":123}}' }; }; try { await createZproClient(db).sendTemplateWaba({ number: '5511999990001', templateName: 'confirma_suporte', languageCode: 'pt_BR', variables: ['Gustavo', 'Chamado #123'] }); } finally { global.fetch = originalFetch; } assert.deepEqual(payload.templateData.template.components, [ { type: 'body', parameters: [{ type: 'text', text: 'Gustavo' }, { type: 'text', text: 'Chamado #123' }] } ]); db.close(); }); test('sendTemplateWaba permite escolher rota templateBody', async () => { const db = openDb(':memory:'); setSetting(db, 'zpro.baseUrl', 'apigchat.exemplo.com.br'); setSetting(db, 'zpro.apiId', 'abc123'); setSetting(db, 'zpro.bearerToken', 'token-da-tela'); const originalFetch = global.fetch; let urlChamada; global.fetch = async (url) => { urlChamada = String(url); return { ok: true, status: 200, text: async () => '{"success":true}' }; }; try { await createZproClient(db).sendTemplateWaba({ number: '5511999990001', templateName: 'confirma_suporte', route: 'templateBody' }); } finally { global.fetch = originalFetch; } assert.equal(urlChamada, 'https://apigchat.exemplo.com.br/v2/api/external/abc123/templateBody'); db.close(); });