83 lines
3.6 KiB
JavaScript
83 lines
3.6 KiB
JavaScript
import { SkillRegistry, DEFAULT_SKILLS } from '@/lib/assistant/skills/SkillRegistry';
|
|
|
|
/**
|
|
* The SkillRegistry joins the rules (code) with the settings (the backend
|
|
* agent registry, /admin/ai/skills). Rows below are shaped exactly like that
|
|
* endpoint's response.
|
|
*/
|
|
|
|
const row = (skillid, enabled, thresholds = {}) => ({ skillid, enabled, thresholds });
|
|
|
|
describe('SkillRegistry', () => {
|
|
beforeEach(() => {
|
|
SkillRegistry.clearRegistryConfig();
|
|
});
|
|
|
|
it('registers the eight built-in logistics skills', () => {
|
|
const ids = SkillRegistry.getAllSkills().map((s) => s.id);
|
|
expect(ids).toHaveLength(8);
|
|
expect(ids).toEqual(DEFAULT_SKILLS.map((s) => s.id));
|
|
expect(ids).toContain('skill_sla_guardian');
|
|
expect(ids).toContain('skill_rider_battery_safety');
|
|
});
|
|
|
|
it('runs on code defaults until the registry answers, and says so', () => {
|
|
expect(SkillRegistry.getSource()).toBe('defaults');
|
|
const sla = SkillRegistry.getSkill('skill_sla_guardian');
|
|
expect(sla.enabled).toBe(true);
|
|
expect(sla.effectiveThresholds.slaRiskWindowMin.currentValue).toBe(45);
|
|
});
|
|
|
|
it('takes enabled and thresholds from the registry', () => {
|
|
SkillRegistry.applyRegistryConfig([
|
|
row('skill_fleet_balancer', false),
|
|
row('skill_sla_guardian', true, { slaRiskWindowMin: 30 })
|
|
]);
|
|
expect(SkillRegistry.getSource()).toBe('registry');
|
|
expect(SkillRegistry.getSkill('skill_fleet_balancer').enabled).toBe(false);
|
|
expect(SkillRegistry.getSkill('skill_sla_guardian').effectiveThresholds.slaRiskWindowMin.currentValue).toBe(30);
|
|
// A threshold the registry did not send keeps its default.
|
|
expect(SkillRegistry.getSkill('skill_sla_guardian').effectiveThresholds.unassignedAgingMin.currentValue).toBe(60);
|
|
});
|
|
|
|
it('can switch on a skill that is off by default', () => {
|
|
SkillRegistry.applyRegistryConfig([row('skill_cash_exposure', true)]);
|
|
expect(SkillRegistry.getSkill('skill_cash_exposure').enabled).toBe(true);
|
|
});
|
|
|
|
it('ignores an out-of-range stored value rather than running on it', () => {
|
|
SkillRegistry.applyRegistryConfig([row('skill_doorstep_stall', true, { arrivedStalledMin: 999 })]);
|
|
expect(SkillRegistry.getSkill('skill_doorstep_stall').effectiveThresholds.arrivedStalledMin.currentValue).toBe(20);
|
|
});
|
|
|
|
it('ignores registry rows for skills this console does not implement', () => {
|
|
SkillRegistry.applyRegistryConfig([row('stall_response', false), row('custom_night_shift_watch', true)]);
|
|
expect(SkillRegistry.getAllSkills()).toHaveLength(8);
|
|
expect(SkillRegistry.getSkill('stall_response')).toBeUndefined();
|
|
});
|
|
|
|
it('goes back to defaults when the registry is lost', () => {
|
|
SkillRegistry.applyRegistryConfig([row('skill_sla_guardian', false, { slaRiskWindowMin: 15 })]);
|
|
SkillRegistry.clearRegistryConfig();
|
|
const sla = SkillRegistry.getSkill('skill_sla_guardian');
|
|
expect(SkillRegistry.getSource()).toBe('defaults');
|
|
expect(sla.enabled).toBe(true);
|
|
expect(sla.effectiveThresholds.slaRiskWindowMin.currentValue).toBe(45);
|
|
});
|
|
|
|
it('keeps nothing in browser storage', () => {
|
|
localStorage.clear();
|
|
SkillRegistry.applyRegistryConfig([row('skill_sla_guardian', false)]);
|
|
expect(localStorage.length).toBe(0);
|
|
});
|
|
|
|
it('notifies subscribers when the registry settings change', () => {
|
|
const listener = jest.fn();
|
|
const unsubscribe = SkillRegistry.subscribe(listener);
|
|
SkillRegistry.applyRegistryConfig([row('skill_doorstep_stall', false)]);
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
expect(listener.mock.calls[0][0].find((s) => s.id === 'skill_doorstep_stall').enabled).toBe(false);
|
|
unsubscribe();
|
|
});
|
|
});
|