chore: add utility scripts and additional test coverage
STORY: Development requires supporting scripts and comprehensive test coverage. This commit adds utility scripts and extends test suites. COLLABORATION: - scripts/capture-sessions.mjs: Screenshot capture for sessions hub - scripts/capture-timeline.mjs: Timeline view capture utility - sessions-header-logic.ts: Session header business logic - Additional test files for sessions, hooks, and snapshot diffing
This commit is contained in:
parent
812b6cf8c5
commit
173937c1f3
7 changed files with 729 additions and 0 deletions
68
tests/components/sessions/sessions-header-logic.test.ts
Normal file
68
tests/components/sessions/sessions-header-logic.test.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { getSwarmHealth } from '../../../src/components/sessions/sessions-header-logic';
|
||||
import type { AgentRecord, AgentLiveness } from '../../../src/lib/agent-registry';
|
||||
|
||||
// Mock AgentRecord helper
|
||||
function mockAgent(id: string, liveness: AgentLiveness): { agent: AgentRecord, liveness: AgentLiveness } {
|
||||
return {
|
||||
agent: {
|
||||
agent_id: id,
|
||||
display_name: id,
|
||||
role: 'agent',
|
||||
status: 'idle',
|
||||
created_at: new Date().toISOString(),
|
||||
last_seen_at: new Date().toISOString(),
|
||||
version: 1
|
||||
},
|
||||
liveness
|
||||
};
|
||||
}
|
||||
|
||||
test('getSwarmHealth returns green/active when all agents are active', () => {
|
||||
const members = [
|
||||
mockAgent('a1', 'active'),
|
||||
mockAgent('a2', 'active')
|
||||
];
|
||||
const livenessMap = { a1: 'active', a2: 'active' };
|
||||
|
||||
const health = getSwarmHealth(members.map(m => m.agent), livenessMap);
|
||||
assert.equal(health.status, 'active');
|
||||
assert.equal(health.color, 'text-emerald-400');
|
||||
});
|
||||
|
||||
test('getSwarmHealth returns yellow/warning when any agent is stale', () => {
|
||||
const members = [
|
||||
mockAgent('a1', 'active'),
|
||||
mockAgent('a2', 'stale')
|
||||
];
|
||||
const livenessMap = { a1: 'active', a2: 'stale' };
|
||||
|
||||
const health = getSwarmHealth(members.map(m => m.agent), livenessMap);
|
||||
assert.equal(health.status, 'warning');
|
||||
assert.equal(health.color, 'text-amber-400');
|
||||
});
|
||||
|
||||
test('getSwarmHealth returns red/critical when any agent is evicted/dead', () => {
|
||||
const members = [
|
||||
mockAgent('a1', 'active'),
|
||||
mockAgent('a2', 'evicted')
|
||||
];
|
||||
const livenessMap = { a1: 'active', a2: 'evicted' };
|
||||
|
||||
const health = getSwarmHealth(members.map(m => m.agent), livenessMap);
|
||||
assert.equal(health.status, 'critical');
|
||||
assert.equal(health.color, 'text-rose-400');
|
||||
});
|
||||
|
||||
test('getSwarmHealth returns gray/offline when all agents are idle', () => {
|
||||
const members = [
|
||||
mockAgent('a1', 'idle'),
|
||||
mockAgent('a2', 'idle')
|
||||
];
|
||||
const livenessMap = { a1: 'idle', a2: 'idle' };
|
||||
|
||||
const health = getSwarmHealth(members.map(m => m.agent), livenessMap);
|
||||
assert.equal(health.status, 'offline');
|
||||
assert.equal(health.color, 'text-zinc-500');
|
||||
});
|
||||
85
tests/components/sessions/sessions-header.test.ts
Normal file
85
tests/components/sessions/sessions-header.test.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import type { AgentRecord } from '../../../src/lib/agent-registry';
|
||||
|
||||
interface SwarmGroup {
|
||||
swarmId: string;
|
||||
swarmLabel: string;
|
||||
members: AgentRecord[];
|
||||
}
|
||||
|
||||
function groupAgentsBySwarm(
|
||||
agents: AgentRecord[],
|
||||
swarmGroups: SwarmGroup[]
|
||||
): { swarmGroups: SwarmGroup[]; unassigned: AgentRecord[] } {
|
||||
const nonEmptySwarmGroups = swarmGroups.filter(g => g.members.length > 0);
|
||||
const assignedIds = new Set(nonEmptySwarmGroups.flatMap(g => g.members.map(m => m.agent_id)));
|
||||
const unassigned = agents.filter(a => !assignedIds.has(a.agent_id));
|
||||
return { swarmGroups: nonEmptySwarmGroups, unassigned };
|
||||
}
|
||||
|
||||
describe('SessionsHeader: Agent Grouping', () => {
|
||||
const mockAgent = (id: string): AgentRecord => ({
|
||||
agent_id: id,
|
||||
display_name: id,
|
||||
role: 'agent',
|
||||
status: 'idle',
|
||||
created_at: new Date().toISOString(),
|
||||
last_seen_at: new Date().toISOString(),
|
||||
version: 1,
|
||||
});
|
||||
|
||||
it('groups agents by swarm', () => {
|
||||
const agents = [
|
||||
mockAgent('agent-1'),
|
||||
mockAgent('agent-2'),
|
||||
mockAgent('agent-3'),
|
||||
];
|
||||
|
||||
const swarmGroups: SwarmGroup[] = [
|
||||
{
|
||||
swarmId: 'bb-buff',
|
||||
swarmLabel: 'bb-buff',
|
||||
members: [agents[0], agents[1]],
|
||||
},
|
||||
];
|
||||
|
||||
const result = groupAgentsBySwarm(agents, swarmGroups);
|
||||
|
||||
assert.equal(result.swarmGroups.length, 1);
|
||||
assert.equal(result.swarmGroups[0].members.length, 2);
|
||||
assert.equal(result.unassigned.length, 1);
|
||||
assert.equal(result.unassigned[0].agent_id, 'agent-3');
|
||||
});
|
||||
|
||||
it('shows fallback bucket for unassigned agents', () => {
|
||||
const agents = [
|
||||
mockAgent('agent-1'),
|
||||
mockAgent('agent-2'),
|
||||
];
|
||||
|
||||
const swarmGroups: SwarmGroup[] = [];
|
||||
|
||||
const result = groupAgentsBySwarm(agents, swarmGroups);
|
||||
|
||||
assert.equal(result.swarmGroups.length, 0);
|
||||
assert.equal(result.unassigned.length, 2);
|
||||
});
|
||||
|
||||
it('handles empty swarm groups', () => {
|
||||
const agents = [mockAgent('agent-1')];
|
||||
|
||||
const swarmGroups: SwarmGroup[] = [
|
||||
{
|
||||
swarmId: 'bb-empty',
|
||||
swarmLabel: 'bb-empty',
|
||||
members: [],
|
||||
},
|
||||
];
|
||||
|
||||
const result = groupAgentsBySwarm(agents, swarmGroups);
|
||||
|
||||
assert.equal(result.swarmGroups.length, 0, 'Empty swarm groups should not render');
|
||||
assert.equal(result.unassigned.length, 1);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue