import { describe, it, expect, jest } from '@jest/globals';
import { ROICalculator } from '../../../src/core/calculators/roi-calculator.js';
import type { UseCaseCreate } from '../../../src/schemas/use-case.js';
describe('ROI Calculator', () => {
let calculator: ROICalculator;
beforeEach(() => {
calculator = new ROICalculator();
});
describe('calculateProjectROI', () => {
const baseUseCases: UseCaseCreate[] = [
{
name: 'Invoice Processing',
category: 'document_processing',
current_state: {
volume_per_month: 1000,
cost_per_transaction: 10,
time_per_transaction: 15,
error_rate: 0.05,
resource_hours: 150,
},
future_state: {
automation_percentage: 0.8,
error_reduction: 0.9,
time_savings: 0.7,
quality_improvement: 0.95,
},
implementation: {
complexity_score: 5,
integration_points: 3,
training_hours: 20,
},
},
];
const baseImplementationCosts = {
software_licenses: 50000,
development_hours: 500,
training_costs: 10000,
infrastructure: 20000,
ongoing_monthly: 5000,
};
it('should calculate ROI for a simple project', () => {
const result = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
12,
'technology'
);
expect(result).toHaveProperty('summary');
expect(result).toHaveProperty('details');
expect(result).toHaveProperty('cashFlows');
expect(result.summary.totalInvestment).toBeGreaterThan(0);
expect(result.summary.monthlyBenefit).toBeGreaterThan(0);
expect(result.summary.paybackPeriodMonths).toBeGreaterThan(0);
expect(result.summary.fiveYearROI).toBeGreaterThan(0);
});
it('should calculate monthly benefit correctly', () => {
const result = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
12,
'technology'
);
// Expected: 1000 * 10 * 0.8 = 8000 base savings
// Plus error reduction and time savings benefits
expect(result.summary.monthlyBenefit).toBeGreaterThan(8000);
});
it('should handle multiple use cases', () => {
const multipleUseCases = [
...baseUseCases,
{
...baseUseCases[0],
name: 'Order Processing',
current_state: {
...baseUseCases[0].current_state,
volume_per_month: 500,
},
},
];
const result = calculator.calculateProjectROI(
multipleUseCases,
baseImplementationCosts,
12,
'technology'
);
expect(result.details.byUseCase).toHaveLength(2);
expect(result.summary.monthlyBenefit).toBeGreaterThan(10000);
});
it('should apply industry modifiers', () => {
const techResult = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
12,
'technology'
);
const healthcareResult = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
12,
'healthcare'
);
// Healthcare typically has higher implementation costs
expect(healthcareResult.summary.totalInvestment).toBeGreaterThan(
techResult.summary.totalInvestment
);
});
it('should calculate accurate payback period', () => {
const result = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
24,
'technology'
);
expect(result.summary.paybackPeriodMonths).toBeGreaterThan(0);
expect(result.summary.paybackPeriodMonths).toBeLessThan(24);
// Verify payback period matches cumulative cash flow
const cumulativeCashFlow = result.cashFlows.cumulative;
const paybackIndex = Math.ceil(result.summary.paybackPeriodMonths);
if (paybackIndex < cumulativeCashFlow.length) {
expect(cumulativeCashFlow[paybackIndex]).toBeGreaterThan(0);
}
});
it('should generate correct cash flow length', () => {
const timelineMonths = 36;
const result = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
timelineMonths,
'technology'
);
expect(result.cashFlows.monthly).toHaveLength(timelineMonths);
expect(result.cashFlows.cumulative).toHaveLength(timelineMonths);
});
});
describe('calculateUseCaseROI', () => {
it('should calculate individual use case benefits', () => {
const useCase = global.testUtils.createMockUseCase();
const benefits = (calculator as any).calculateUseCaseROI(
useCase,
2 // multiplier
);
expect(benefits).toHaveProperty('directCostSavings');
expect(benefits).toHaveProperty('errorReductionSavings');
expect(benefits).toHaveProperty('timeSavings');
expect(benefits).toHaveProperty('qualityImprovement');
expect(benefits).toHaveProperty('totalMonthlyBenefit');
expect(benefits.totalMonthlyBenefit).toBeGreaterThan(0);
});
it('should apply multiplier correctly', () => {
const useCase = global.testUtils.createMockUseCase();
const benefits1x = (calculator as any).calculateUseCaseROI(useCase, 1);
const benefits2x = (calculator as any).calculateUseCaseROI(useCase, 2);
expect(benefits2x.totalMonthlyBenefit).toBeCloseTo(
benefits1x.totalMonthlyBenefit * 2,
2
);
});
});
describe('edge cases', () => {
it('should handle zero volume use cases', () => {
const zeroVolumeUseCase: UseCaseCreate = {
name: 'Zero Volume',
category: 'automation',
current_state: {
volume_per_month: 0,
cost_per_transaction: 10,
time_per_transaction: 5,
error_rate: 0.05,
resource_hours: 0,
},
future_state: {
automation_percentage: 0.8,
error_reduction: 0.9,
time_savings: 0.7,
quality_improvement: 0.95,
},
};
const result = calculator.calculateProjectROI(
[zeroVolumeUseCase],
baseImplementationCosts,
12,
'technology'
);
expect(result.summary.monthlyBenefit).toBe(0);
expect(result.summary.fiveYearROI).toBeLessThan(0); // Negative due to costs
});
it('should handle very short timelines', () => {
const result = calculator.calculateProjectROI(
baseUseCases,
baseImplementationCosts,
6, // Only 6 months
'technology'
);
expect(result.cashFlows.monthly).toHaveLength(6);
expect(result.summary.paybackPeriodMonths).toBeGreaterThan(6);
});
it('should handle very high implementation costs', () => {
const highCosts = {
software_licenses: 1000000,
development_hours: 10000,
training_costs: 100000,
infrastructure: 500000,
ongoing_monthly: 50000,
};
const result = calculator.calculateProjectROI(
baseUseCases,
highCosts,
60,
'technology'
);
expect(result.summary.fiveYearROI).toBeLessThan(50); // Low ROI
expect(result.summary.paybackPeriodMonths).toBeGreaterThan(60);
});
});
});