import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { calculateSessionDurationHours, resolveSessionDurationHours, calculatePercentageShare, calculateHourlyShare, calculateExtraExpenses, calculateProfessorPayout, calculateNetProfit, calculatePendingReceivables, calculateOverpaidAmount } from './professorShare.js'; describe('calculateSessionDurationHours', () => { it('computes duration between two clock times', () => { assert.equal(calculateSessionDurationHours('18:00', '20:00'), 2); }); it('returns 0 for invalid input', () => { assert.equal(calculateSessionDurationHours('', ''), 0); assert.equal(calculateSessionDurationHours(null, undefined), 0); }); }); describe('resolveSessionDurationHours', () => { it('prefers hoursPerSection over the class time window', () => { assert.equal(resolveSessionDurationHours({ hoursPerSection: 2, startTime: '18:00', endTime: '20:30' }), 2); }); it('falls back to the class time window', () => { assert.equal(resolveSessionDurationHours({ startTime: '18:00', endTime: '19:30' }), 1.5); }); }); describe('calculateProfessorPayout', () => { it('computes percentage-based payout with extra expenses', () => { const result = calculateProfessorPayout({ payoutType: 'percentage', payoutPercentage: 40, revenue: 10_000_000, extraExpensePerSession: 100_000, sessionsCount: 5 }); assert.equal(result.baseShare, 4_000_000); assert.equal(result.extraExpenses, 500_000); assert.equal(result.totalPayout, 4_500_000); }); it('computes percentage-based payout deducting serviceFeePerPerson * studentsCount first', () => { const result = calculateProfessorPayout({ payoutType: 'percentage', payoutPercentage: 50, revenue: 20_000_000, serviceFeePerPerson: 400_000, studentsCount: 2 }); // 20M - (400k * 2) = 19.2M * 50% = 9.6M assert.equal(result.baseShare, 9_600_000); assert.equal(result.totalPayout, 9_600_000); }); it('computes hourly-based payout with extra expenses', () => { const result = calculateProfessorPayout({ payoutType: 'hourly', payoutHourlyRate: 300_000, sessionDurationHours: 1.5, extraExpensePerSession: 80_000, sessionsCount: 10 }); assert.equal(result.baseShare, 4_500_000); assert.equal(result.extraExpenses, 800_000); assert.equal(result.totalPayout, 5_300_000); }); }); describe('calculateNetProfit', () => { it('can go negative to represent a loss', () => { assert.equal(calculateNetProfit({ totalIncome: 1_000_000, professorTotalPayout: 4_500_000 }), -3_500_000); }); }); describe('calculatePendingReceivables / calculateOverpaidAmount', () => { it('never returns negative outstanding, and surfaces overpayments separately', () => { assert.equal(calculatePendingReceivables(10_000_000, 12_000_000), 0); assert.equal(calculateOverpaidAmount(10_000_000, 12_000_000), 2_000_000); }); it('handles zero revenue classes gracefully', () => { assert.equal(calculatePendingReceivables(0, 0), 0); assert.equal(calculateExtraExpenses({}), 0); }); });