2026-09-03 11:58:17 +05:30
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
|
import { roundRobinSelectIndex } from '@/modules/orchestration/assignments/strategies/round-robin.strategy';
|
|
|
|
|
import {
|
|
|
|
|
lowestLoadCandidates,
|
|
|
|
|
AgentWithLoad,
|
|
|
|
|
} from '@/modules/orchestration/assignments/calculators/workload.calculator';
|
|
|
|
|
import {
|
|
|
|
|
highestSkillScoreCandidates,
|
|
|
|
|
withSkillScores,
|
|
|
|
|
AgentWithSkillScore,
|
|
|
|
|
} from '@/modules/orchestration/assignments/calculators/skill-match.calculator';
|
|
|
|
|
|
|
|
|
|
function fakeAgent(id: string) {
|
|
|
|
|
return {
|
|
|
|
|
id,
|
|
|
|
|
teamId: 't',
|
|
|
|
|
name: id,
|
|
|
|
|
active: true,
|
2026-09-07 12:45:37 +05:30
|
|
|
userId: null,
|
2026-09-03 11:58:17 +05:30
|
|
|
createdAt: new Date(),
|
|
|
|
|
updatedAt: new Date(),
|
|
|
|
|
skills: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('roundRobinSelectIndex', () => {
|
|
|
|
|
it('cycles through indices 0..length-1 as the count increases', () => {
|
|
|
|
|
expect(roundRobinSelectIndex(1, 3)).toBe(0);
|
|
|
|
|
expect(roundRobinSelectIndex(2, 3)).toBe(1);
|
|
|
|
|
expect(roundRobinSelectIndex(3, 3)).toBe(2);
|
|
|
|
|
expect(roundRobinSelectIndex(4, 3)).toBe(0); // wraps back to the first agent
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('never produces the same index for two different, sequential counter values within one cycle', () => {
|
|
|
|
|
const seen = new Set<number>();
|
|
|
|
|
for (let count = 1; count <= 5; count++) {
|
|
|
|
|
seen.add(roundRobinSelectIndex(count, 5));
|
|
|
|
|
}
|
|
|
|
|
expect(seen.size).toBe(5);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('lowestLoadCandidates', () => {
|
|
|
|
|
it('picks the single lowest-load agent when there is no tie', () => {
|
|
|
|
|
const withLoads: AgentWithLoad[] = [
|
|
|
|
|
{ agent: fakeAgent('a'), currentLoad: 5 },
|
|
|
|
|
{ agent: fakeAgent('b'), currentLoad: 2 },
|
|
|
|
|
{ agent: fakeAgent('c'), currentLoad: 8 },
|
|
|
|
|
];
|
|
|
|
|
expect(lowestLoadCandidates(withLoads).map((a) => a.id)).toEqual(['b']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns every agent tied for the lowest load', () => {
|
|
|
|
|
const withLoads: AgentWithLoad[] = [
|
|
|
|
|
{ agent: fakeAgent('a'), currentLoad: 2 },
|
|
|
|
|
{ agent: fakeAgent('b'), currentLoad: 2 },
|
|
|
|
|
{ agent: fakeAgent('c'), currentLoad: 8 },
|
|
|
|
|
];
|
|
|
|
|
expect(
|
|
|
|
|
lowestLoadCandidates(withLoads)
|
|
|
|
|
.map((a) => a.id)
|
|
|
|
|
.sort(),
|
|
|
|
|
).toEqual(['a', 'b']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns an empty array for an empty input', () => {
|
|
|
|
|
expect(lowestLoadCandidates([])).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('withSkillScores', () => {
|
|
|
|
|
it('sums proficiency level only across the required skills, ignoring extras', () => {
|
|
|
|
|
const eligible = [
|
|
|
|
|
{
|
|
|
|
|
...fakeAgent('a'),
|
|
|
|
|
skills: [
|
|
|
|
|
{ id: '1', agentId: 'a', skillTag: 'x', level: 3 },
|
|
|
|
|
{ id: '2', agentId: 'a', skillTag: 'y', level: 10 }, // not required — ignored
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
...fakeAgent('b'),
|
|
|
|
|
skills: [
|
|
|
|
|
{ id: '3', agentId: 'b', skillTag: 'x', level: 1 },
|
|
|
|
|
{ id: '4', agentId: 'b', skillTag: 'z', level: 1 },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
const scored = withSkillScores(eligible, ['x', 'z']);
|
|
|
|
|
expect(scored.find((s) => s.agent.id === 'a')?.totalLevel).toBe(3); // only x counts
|
|
|
|
|
expect(scored.find((s) => s.agent.id === 'b')?.totalLevel).toBe(2); // x + z
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('highestSkillScoreCandidates', () => {
|
|
|
|
|
it('picks the single highest-scoring agent when there is no tie', () => {
|
|
|
|
|
const scored: AgentWithSkillScore[] = [
|
|
|
|
|
{ agent: fakeAgent('a'), totalLevel: 3 },
|
|
|
|
|
{ agent: fakeAgent('b'), totalLevel: 7 },
|
|
|
|
|
];
|
|
|
|
|
expect(highestSkillScoreCandidates(scored).map((a) => a.id)).toEqual(['b']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns every agent tied for the highest score', () => {
|
|
|
|
|
const scored: AgentWithSkillScore[] = [
|
|
|
|
|
{ agent: fakeAgent('a'), totalLevel: 5 },
|
|
|
|
|
{ agent: fakeAgent('b'), totalLevel: 5 },
|
|
|
|
|
{ agent: fakeAgent('c'), totalLevel: 1 },
|
|
|
|
|
];
|
|
|
|
|
expect(
|
|
|
|
|
highestSkillScoreCandidates(scored)
|
|
|
|
|
.map((a) => a.id)
|
|
|
|
|
.sort(),
|
|
|
|
|
).toEqual(['a', 'b']);
|
|
|
|
|
});
|
|
|
|
|
});
|