Carry over pre-existing WABA outreach automation WIP
This work already existed uncommitted in the main working tree (not authored by this branch) and is required as a runtime dependency for the campaign outreach port (zpro-client, templates, outreach routes/UI). Copied in as-is, untouched, so this feature branch has a working baseline; the original uncommitted state in the main checkout is left exactly as it was.
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
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();
|
||||
});
|
||||
Reference in New Issue
Block a user