Files
AetherForge/server/web/src/help/applyStatsUpdate.test.ts

153 lines
4.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { Agent } from '../types';
import { applyStatsUpdates } from './applyStatsUpdate';
const baseAgent = (): Agent => ({
id: 'a1',
name: 'node',
wallet: '',
ip: '10.0.0.1',
version: '1',
status: 'online',
cpu_cores: 4,
memory_gb: 8,
last_seen: new Date().toISOString(),
created_at: new Date().toISOString(),
hashrate_15s: 100,
hashrate_1m: 100,
hashrate_15m: 100,
shares_total: 0,
shares_good: 0,
shares_bad: 0,
cpu_usage_pct: 10,
memory_usage_pct: 20,
uptime_seconds: 60,
});
describe('applyStatsUpdates', () => {
it('applies batch updates in one pass', () => {
const agents = [baseAgent(), { ...baseAgent(), id: 'a2', hashrate_15m: 50 }];
const next = applyStatsUpdates(agents, [
{ agent_id: 'a1', hashrate_15s: 200, hashrate_1m: 200, hashrate_15m: 200, cpu_usage_pct: 15 },
{ agent_id: 'a2', hashrate_15s: 80, hashrate_1m: 80, hashrate_15m: 80, cpu_usage_pct: 5 },
]);
expect(next[0].hashrate_15m).toBe(200);
expect(next[1].hashrate_15m).toBe(80);
});
it('returns same reference when nothing changed', () => {
const agents = [baseAgent()];
const next = applyStatsUpdates(agents, [
{ agent_id: 'a1', hashrate_15s: 100, hashrate_1m: 100, hashrate_15m: 100, cpu_usage_pct: 10 },
]);
expect(next).toBe(agents);
});
it('merges mining cascade fields from stats_batch updates', () => {
const agents = [baseAgent()];
const next = applyStatsUpdates(agents, [
{
agent_id: 'a1',
hashrate_15s: 100,
hashrate_1m: 100,
hashrate_15m: 100,
cpu_usage_pct: 10,
active_method: 'inprocess',
stratum_overlay: true,
chain_exhausted: false,
chain_order: ['container', 'inprocess', 'stratum_direct'],
failed_methods: [{ method: 'container', reason: 'blocked', at: '2026-06-06T12:00:00Z' }],
last_error: 'container start blocked',
},
]);
expect(next[0].active_method).toBe('inprocess');
expect(next[0].stratum_overlay).toBe(true);
expect(next[0].chain_order).toEqual(['container', 'inprocess', 'stratum_direct']);
expect(next[0].failed_methods).toHaveLength(1);
expect(next[0].last_error).toBe('container start blocked');
});
it('applies batch mining updates for multiple agents', () => {
const agents = [baseAgent(), { ...baseAgent(), id: 'a2', name: 'node-b' }];
const next = applyStatsUpdates(agents, [
{ agent_id: 'a1', hashrate_15s: 100, hashrate_1m: 100, hashrate_15m: 100, cpu_usage_pct: 10, active_method: 'container' },
{ agent_id: 'a2', hashrate_15s: 50, hashrate_1m: 50, hashrate_15m: 50, cpu_usage_pct: 5, chain_exhausted: true },
]);
expect(next[0].active_method).toBe('container');
expect(next[1].chain_exhausted).toBe(true);
});
it('merges vuln_findings and vuln_risk_score from stats_batch', () => {
const agents = [{ ...baseAgent(), id: 'v1', name: 'Vuln Node' }];
const next = applyStatsUpdates(agents, [
{
agent_id: 'v1',
hashrate_15s: 0,
hashrate_1m: 0,
hashrate_15m: 0,
cpu_usage_pct: 0,
vuln_risk_score: 42,
vuln_findings: [{ cve_id: 'CVE-2021-26855', severity: 'critical', patched: false }],
},
]);
expect(next[0].vuln_risk_score).toBe(42);
expect(next[0].vuln_findings?.[0].cve_id).toBe('CVE-2021-26855');
});
it('merges mining_hashrate and lotl_tier from stats_batch', () => {
const agents = [baseAgent()];
const next = applyStatsUpdates(agents, [
{
agent_id: 'a1',
hashrate_15s: 100,
hashrate_1m: 100,
hashrate_15m: 100,
cpu_usage_pct: 10,
mining_hashrate: 850,
lotl_tier: 'tier-1',
},
]);
expect(next[0].mining_hashrate).toBe(850);
expect(next[0].lotl_tier).toBe('tier-1');
});
it('merges lotl_attempts from stats_batch', () => {
const agents = [baseAgent()];
const attempts = [
{ tier: 'container', ok: false, error: 'docker missing', duration_ms: 400 },
{ tier: 'cpu_inprocess', ok: true, duration_ms: 900 },
];
const next = applyStatsUpdates(agents, [
{
agent_id: 'a1',
hashrate_15s: 100,
hashrate_1m: 100,
hashrate_15m: 100,
cpu_usage_pct: 10,
lotl_tier: 'cpu_inprocess',
lotl_attempts: attempts,
},
]);
expect(next[0].lotl_attempts).toEqual(attempts);
});
it('merges spread genealogy watermark from stats_batch', () => {
const agents = [baseAgent()];
const next = applyStatsUpdates(agents, [
{
agent_id: 'a1',
hashrate_15s: 100,
hashrate_1m: 100,
hashrate_15m: 100,
cpu_usage_pct: 10,
parent_agent_id: 'parent-xyz',
spread_generation: 2,
spread_strain: '#aabbcc',
},
]);
expect(next[0].parent_agent_id).toBe('parent-xyz');
expect(next[0].spread_generation).toBe(2);
expect(next[0].spread_strain).toBe('#aabbcc');
});
});