feat(v2): normalize GLPI text before context/hash, add real ticket description
context.normalizeTicket now runs normalizeGlpiText on titulo, descricao (new field, sourced from glpi_tickets.content -- never fetched before), and every followup/task texto. buildAiContextText restructured into delimited sections (TITULO / DESCRICAO ORIGINAL / STATUS-TECNICO-GRUPO / FOLLOW-UPS / TAREFAS) instead of one flat string. hash.buildContextFingerprint now includes titulo/descricao and the normalized followup/task texto (previously only id/date/status), so: (a) the same content encoded differently (HTML vs escaped entities) produces the same hash, and (b) editing a followup's actual content (same id/date) is now detected as a real context change -- previously silently invisible to the hash. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d7650308a9
commit
db7a261a10
+22
-3
@@ -1,6 +1,7 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { buildContextFingerprint, stableStringify } = require('./hash');
|
||||
const { normalizeGlpiText } = require('./normalize');
|
||||
|
||||
function baseTicket(overrides) {
|
||||
return Object.assign({
|
||||
@@ -8,8 +9,10 @@ function baseTicket(overrides) {
|
||||
tecnico_atribuido: 'Wagner',
|
||||
categoria: 'Rede',
|
||||
entidade: 'Matriz',
|
||||
followups: [{ id: 1, date: '2026-08-20 10:00:00' }],
|
||||
tasks: [{ id: 1, status: 1, date: '2026-08-19 09:00:00' }],
|
||||
titulo: 'Nobreak desligando',
|
||||
descricao: 'Nobreak do servidor desligando sozinho',
|
||||
followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: 'Aguardando peca' }],
|
||||
tasks: [{ id: 1, status: 1, date: '2026-08-19 09:00:00', texto: 'Verificado no local' }],
|
||||
dias_aberto: 10,
|
||||
dias_sem_interacao: 3,
|
||||
}, overrides);
|
||||
@@ -23,7 +26,7 @@ test('alterar apenas dias_aberto/dias_sem_interacao NAO muda o hash', () => {
|
||||
|
||||
test('novo followup MUDA o hash', () => {
|
||||
const a = buildContextFingerprint(baseTicket({}));
|
||||
const b = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00' }, { id: 2, date: '2026-08-21 08:00:00' }] }));
|
||||
const b = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: 'Aguardando peca' }, { id: 2, date: '2026-08-21 08:00:00', texto: 'Peca chegou' }] }));
|
||||
assert.notEqual(a, b);
|
||||
});
|
||||
|
||||
@@ -33,6 +36,22 @@ test('mudanca de tecnico MUDA o hash', () => {
|
||||
assert.notEqual(a, b);
|
||||
});
|
||||
|
||||
test('mesmo conteudo normalizado de fontes com encoding HTML diferente gera o MESMO hash', () => {
|
||||
const textoHtml = normalizeGlpiText('<p>Aguardando cliente</p>');
|
||||
const textoEscapado = normalizeGlpiText('<p>Aguardando cliente</p>');
|
||||
assert.equal(textoHtml, textoEscapado, 'pre-condicao: normalizacao deve produzir o mesmo texto');
|
||||
|
||||
const a = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: textoHtml }] }));
|
||||
const b = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: textoEscapado }] }));
|
||||
assert.equal(a, b);
|
||||
});
|
||||
|
||||
test('edicao real do conteudo de um followup (mesmo id/date) MUDA o hash', () => {
|
||||
const a = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: 'Aguardando peca' }] }));
|
||||
const b = buildContextFingerprint(baseTicket({ followups: [{ id: 1, date: '2026-08-20 10:00:00', texto: 'Peca instalada, testando' }] }));
|
||||
assert.notEqual(a, b);
|
||||
});
|
||||
|
||||
test('stableStringify ignora ordem de chaves', () => {
|
||||
assert.equal(stableStringify({ b: 1, a: 2 }), stableStringify({ a: 2, b: 1 }));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user