const { test } = require('node:test'); const assert = require('node:assert/strict'); const { computeCooldown, applyFallbackPolicy, isCriticalOverride } = require('./cooldown'); const CONFIG = { cooldownDays: 1 }; const NOW = new Date('2026-08-24T08:00:00Z').getTime(); const ONE_DAY = 24 * 60 * 60 * 1000; function ticket(overrides) { return Object.assign({ sla_vencido: false, prioridade: 2, urgencia: 2 }, overrides); } test('WAITING_VENDOR sem mudanca de contexto fica bloqueado (nao repete diariamente)', () => { const memory = { consecutive_days_sent: 2, last_sent_at: NOW - ONE_DAY, next_review_at: NOW + ONE_DAY }; const result = computeCooldown({ ticket: ticket({}), classification: 'WAITING_VENDOR', contextChanged: false, memory, now: NOW, config: CONFIG }); assert.equal(result.blocked, true); }); test('enviado ontem sem mudanca recebe penalidade crescente', () => { const memory = { consecutive_days_sent: 3, last_sent_at: NOW - ONE_DAY, next_review_at: null }; const result = computeCooldown({ ticket: ticket({}), classification: 'ACTIONABLE_NOW', contextChanged: false, memory, now: NOW, config: CONFIG }); assert.ok(result.penalty > 0); assert.equal(result.blocked, false); }); test('mudanca de contexto real desbloqueia mesmo em estado de espera', () => { const memory = { consecutive_days_sent: 2, last_sent_at: NOW - ONE_DAY, next_review_at: NOW + ONE_DAY }; const result = computeCooldown({ ticket: ticket({}), classification: 'WAITING_VENDOR', contextChanged: true, memory, now: NOW, config: CONFIG }); assert.equal(result.blocked, false); }); test('NEEDS_COORDINATION sempre bloqueado para o Top 10 diario', () => { const result = computeCooldown({ ticket: ticket({}), classification: 'NEEDS_COORDINATION', contextChanged: false, memory: null, now: NOW, config: CONFIG }); assert.equal(result.blocked, true); assert.equal(result.reason, 'needs_coordination'); }); test('chamado critico (SLA vencido + prioridade alta) quebra o cooldown', () => { const memory = { consecutive_days_sent: 4, last_sent_at: NOW - ONE_DAY, next_review_at: NOW + ONE_DAY }; const critico = ticket({ sla_vencido: true, prioridade: 5 }); const result = computeCooldown({ ticket: critico, classification: 'WAITING_VENDOR', contextChanged: false, memory, now: NOW, config: CONFIG }); assert.equal(result.blocked, false); assert.equal(result.breakCooldown, true); }); test('sem memoria previa nunca bloqueia por cooldown', () => { const result = computeCooldown({ ticket: ticket({}), classification: 'ACTIONABLE_NOW', contextChanged: true, memory: null, now: NOW, config: CONFIG }); assert.equal(result.blocked, false); assert.equal(result.penalty, 0); }); test('sem tecnico + prioridade alta tambem conta como critico (mesmo sem SLA vencido)', () => { assert.equal(isCriticalOverride(ticket({ sem_tecnico: true, prioridade: 5, sla_vencido: false })), true); }); test('chamado comum (nao critico) NAO conta como override', () => { assert.equal(isCriticalOverride(ticket({ sla_vencido: true, prioridade: 2 })), false); assert.equal(isCriticalOverride(ticket({ sem_tecnico: true, prioridade: 2 })), false); }); test('AI_FALLBACK comum (nao critico) fica bloqueado por politica -- nao compete como acionavel confirmado', () => { const result = applyFallbackPolicy({ aiClassification: { is_fallback: true, classification: 'AI_FALLBACK' }, ticket: ticket({}) }); assert.equal(result.blocked, true); assert.equal(result.decisionReason, 'ai_fallback_no_override'); }); test('AI_FALLBACK critico (SLA vencido + prioridade alta) entra por excecao explicita', () => { const critico = ticket({ sla_vencido: true, prioridade: 5 }); const result = applyFallbackPolicy({ aiClassification: { is_fallback: true, classification: 'AI_FALLBACK' }, ticket: critico }); assert.equal(result.blocked, false); assert.equal(result.decisionReason, 'ai_fallback_critical_override'); }); test('INSUFFICIENT_CONTEXT comum fica fora da competicao operacional normal', () => { const result = applyFallbackPolicy({ aiClassification: { is_fallback: false, classification: 'INSUFFICIENT_CONTEXT' }, ticket: ticket({}) }); assert.equal(result.blocked, true); assert.equal(result.decisionReason, 'insufficient_context_no_override'); }); test('INSUFFICIENT_CONTEXT critico entra somente pela excecao definida', () => { const critico = ticket({ sem_tecnico: true, prioridade: 4 }); const result = applyFallbackPolicy({ aiClassification: { is_fallback: false, classification: 'INSUFFICIENT_CONTEXT' }, ticket: critico }); assert.equal(result.blocked, false); assert.equal(result.decisionReason, 'insufficient_context_critical_override'); }); test('classificacao real da IA (nao fallback, nao INSUFFICIENT_CONTEXT) nunca e bloqueada por esta politica', () => { const result = applyFallbackPolicy({ aiClassification: { is_fallback: false, classification: 'ACTIONABLE_NOW' }, ticket: ticket({}) }); assert.equal(result.blocked, false); assert.equal(result.decisionReason, null); });