chore: import relatorio (subtree)
git-subtree-dir: relatorio git-subtree-mainline:e322cec92bgit-subtree-split:34bcb132f3
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Smoke test do workflow GERADO (nao dos modulos fonte, que ja tem testes
|
||||
* de unidade em test/*.test.js). Executa, com Node puro + vm, exatamente o
|
||||
* texto que sera colado em cada Code node do n8n, simulando $()/$input/$json
|
||||
* com dados de exemplo (mesmos numeros de referencia da missao, 20/08/2026).
|
||||
*
|
||||
* Uso: node tools/smoke_test_generated_nodes.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
|
||||
const WF_PATH = path.join(__dirname, '..', 'Resumo Operacional GLPI v2 - IA.n8n.json');
|
||||
const workflow = JSON.parse(fs.readFileSync(WF_PATH, 'utf8'));
|
||||
|
||||
function nodeByName(name) {
|
||||
const n = workflow.nodes.find((x) => x.name === name);
|
||||
if (!n) throw new Error(`node nao encontrado no workflow gerado: ${name}`);
|
||||
return n;
|
||||
}
|
||||
|
||||
/** registry simula a memoria de execucao do n8n: nome do node -> lista de items {json} */
|
||||
const registry = new Map();
|
||||
|
||||
function makeDollar() {
|
||||
return function $(nodeName) {
|
||||
const items = registry.get(nodeName);
|
||||
if (!items) throw new Error(`$('${nodeName}') chamado antes do node rodar (ordem errada no smoke test)`);
|
||||
return {
|
||||
first: () => items[0],
|
||||
all: () => items,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function runCodeNode(nodeName, inputItems, extraGlobals) {
|
||||
const node = nodeByName(nodeName);
|
||||
const code = node.parameters.jsCode;
|
||||
const sandbox = {
|
||||
$: makeDollar(),
|
||||
$json: inputItems[0] ? inputItems[0].json : {},
|
||||
$input: {
|
||||
first: () => inputItems[0],
|
||||
all: () => inputItems,
|
||||
},
|
||||
// $execution.mode: 'trigger' simula o Schedule Trigger (producao real);
|
||||
// o smoke test usa isso por padrao porque e o caminho mais exercitado
|
||||
// pelo relatorio (execucao manual e coberta por sendGuard.test.js).
|
||||
$execution: { mode: 'trigger' },
|
||||
console,
|
||||
Date,
|
||||
JSON,
|
||||
Math,
|
||||
Number,
|
||||
String,
|
||||
Boolean,
|
||||
Array,
|
||||
Object,
|
||||
Map,
|
||||
Set,
|
||||
RegExp,
|
||||
...extraGlobals,
|
||||
};
|
||||
vm.createContext(sandbox);
|
||||
const wrapped = `(function(){ ${code} \n})()`;
|
||||
const result = vm.runInContext(wrapped, sandbox, { filename: nodeName });
|
||||
registry.set(nodeName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fixtures (mesmos numeros de referencia da missao: 20/08/2026)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
const configItem = [
|
||||
{
|
||||
json: {
|
||||
data_alvo: '2026-08-20',
|
||||
slack_webhook_url: 'https://hooks.slack.com/services/TESTE',
|
||||
AI_ENABLED: true,
|
||||
AI_MODEL: 'gpt-5.5',
|
||||
AI_MAX_TICKETS: 40,
|
||||
AI_MAX_CONTEXT_CHARS: 12000,
|
||||
AI_MIN_CONFIDENCE: 0.6,
|
||||
AI_TIMEOUT_MS: 30000,
|
||||
HISTORICAL_BASELINE_DAYS: 20,
|
||||
HISTORICAL_BASELINE_WEEKS: 4,
|
||||
MAX_REPORT_INSIGHTS: 5,
|
||||
DEV_MODE: true,
|
||||
SHADOW_MODE: true,
|
||||
SEND_ENABLED: true,
|
||||
FORCE_SEND: false,
|
||||
SLACK_DESTINATION: 'C0000TESTE',
|
||||
EXTENDED_METRICS_ENABLED: false,
|
||||
ANOMALY_MULTIPLIER: 2.0,
|
||||
ANOMALY_MIN_DELTA: 3,
|
||||
CLIENT_CONCENTRATION_MIN_COUNT: 3,
|
||||
CLIENT_CONCENTRATION_MIN_SHARE: 0.4,
|
||||
PENDING_AGE_ATTENTION_DAYS: 15,
|
||||
TICKET_CONTEXT_SQL_LIMIT: 300,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
console.log('--- 1) Code - Periodo e Queries SQL ---');
|
||||
const out1 = runCodeNode('Code - Periodo e Queries SQL', configItem);
|
||||
console.log('tipo_periodo:', out1[0].json.period.tipo_periodo, '| periodo_label:', out1[0].json.period.periodo_label);
|
||||
console.log('query indicadores (trecho):', out1[0].json.queries.indicadores.slice(0, 80).replace(/\n/g, ' '), '...');
|
||||
|
||||
registry.set('MySQL - Indicadores do Periodo', [
|
||||
{
|
||||
json: {
|
||||
tipo_periodo: 'manual_diario',
|
||||
periodo_label: '2026-08-20',
|
||||
data_inicio: '2026-08-20',
|
||||
data_fim: '2026-08-20',
|
||||
abertos: 12,
|
||||
fechados: 11,
|
||||
saldo: -1,
|
||||
pendentes: 22,
|
||||
alta_prioridade_pendentes: 3,
|
||||
categorias_recorrentes: 'Software / SO: 4\nBug / Falha do Sistema: 2',
|
||||
clientes_volume: 'IMDI: 2 chamados\nLaboratorio da Cidade: 2 chamados',
|
||||
},
|
||||
},
|
||||
]);
|
||||
registry.set('MySQL - Ranking Categorias', [
|
||||
{ json: { id: 10, nome: 'Software / SO', total: 4 } },
|
||||
{ json: { id: 11, nome: 'Bug / Falha do Sistema', total: 2 } },
|
||||
]);
|
||||
registry.set('MySQL - Ranking Clientes', [
|
||||
{ json: { id: 20, nome: 'IMDI', total: 2 } },
|
||||
{ json: { id: 21, nome: 'Laboratorio da Cidade', total: 2 } },
|
||||
]);
|
||||
registry.set(
|
||||
'MySQL - Contexto de Chamados',
|
||||
Array.from({ length: 5 }, (_, i) => ({
|
||||
json: {
|
||||
id: i + 1,
|
||||
title: `Chamado de teste ${i + 1}`,
|
||||
category: 'Software / SO',
|
||||
client: 'IMDI',
|
||||
priority: 3,
|
||||
status: 5,
|
||||
opened_at: '2026-08-20',
|
||||
closed_at: null,
|
||||
solved_at: null,
|
||||
description: 'Descricao de teste para o smoke test.',
|
||||
solution: null,
|
||||
},
|
||||
}))
|
||||
);
|
||||
registry.set('MySQL - Baseline Diario Categoria', [
|
||||
{ dia: '2026-08-13', id: 10, total: 1 },
|
||||
{ dia: '2026-08-14', id: 10, total: 1 },
|
||||
].map((r) => ({ json: r })));
|
||||
registry.set('MySQL - Baseline Diario Cliente', [
|
||||
{ dia: '2026-08-13', id: 20, total: 1 },
|
||||
].map((r) => ({ json: r })));
|
||||
registry.set('Data Table - Buscar Memoria de Insights', []);
|
||||
registry.set('Data Table - Buscar Historico Anterior', []);
|
||||
registry.set('Data Table - Buscar Log de Envios', []);
|
||||
|
||||
console.log('\n--- 2) Code - Preparar Contexto IA ---');
|
||||
const out2 = runCodeNode('Code - Preparar Contexto IA', [{ json: {} }]);
|
||||
console.log('tickets no contexto:', out2[0].json.ticketContext.tickets.length);
|
||||
console.log('modelo solicitado:', out2[0].json.openai_request_body.model);
|
||||
|
||||
console.log('\n--- 3) Code - Normalizar Resposta IA (simulando resposta OpenAI valida) ---');
|
||||
const respostaIaSimulada = {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: JSON.stringify({
|
||||
period_assessment: { status: 'stable', summary: 'Operacao dentro do padrao.' },
|
||||
insights: [
|
||||
{
|
||||
type: 'CATEGORY_SPIKE',
|
||||
severity: 'medium',
|
||||
title: 'Software/SO acima do padrao',
|
||||
summary: 'Volume de Software/SO ficou acima da media recente.',
|
||||
evidence_ticket_ids: [1, 2],
|
||||
confidence: 0.85,
|
||||
reportable: true,
|
||||
subject_key: 'categoria:software_so',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const out3 = runCodeNode('Code - Normalizar Resposta IA', [{ json: respostaIaSimulada }]);
|
||||
console.log('aiCall:', out3[0].json.aiCall.attempted, out3[0].json.aiCall.error);
|
||||
|
||||
console.log('\n--- 4) Code - Motor V2: Relatorio Completo ---');
|
||||
const out4 = runCodeNode('Code - Motor V2: Relatorio Completo', [{ json: out3[0].json }]);
|
||||
const relatorio = out4[0].json;
|
||||
console.log('status_operacional:', relatorio.status_operacional);
|
||||
console.log('insights_aceitos:', relatorio.insights_aceitos.length);
|
||||
console.log('envio:', JSON.stringify(relatorio.envio, null, 2));
|
||||
console.log('observabilidade:', JSON.stringify(relatorio.observabilidade, null, 2));
|
||||
console.log('\n=== mensagem_v1 (fallback / legado) ===\n' + relatorio.mensagem_v1);
|
||||
console.log('\n=== mensagem_v2 (V2 curta) ===\n' + relatorio.mensagem_v2);
|
||||
|
||||
console.log('\n--- 5) Code - Preparar Historico ---');
|
||||
const out5 = runCodeNode('Code - Preparar Historico', [{ json: relatorio }]);
|
||||
console.log(JSON.stringify(out5[0].json, null, 2));
|
||||
|
||||
console.log('\n--- 6) Code - Preparar Log de Envio (Sucesso/Falha) ---');
|
||||
registry.set('Code - Motor V2: Relatorio Completo', [{ json: relatorio }]);
|
||||
const out6a = runCodeNode('Code - Preparar Log de Envio (Sucesso)', [{ json: {} }]);
|
||||
console.log('log sucesso:', JSON.stringify(out6a[0].json));
|
||||
const out6b = runCodeNode('Code - Preparar Log de Envio (Falha)', [{ json: { error: { message: 'timeout simulado' } } }]);
|
||||
console.log('log falha:', JSON.stringify(out6b[0].json));
|
||||
|
||||
console.log('\nSMOKE TEST OK — todos os Code nodes gerados executaram sem excecao.');
|
||||
Reference in New Issue
Block a user