Files
inq-roi-simulador-web/tests/features/report/efficiency.test.ts
Marcos Benítez bf200b72f9
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Los cambios en reporte de eficiencia
2026-06-23 17:09:54 -03:00

127 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Tests de las fórmulas "Ahorro esperado" y "Eficiencia" de ComparisonTable (Sprint 5, Etapa 5).
* expectedSavings = savings × executionProbability
* efficiency = expectedSavings / executionTimeMinutes
* Reglas de null: no automatable → null; savings <= 0 → null; efficiency además requiere
* executionTimeMinutes > 0.
*
* Las fórmulas viven inline en el useMemo de ComparisonTable.tsx (no hay función pura
* exportada — por scope de la etapa no se modifica el dominio). Estos tests replican
* exactamente esa lógica para verificar las fórmulas y las reglas de null/ranking.
*/
import { describe, it, expect } from 'vitest'
interface Activity {
savings: number
automatable: boolean
executionProbability: number
executionTimeMinutes: number
}
function computeExpectedSavings(act: Activity): number | null {
return act.automatable && act.savings > 0 ? act.savings * act.executionProbability : null
}
function computeEfficiency(act: Activity): number | null {
return act.automatable && act.savings > 0 && act.executionTimeMinutes > 0
? (act.savings * act.executionProbability) / act.executionTimeMinutes
: null
}
describe('ComparisonTable — fórmula Ahorro esperado', () => {
it('calcula savings × executionProbability', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 0.8, executionTimeMinutes: 60 }
expect(computeExpectedSavings(act)).toBeCloseTo(432)
})
it('probability 1.0 → expectedSavings === savings', () => {
const act: Activity = { savings: 270, automatable: true, executionProbability: 1.0, executionTimeMinutes: 30 }
expect(computeExpectedSavings(act)).toBe(270)
})
it('no automatable → null', () => {
const act: Activity = { savings: 540, automatable: false, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeExpectedSavings(act)).toBeNull()
})
it('savings <= 0 → null', () => {
const act: Activity = { savings: 0, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeExpectedSavings(act)).toBeNull()
const negative: Activity = { savings: -100, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeExpectedSavings(negative)).toBeNull()
})
it('executionProbability 0 → resultado 0, no null (0 sigue siendo un valor válido salvo que savings <= 0)', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 0, executionTimeMinutes: 60 }
expect(computeExpectedSavings(act)).toBe(0)
})
})
describe('ComparisonTable — fórmula Eficiencia', () => {
it('calcula (savings × executionProbability) / executionTimeMinutes', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeEfficiency(act)).toBeCloseTo(9.0)
})
it('considera executionProbability fraccionaria', () => {
const act: Activity = { savings: 300, automatable: true, executionProbability: 0.5, executionTimeMinutes: 30 }
expect(computeEfficiency(act)).toBeCloseTo(5.0)
})
it('no automatable → null', () => {
const act: Activity = { savings: 540, automatable: false, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeEfficiency(act)).toBeNull()
})
it('savings <= 0 → null', () => {
const act: Activity = { savings: -50, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }
expect(computeEfficiency(act)).toBeNull()
})
it('executionTimeMinutes === 0 → null aunque savings sea válido', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 1.0, executionTimeMinutes: 0 }
expect(computeEfficiency(act)).toBeNull()
})
it('executionTimeMinutes === 0 pero expectedSavings sigue siendo válido (no null)', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 1.0, executionTimeMinutes: 0 }
expect(computeExpectedSavings(act)).toBe(540)
expect(computeEfficiency(act)).toBeNull()
})
it('executionProbability 0 → resultado 0, no null', () => {
const act: Activity = { savings: 540, automatable: true, executionProbability: 0, executionTimeMinutes: 60 }
expect(computeEfficiency(act)).toBe(0)
})
})
describe('ComparisonTable — ranking con valores null', () => {
function rankByEfficiency(acts: Activity[]): (number | null)[] {
return acts
.map(computeEfficiency)
.sort((a, b) => (b ?? -Infinity) - (a ?? -Infinity))
}
it('filas con efficiency null quedan siempre al final del sort descendente', () => {
const acts: Activity[] = [
{ savings: -10, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }, // null
{ savings: 600, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }, // 10
{ savings: 60, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }, // 1
]
const ranked = rankByEfficiency(acts)
expect(ranked[0]).toBeCloseTo(10)
expect(ranked[1]).toBeCloseTo(1)
expect(ranked[ranked.length - 1]).toBeNull()
})
it('fila nula siempre por debajo de una fila con efficiency 0.01', () => {
const acts: Activity[] = [
{ savings: -5, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }, // null
{ savings: 0.6, automatable: true, executionProbability: 1.0, executionTimeMinutes: 60 }, // 0.01
]
const ranked = rankByEfficiency(acts)
expect(ranked[0]).toBeCloseTo(0.01)
expect(ranked[1]).toBeNull()
})
})