Add LOTL Timeline page for live tier progression visualization.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Operators get a dedicated Onion view with 14-tier stepper, fleet progress chips, and AI decision overlay when fleet AI control is enabled.
This commit is contained in:
58
server/web/src/components/Lotl/LotlTierTimeline.test.tsx
Normal file
58
server/web/src/components/Lotl/LotlTierTimeline.test.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
||||
import LotlTierTimeline from './LotlTierTimeline';
|
||||
import { buildLotlTimelineModel, resolveLotlTierOrder } from '../../help/lotlTimeline';
|
||||
import { mockAgent } from '../../test/fixtures';
|
||||
|
||||
describe('LotlTierTimeline', () => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('renders 14 tiers from fixture attempts', () => {
|
||||
const order = resolveLotlTierOrder();
|
||||
expect(order).toHaveLength(14);
|
||||
|
||||
const agent = mockAgent({
|
||||
status: 'online',
|
||||
lotl_tier: 'wsl',
|
||||
lotl_attempts: [
|
||||
{ tier: 'vuln_recon', ok: true, duration_ms: 800, phase: 'recon' },
|
||||
{ tier: 'docker', ok: false, error: 'no daemon', duration_ms: 1200, phase: 'deploy' },
|
||||
{ tier: 'wsl', ok: false, error: 'distro missing', duration_ms: 900, phase: 'deploy' },
|
||||
],
|
||||
});
|
||||
|
||||
const model = buildLotlTimelineModel(agent, order, agent.lotl_attempts ?? []);
|
||||
expect(model.tiers).toHaveLength(14);
|
||||
expect(model.succeeded).toBe(1);
|
||||
expect(model.tiers[0].state).toBe('success');
|
||||
expect(model.tiers[1].state).toBe('failed');
|
||||
expect(model.tiers[2].state).toBe('trying');
|
||||
|
||||
render(<LotlTierTimeline model={model} agentName="Test Miner" />);
|
||||
|
||||
expect(screen.getByText('ONION TIER CHAIN')).toBeInTheDocument();
|
||||
expect(screen.getByText('1/14 tiers succeeded')).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('listitem')).toHaveLength(14);
|
||||
expect(screen.getByText('Vuln Recon')).toBeInTheDocument();
|
||||
expect(screen.getByText('GPO')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('expands attempt detail on tier click', () => {
|
||||
const order = resolveLotlTierOrder();
|
||||
const agent = mockAgent({
|
||||
lotl_attempts: [
|
||||
{ tier: 'docker', ok: false, error: 'AV blocked', duration_ms: 500, phase: 'deploy' },
|
||||
],
|
||||
});
|
||||
const model = buildLotlTimelineModel(agent, order, agent.lotl_attempts ?? []);
|
||||
|
||||
render(<LotlTierTimeline model={model} agentName="Node A" />);
|
||||
fireEvent.click(screen.getByText('Docker'));
|
||||
expect(screen.getByText('AV blocked')).toBeInTheDocument();
|
||||
expect(screen.getByText('deploy')).toBeInTheDocument();
|
||||
expect(screen.getByText('500ms')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user