Files
doormilxpress_astryx/tests/lib/agentNetwork.test.js

154 lines
5.3 KiB
JavaScript

import {
AGENTS,
LOG_ERROR_LINES,
LOG_TOTAL_LINES,
RUN_LOG,
TRIGGER,
agentById,
eventDrivenAgents,
networkStats,
taskDrivenAgents,
} from '@/lib/agentNetwork';
/**
* The agent table behind the Agents page.
*
* It is a hand-maintained snapshot, not a feed — AI_engine exposes no endpoint
* this console can reach. That makes these tests load-bearing in an unusual
* way: they are the only thing standing between a typo in the table and a
* status board that states something untrue with total confidence.
*/
describe('the swarm as recorded', () => {
it('is nine agents — JARVIS and eight specialised', () => {
expect(AGENTS).toHaveLength(9);
expect(AGENTS.map((a) => a.id)).toContain('JARVIS');
});
it('gives every agent a unique id', () => {
const ids = AGENTS.map((a) => a.id);
expect(new Set(ids).size).toBe(ids.length);
});
it('gives every agent a trigger the page can label', () => {
// A trigger with no entry in TRIGGER renders as `undefined` on the card —
// silently, because React prints nothing for it.
AGENTS.forEach((a) => {
expect(Object.keys(TRIGGER)).toContain(a.trigger);
});
});
it('gives every agent the fields the detail panel reads', () => {
AGENTS.forEach((a) => {
expect(typeof a.detail).toBe('string');
expect(a.detail.length).toBeGreaterThan(20);
expect(typeof a.publishes).toBe('string');
expect(typeof a.api).toBe('string');
expect(typeof a.autonomy).toBe('string');
expect(typeof a.lastSeen).toBe('string');
expect(Array.isArray(a.subscribes)).toBe(true);
});
});
it('uses only the four states the page can colour', () => {
// An unknown status falls through TONE to `undefined`, and the card then
// renders with no dot, no badge and no bar — invisible breakage.
AGENTS.forEach((a) => {
expect(['active', 'running', 'degraded', 'idle']).toContain(a.status);
});
});
});
describe('the split that the page is really about', () => {
it('three agents subscribe to the bus themselves', () => {
expect(eventDrivenAgents().map((a) => a.id)).toEqual([
'DISPATCH_AGENT',
'EXCEPTION_AGENT',
'EXPRESS_DISPATCH_AGENT',
]);
});
it('five only run when JARVIS hands them work', () => {
expect(taskDrivenAgents()).toHaveLength(5);
});
it('every event-driven agent names at least one subject', () => {
// The pipeline chip prints `subscribes[0]`. An empty array there renders a
// blank line where a NATS subject should be.
eventDrivenAgents().forEach((a) => {
expect(a.subscribes.length).toBeGreaterThan(0);
});
});
it('no task-driven agent claims to subscribe to anything', () => {
// The whole point of the second group: they cannot be woken by an event.
taskDrivenAgents().forEach((a) => {
expect(a.subscribes).toEqual([]);
});
});
});
describe('the headline numbers are derived, never written down', () => {
it('counts what the cards underneath actually show', () => {
// A dashboard that says "3 active" over three grey dots is the failure
// this guards. Every stat comes off the same table the grid renders.
const s = networkStats();
expect(s.total).toBe(AGENTS.length);
expect(s.working).toBe(AGENTS.filter((a) => a.status === 'active').length);
expect(s.degraded).toBe(AGENTS.filter((a) => a.status === 'degraded').length);
expect(s.working + s.degraded + s.idle + 1).toBe(s.total); // +1 for JARVIS, which is `running`
});
it('reports exactly one agent doing real work', () => {
expect(networkStats().working).toBe(1);
});
it('reports no autonomy gate switched on', () => {
// Stated on the page as a promise: the swarm observes and alerts, and does
// not write to the system. If a gate is ever turned on, this fails and the
// page's own subtitle has to change with it.
const s = networkStats();
expect(s.autonomyOn).toBe(0);
expect(s.gated).toBe(3);
});
it('recomputes for a different set rather than returning constants', () => {
const two = networkStats([
{ status: 'active', events: 5, gated: true },
{ status: 'idle', events: 0, gated: false },
]);
expect(two.total).toBe(2);
expect(two.working).toBe(1);
expect(two.events).toBe(5);
expect(two.gated).toBe(1);
});
});
describe('agentById', () => {
it('finds an agent', () => {
expect(agentById('EXCEPTION_AGENT').name).toBe('Exception');
});
it('falls back to the first rather than returning undefined', () => {
// The page reads `.status` off the result straight away; undefined here is
// a blank panel at best and a crash at worst.
expect(agentById('NOPE')).toBe(AGENTS[0]);
});
});
describe('the run log', () => {
it('uses only tones the page can colour', () => {
RUN_LOG.forEach((l) => {
expect(['info', 'warn', 'error']).toContain(l.tone);
});
});
it("the two repeated errors account for the log's stated error total", () => {
// 2 x 8,594 = 17,188, which is the number in the panel header. If the
// table and the header ever disagree, one of them is lying.
const repeated = RUN_LOG.filter((l) => l.tone === 'error').reduce((n, l) => n + (l.count || 0), 0);
expect(repeated).toBe(LOG_ERROR_LINES);
expect(LOG_ERROR_LINES).toBeLessThan(LOG_TOTAL_LINES);
});
});