125 lines
4.5 KiB
JavaScript
125 lines
4.5 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { getWorkdays, generatePlan } from '../js/algorithm.js';
|
|
|
|
const fixed = () => 0.5; // deterministic random for tests
|
|
|
|
function emp(id, constraints = {}) {
|
|
return {
|
|
id, name: id,
|
|
constraints: {
|
|
neverDays: [], lowPriorityDays: [], onlyDays: [],
|
|
maxPerMonth: null, minGapDays: 0, vacations: [],
|
|
...constraints
|
|
}
|
|
};
|
|
}
|
|
|
|
// getWorkdays
|
|
{
|
|
const days = getWorkdays(2026, 4, [], []);
|
|
assert.equal(days.length, 22, 'April 2026: 22 workdays');
|
|
assert.equal(days[0], '2026-04-01', 'First workday = 2026-04-01');
|
|
assert.equal(days.at(-1), '2026-04-30', 'Last workday = 2026-04-30');
|
|
}
|
|
{
|
|
const days = getWorkdays(2026, 4, ['2026-04-01'], []);
|
|
assert.equal(days.length, 21, 'Holiday reduces count by 1');
|
|
assert.ok(!days.includes('2026-04-01'), 'Holiday excluded');
|
|
}
|
|
{
|
|
const days = getWorkdays(2026, 4, [], []);
|
|
assert.ok(!days.includes('2026-04-04'), '2026-04-04 Saturday excluded');
|
|
assert.ok(!days.includes('2026-04-05'), '2026-04-05 Sunday excluded');
|
|
}
|
|
|
|
// generatePlan — basic assignment
|
|
{
|
|
const result = generatePlan(['2026-04-01'], [emp('alice'), emp('bob')], [], fixed);
|
|
assert.equal(result.length, 1, 'One workday → one assignment');
|
|
assert.ok(['alice','bob'].includes(result[0].employeeId), 'Assigned to alice or bob');
|
|
}
|
|
|
|
// neverDays blocks employee
|
|
{
|
|
// 2026-04-01 is Wednesday (3)
|
|
const result = generatePlan(['2026-04-01'], [emp('alice', {neverDays:[3]}), emp('bob')], [], fixed);
|
|
assert.equal(result[0].employeeId, 'bob', 'alice blocked on Wednesday → bob assigned');
|
|
}
|
|
|
|
// onlyDays restricts to specific days
|
|
{
|
|
// 2026-04-01 = Wednesday (3), 2026-04-02 = Thursday (4)
|
|
const employees = [emp('alice', {onlyDays:[3]}), emp('bob')];
|
|
const result = generatePlan(['2026-04-01','2026-04-02'], employees, [], fixed);
|
|
const aliceOnThursday = result.find(r => r.date === '2026-04-02' && r.employeeId === 'alice');
|
|
assert.ok(!aliceOnThursday, 'alice with onlyDays:[3] not assigned on Thursday');
|
|
}
|
|
|
|
// vacation blocks employee
|
|
{
|
|
const employees = [
|
|
emp('alice', {vacations:[{from:'2026-04-01', to:'2026-04-03'}]}),
|
|
emp('bob')
|
|
];
|
|
const result = generatePlan(['2026-04-01'], employees, [], fixed);
|
|
assert.equal(result[0].employeeId, 'bob', 'alice on vacation → bob assigned');
|
|
}
|
|
|
|
// maxPerMonth cap
|
|
{
|
|
const days = ['2026-04-01','2026-04-02','2026-04-03'];
|
|
const employees = [emp('alice', {maxPerMonth:1}), emp('bob'), emp('clara')];
|
|
const result = generatePlan(days, employees, [], fixed);
|
|
const aliceCount = result.filter(r => r.employeeId === 'alice').length;
|
|
assert.ok(aliceCount <= 1, 'alice maxPerMonth=1 → assigned at most once');
|
|
}
|
|
|
|
// minGapDays
|
|
{
|
|
// 2026-04-06 = Monday, alice last assigned 2026-04-02 (Thu) = 4 days ago, gap=5 → blocked
|
|
const history = [{date:'2026-04-02', employeeId:'alice', manual:false}];
|
|
const employees = [emp('alice', {minGapDays:5}), emp('bob')];
|
|
const result = generatePlan(['2026-04-06'], employees, history, fixed);
|
|
assert.equal(result[0].employeeId, 'bob', 'alice within minGap (4<5) → bob assigned');
|
|
}
|
|
|
|
// No candidates → day skipped
|
|
{
|
|
// 2026-04-01 = Wednesday (3), only employee blocked
|
|
const result = generatePlan(['2026-04-01'], [emp('alice', {neverDays:[3]})], [], fixed);
|
|
assert.equal(result.length, 0, 'No candidate → day skipped');
|
|
}
|
|
|
|
// Fairness: less history = higher priority
|
|
{
|
|
const history = [1,2,3,4,5].map(i => ({
|
|
date: '2026-04-0' + i, employeeId: 'alice', manual: false
|
|
}));
|
|
const result = generatePlan(['2026-05-04'], [emp('alice'), emp('bob')], history, fixed);
|
|
assert.equal(result[0].employeeId, 'bob', 'bob (0 history) beats alice (5 history)');
|
|
}
|
|
|
|
// companyClosures excluded from workdays
|
|
{
|
|
const days = getWorkdays(2026, 4, [], ['2026-04-01']);
|
|
assert.equal(days.length, 21, 'Closure reduces count by 1');
|
|
assert.ok(!days.includes('2026-04-01'), 'Closure date excluded');
|
|
}
|
|
|
|
// lowPriorityDays reduces score — bob without penalty wins over alice with penalty
|
|
{
|
|
// 2026-04-01 = Wednesday (3); alice has lowPriorityDays on Wednesday
|
|
// Both have same history (0) — alice's score gets -5 penalty, bob wins
|
|
const employees = [emp('alice', {lowPriorityDays:[3]}), emp('bob')];
|
|
const result = generatePlan(['2026-04-01'], employees, [], fixed);
|
|
assert.equal(result[0].employeeId, 'bob', 'alice low priority on Wednesday → bob wins');
|
|
}
|
|
|
|
// manual: false on auto-assigned entries
|
|
{
|
|
const result = generatePlan(['2026-04-01'], [emp('alice')], [], fixed);
|
|
assert.equal(result[0].manual, false, 'auto-assignment sets manual: false');
|
|
}
|
|
|
|
console.log('All algorithm tests passed.');
|