94 lines
3.1 KiB
JavaScript
94 lines
3.1 KiB
JavaScript
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);
|
|
});
|
|
});
|