Files
support_backend/tests/unit/orchestration/strategies.test.ts
T

117 lines
3.7 KiB
TypeScript
Raw Normal View History

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,
userId: null,
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']);
});
});