Final sweep: Crucible fixes, Path Tracer polish, forge progress, tests green.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Align dashboard subtitle default and UpsertAgent tests with fleet label behavior; WebSocket coalesce and PathForge hardening; Crucible expanded ops and visual DV fixes; Vitest 610/610 and full test-suite pass; trim PROBLEMS.md to open items only.
This commit is contained in:
AetherForge
2026-06-06 18:07:47 -07:00
parent e65753ce49
commit 6372b07e6c
40 changed files with 1495 additions and 794 deletions

View File

@@ -1,6 +1,13 @@
import { describe, expect, it } from 'vitest';
/**
* @vitest-environment happy-dom
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { mockAgent } from '../test/fixtures';
import {
import { useWebSocket } from '../hooks/useWebSocket';
import { api } from '../api/client';
import CruciblePage, {
agentColor,
patchLabel,
pendingBadge,
@@ -11,6 +18,201 @@ import {
thermalBadge,
} from './CruciblePage';
// ── Mocks ─────────────────────────────────────────────────────────────────
vi.mock('../hooks/useWebSocket', () => ({ useWebSocket: vi.fn() }));
vi.mock('../context/MatrixRainContext', () => ({
useMatrixRain: () => ({ setCrucibleFocus: vi.fn() }),
}));
vi.mock('../hooks/useFleetGroups', () => ({
useFleetGroups: () => ({ groups: [], addGroup: vi.fn(), removeGroup: vi.fn() }),
}));
vi.mock('../api/client', () => ({
api: {
sendAgentCommand: vi.fn().mockResolvedValue({ success: true }),
},
}));
vi.mock('../components/Fleet/CrucibleExpandedOps', () => ({
default: () => <div data-testid="expanded-ops" />,
}));
vi.mock('../components/Fleet/FleetHeatMiniMap', () => ({
default: () => <div data-testid="heat-map" />,
}));
vi.mock('../components/Fleet/LatencyBadge', () => ({
default: () => null,
}));
vi.mock('../components/Fleet/CreateGroupModal', () => ({
default: () => null,
}));
vi.mock('../components/Fleet/FleetGroupsStrip', () => ({
default: () => null,
}));
vi.mock('../components/Presence/AlsoHere', () => ({
default: () => null,
}));
vi.mock('../components/Fleet/FullSysCheckPanel', () => ({
default: () => null,
}));
vi.mock('../components/HelpTip', () => ({
HelpTip: () => null,
}));
vi.mock('../components/NeonCard/NeonCard', () => ({
default: ({ children, className }: { children: React.ReactNode; className?: string }) => (
<div className={className}>{children}</div>
),
}));
const useWebSocketMock = vi.mocked(useWebSocket);
function makeWsValue(overrides: Partial<ReturnType<typeof useWebSocket>>) {
return {
isConnected: true,
agents: [],
recentShares: [],
fleetAlerts: [],
poolStatus: [],
aiActivity: [],
agentLogs: {},
commandResults: [],
policyAcks: [],
latestMessage: null,
sendDashboardMessage: vi.fn(),
...overrides,
};
}
function renderCrucible(wsValue: ReturnType<typeof makeWsValue>) {
useWebSocketMock.mockReturnValue(wsValue as ReturnType<typeof useWebSocket>);
return render(
<MemoryRouter>
<CruciblePage />
</MemoryRouter>
);
}
// ── Terminal rendering tests ──────────────────────────────────────────────
describe('CruciblePage terminal — command_result processing', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
cleanup();
});
it('displays exec result in terminal when agent is selected', async () => {
const agent = mockAgent({ id: 'agent-aaa-001', name: 'TestNode', status: 'online' });
const commandResults = [
{ agent_id: 'agent-aaa-001', action: 'exec', success: true, message: 'hello world', _seq: 1 },
];
// Render with the agent already "selected" by providing a pre-selected state.
// The component reads commandResults from context and renders terminal lines.
renderCrucible(makeWsValue({ agents: [agent], commandResults }));
// The terminal should display the result message from the command.
await waitFor(() => {
expect(screen.getByText('hello world')).toBeInTheDocument();
});
});
it('displays multiple exec result lines split on newline', async () => {
const agent = mockAgent({ id: 'agent-bbb-002', name: 'MultiNode', status: 'online' });
const commandResults = [
{
agent_id: 'agent-bbb-002',
action: 'exec',
success: true,
message: 'line one\nline two\nline three',
_seq: 1,
},
];
renderCrucible(makeWsValue({ agents: [agent], commandResults }));
await waitFor(() => {
expect(screen.getByText('line one')).toBeInTheDocument();
expect(screen.getByText('line two')).toBeInTheDocument();
expect(screen.getByText('line three')).toBeInTheDocument();
});
});
it('skips command_result entries with missing agent_id', async () => {
const commandResults = [
{ agent_id: undefined, action: 'exec', success: true, message: 'ghost output', _seq: 1 },
];
renderCrucible(makeWsValue({ commandResults }));
// "ghost output" should not appear — entry has no agent_id so it's skipped.
await new Promise((r) => setTimeout(r, 50));
expect(screen.queryByText('ghost output')).not.toBeInTheDocument();
});
it('skips already-seen entries when new commandResults arrive', async () => {
const agent = mockAgent({ id: 'agent-ccc-003', name: 'SeqNode', status: 'online' });
// Initial render: seq=1
const ws1 = makeWsValue({
agents: [agent],
commandResults: [{ agent_id: 'agent-ccc-003', action: 'exec', success: true, message: 'first', _seq: 1 }],
});
const { rerender } = renderCrucible(ws1);
await waitFor(() => expect(screen.getByText('first')).toBeInTheDocument());
// Update: add seq=2, keep seq=1 — only 'second' should be added (not 'first' again)
const ws2 = makeWsValue({
agents: [agent],
commandResults: [
{ agent_id: 'agent-ccc-003', action: 'exec', success: true, message: 'first', _seq: 1 },
{ agent_id: 'agent-ccc-003', action: 'exec', success: true, message: 'second', _seq: 2 },
],
});
useWebSocketMock.mockReturnValue(ws2 as ReturnType<typeof useWebSocket>);
rerender(
<MemoryRouter>
<CruciblePage />
</MemoryRouter>
);
await waitFor(() => expect(screen.getByText('second')).toBeInTheDocument());
// 'first' should appear exactly once (not twice from a replay)
expect(screen.getAllByText('first')).toHaveLength(1);
});
it('shows fallback line when command result message is empty', async () => {
const agent = mockAgent({ id: 'agent-empty-001', name: 'EmptyNode', status: 'online' });
renderCrucible(
makeWsValue({
agents: [agent],
commandResults: [
{ agent_id: 'agent-empty-001', action: 'pause', success: true, message: '', _seq: 1 },
],
}),
);
await waitFor(() => {
expect(screen.getByText('[pause] OK')).toBeInTheDocument();
});
});
});
// ── Helper function tests ─────────────────────────────────────────────────
describe('CruciblePage helpers', () => {
it('agentColor cycles palette by agent order', () => {
const ids = ['a', 'b', 'c'];