Files
inq-roi-simulador-web/tests/features/report/sensitivity.test.ts
2026-06-24 16:42:32 -03:00

109 lines
4.2 KiB
TypeScript

/**
* Tests del recálculo de ROI ajustado por sliders de sensibilidad (Sprint 6, Etapa 2).
*
* La lógica vive inline en el useMemo `adjustedRoi` de ReportPage.tsx — no hay una función
* pura exportada (no se modifica domain/roi.ts por scope de la etapa). Estos tests replican
* exactamente esa transformación de inputs y verifican el comportamiento de calculateRoi()
* con los valores ajustados, igual que lo hace el componente.
*/
import { describe, it, expect } from 'vitest'
import { calculateRoi, type RoiInput } from '@/domain/roi'
const BASE: Omit<RoiInput, 'annualFrequency' | 'automationInvestment'> = {
costPerExecutionActual: 600,
costPerExecutionAutomated: 60,
analysisHorizonYears: 3,
}
const BASE_FREQUENCY = 1200
const BASE_INVESTMENT = 50_000
function adjustedRoi(volumeAdjust: number, investmentAdjust: number) {
return calculateRoi({
...BASE,
annualFrequency: BASE_FREQUENCY * (1 + volumeAdjust / 100),
automationInvestment: BASE_INVESTMENT * (1 + investmentAdjust / 100),
})
}
describe('Sensibilidad — ajuste de volumen anual', () => {
it('volumen +20%: annualFrequency y annualSavings escalan 1.2x respecto al base', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(20, 0)
expect(adjusted.annualSavings).toBeCloseTo(base.annualSavings * 1.2)
})
it('volumen -30%: annualSavings escala 0.7x, payback se alarga', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(-30, 0)
expect(adjusted.annualSavings).toBeCloseTo(base.annualSavings * 0.7)
expect(adjusted.paybackMonths).toBeGreaterThan(base.paybackMonths)
})
})
describe('Sensibilidad — ajuste de inversión en automatización', () => {
it('inversión +50%: payback se alarga 1.5x, ROI% baja proporcionalmente', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(0, 50)
expect(adjusted.paybackMonths).toBeCloseTo(base.paybackMonths * 1.5)
expect(adjusted.roiAnnualPercent).toBeCloseTo(base.roiAnnualPercent / 1.5)
})
it('inversión -50%: payback se acorta a la mitad, ROI% se duplica', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(0, -50)
expect(adjusted.paybackMonths).toBeCloseTo(base.paybackMonths * 0.5)
expect(adjusted.roiAnnualPercent).toBeCloseTo(base.roiAnnualPercent * 2)
})
})
describe('Sensibilidad — ajuste combinado', () => {
it('volumen +20% e inversión +50% simultáneos: ambos efectos se componen', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(20, 50)
// annualSavings depende solo de volumen
expect(adjusted.annualSavings).toBeCloseTo(base.annualSavings * 1.2)
// payback = inversión / annualSavings → (1.5 * inv) / (1.2 * savings) = base * 1.25
expect(adjusted.paybackMonths).toBeCloseTo(base.paybackMonths * (1.5 / 1.2))
})
it('volumen -30% e inversión -50% simultáneos', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(-30, -50)
expect(adjusted.annualSavings).toBeCloseTo(base.annualSavings * 0.7)
expect(adjusted.paybackMonths).toBeCloseTo(base.paybackMonths * (0.5 / 0.7))
})
})
describe('Sensibilidad — reset a 0', () => {
it('volumeAdjust=0 e investmentAdjust=0 reproduce exactamente el ROI base', () => {
const base = calculateRoi({
...BASE,
annualFrequency: BASE_FREQUENCY,
automationInvestment: BASE_INVESTMENT,
})
const reset = adjustedRoi(0, 0)
expect(reset).toEqual(base)
})
it('después de mover sliders, volver a 0/0 coincide con el valor base original', () => {
adjustedRoi(40, -20) // simula haber movido los sliders
const resetAgain = adjustedRoi(0, 0)
const base = adjustedRoi(0, 0)
expect(resetAgain).toEqual(base)
})
})
describe('Sensibilidad — límites del rango (-50 a +100)', () => {
it('volumen en el límite inferior (-50%) no produce frecuencia negativa', () => {
const adjusted = adjustedRoi(-50, 0)
expect(adjusted.annualSavings).toBeGreaterThanOrEqual(0)
})
it('volumen en el límite superior (+100%) duplica el ahorro anual', () => {
const base = adjustedRoi(0, 0)
const adjusted = adjustedRoi(100, 0)
expect(adjusted.annualSavings).toBeCloseTo(base.annualSavings * 2)
})
})