feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge

This commit is contained in:
AetherForge
2026-05-30 23:26:50 -07:00
parent 6704933568
commit d005d5d07c
48 changed files with 4621 additions and 22 deletions

View File

@@ -0,0 +1,90 @@
import { describe, expect, it } from 'vitest';
import { blueprintDiff, buildRequestFromRecord } from './buildManager';
describe('blueprintDiff', () => {
it('returns empty diff for identical objects', () => {
const base = { a: 1, b: 'two', c: true };
expect(blueprintDiff(base, { ...base })).toEqual([]);
});
it('detects added, removed, and changed keys', () => {
const base = { keep: 1, gone: 'old', tweak: 'a' };
const current = { keep: 1, newKey: true, tweak: 'b' };
const diff = blueprintDiff(base, current);
expect(diff).toEqual([
{ key: 'gone', kind: 'removed', from: 'old' },
{ key: 'newKey', kind: 'added', to: true },
{ key: 'tweak', kind: 'changed', from: 'a', to: 'b' },
]);
});
it('sorts keys alphabetically', () => {
const diff = blueprintDiff({ z: 1 }, { a: 2, m: 3, z: 1 });
expect(diff.map((d) => d.key)).toEqual(['a', 'm']);
});
it('compares nested values via JSON serialization', () => {
const diff = blueprintDiff(
{ nested: { x: 1, y: 2 } },
{ nested: { x: 1, y: 3 } }
);
expect(diff).toEqual([
{ key: 'nested', kind: 'changed', from: { x: 1, y: 2 }, to: { x: 1, y: 3 } },
]);
});
it('treats array order as significant', () => {
const diff = blueprintDiff({ tags: ['a', 'b'] }, { tags: ['b', 'a'] });
expect(diff).toHaveLength(1);
expect(diff[0].kind).toBe('changed');
});
it('handles empty objects', () => {
expect(blueprintDiff({}, {})).toEqual([]);
expect(blueprintDiff({}, { only: 1 })).toEqual([{ key: 'only', kind: 'added', to: 1 }]);
expect(blueprintDiff({ only: 1 }, {})).toEqual([{ key: 'only', kind: 'removed', from: 1 }]);
});
});
describe('buildRequestFromRecord', () => {
const record = {
worker_name: 'worker-a',
server_url: 'https://c2.example.com',
wallet: '4' + 'B'.repeat(94),
threads: 8,
pool_host: 'pool.example.com',
pool_port: 443,
pool_tls: true,
pool_pass: 'secret',
};
it('spreads defaults then overrides with record fields', () => {
const defaults = { threads: 4, stealth_mode: true, extra_flag: false };
const req = buildRequestFromRecord(record, defaults);
expect(req).toMatchObject({
...defaults,
worker_name: record.worker_name,
server_url: record.server_url,
wallet: record.wallet,
threads: record.threads,
pool_host: record.pool_host,
pool_port: record.pool_port,
pool_tls: record.pool_tls,
pool_pass: record.pool_pass,
});
expect(req.threads).toBe(8);
expect(req.stealth_mode).toBe(true);
});
it('record fields win over colliding default keys', () => {
const req = buildRequestFromRecord(record, { worker_name: 'ignored', threads: 1 });
expect(req.worker_name).toBe('worker-a');
expect(req.threads).toBe(8);
});
it('preserves extra default keys not present on record', () => {
const req = buildRequestFromRecord(record, { fusion_enabled: true, ai_model: 'llama3.2' });
expect(req.fusion_enabled).toBe(true);
expect(req.ai_model).toBe('llama3.2');
});
});

View File

@@ -0,0 +1,200 @@
import { describe, expect, it } from 'vitest';
import {
AI_GUIDE,
CHEAT_SECTIONS,
FORGE_VS_CALIBRATE,
FUSION_GUIDE,
NETWORK_GUIDE,
PIPELINE_STEPS,
ROADMAP_FEATURES,
TROUBLESHOOTING,
} from './cheatSheetContent';
function assertSteps(steps: { id: string; title: string; subtitle: string; icon: string; body: string }[]) {
const ids = steps.map((s) => s.id);
expect(new Set(ids).size).toBe(ids.length);
for (const step of steps) {
expect(step.title.trim().length).toBeGreaterThan(0);
expect(step.subtitle.trim().length).toBeGreaterThan(0);
expect(step.icon.trim().length).toBeGreaterThan(0);
expect(step.body.trim().length).toBeGreaterThan(20);
}
}
describe('PIPELINE_STEPS', () => {
it('defines six pipeline stages in workflow order', () => {
expect(PIPELINE_STEPS).toHaveLength(6);
expect(PIPELINE_STEPS.map((s) => s.id)).toEqual([
'calibrate',
'forge',
'buildmgr',
'drop',
'connect',
'mine',
]);
expect(PIPELINE_STEPS.map((s) => s.title)).toEqual([
'Calibrate',
'Forge',
'Build Manager',
'Drop',
'Connect',
'Mine',
]);
});
it('each step has required content fields', () => {
assertSteps(PIPELINE_STEPS);
});
it('routed steps link to primary app pages', () => {
const routed = PIPELINE_STEPS.filter((s) => s.route);
expect(routed.map((s) => s.route)).toEqual([
'/settings',
'/forge',
'/builds',
'/agents',
'/dashboard',
]);
for (const step of routed) {
expect(step.routeLabel?.trim().length).toBeGreaterThan(0);
}
});
it('drop step includes install one-liner example', () => {
const drop = PIPELINE_STEPS.find((s) => s.id === 'drop')!;
expect(drop.code).toContain('install.ps1');
expect(drop.tips?.some((t) => t.includes('install.sh'))).toBe(true);
});
});
describe('FORGE_VS_CALIBRATE', () => {
it('has forge and calibrate sections with titles and item lists', () => {
expect(FORGE_VS_CALIBRATE.forge.title).toMatch(/Forge/i);
expect(FORGE_VS_CALIBRATE.calibrate.title).toMatch(/Calibrate/i);
expect(FORGE_VS_CALIBRATE.forge.items.length).toBeGreaterThan(10);
expect(FORGE_VS_CALIBRATE.calibrate.items.length).toBeGreaterThan(5);
});
it('forge items cover baked-in agent settings', () => {
const joined = FORGE_VS_CALIBRATE.forge.items.join(' ');
expect(joined).toMatch(/C2|server URL/i);
expect(joined).toMatch(/wallet/i);
expect(joined).toMatch(/Fusion/i);
});
it('calibrate items cover server-only settings', () => {
const joined = FORGE_VS_CALIBRATE.calibrate.items.join(' ');
expect(joined).toMatch(/Listen port/i);
expect(joined).toMatch(/retention/i);
});
});
describe('NETWORK_GUIDE', () => {
it('has five network topology steps with unique ids', () => {
expect(NETWORK_GUIDE).toHaveLength(5);
expect(NETWORK_GUIDE.map((s) => s.id)).toEqual(['n1', 'n2', 'n3', 'n4', 'n5']);
assertSteps(NETWORK_GUIDE);
});
});
describe('FUSION_GUIDE', () => {
it('has four fusion workflow steps', () => {
expect(FUSION_GUIDE).toHaveLength(4);
expect(FUSION_GUIDE.map((s) => s.id)).toEqual(['f1', 'f2', 'f3', 'f4']);
assertSteps(FUSION_GUIDE);
});
});
describe('AI_GUIDE', () => {
it('has three AI autonomy steps', () => {
expect(AI_GUIDE).toHaveLength(3);
expect(AI_GUIDE.map((s) => s.id)).toEqual(['a1', 'a2', 'a3']);
assertSteps(AI_GUIDE);
});
it('mentions Ollama and decide loop', () => {
const bodies = AI_GUIDE.map((s) => s.body).join(' ');
expect(bodies).toMatch(/Ollama/i);
expect(bodies).toMatch(/decide/i);
});
});
describe('TROUBLESHOOTING', () => {
it('lists common problems with non-empty fixes', () => {
expect(TROUBLESHOOTING.length).toBeGreaterThanOrEqual(10);
for (const entry of TROUBLESHOOTING) {
expect(entry.problem.trim().length).toBeGreaterThan(10);
expect(entry.fix.trim().length).toBeGreaterThan(20);
}
});
it('covers forge, pool, and dropper failure modes', () => {
const problems = TROUBLESHOOTING.map((t) => t.problem).join(' ');
expect(problems).toMatch(/Forge/i);
expect(problems).toMatch(/hashrate|shares/i);
expect(problems).toMatch(/dropper|PS1/i);
});
it('shares-rejected fix matches wallet validator range', () => {
const entry = TROUBLESHOOTING.find((t) => t.problem.includes('Shares all rejected'))!;
expect(entry.fix).toMatch(/90.*106/);
expect(entry.fix).toMatch(/4 or 8/);
});
});
describe('ROADMAP_FEATURES', () => {
it('entries have priority, title, and description', () => {
expect(ROADMAP_FEATURES.length).toBeGreaterThan(10);
for (const feat of ROADMAP_FEATURES) {
expect(['high', 'medium', 'low']).toContain(feat.priority);
expect(feat.title.trim().length).toBeGreaterThan(3);
expect(feat.desc.trim().length).toBeGreaterThan(10);
}
});
it('includes shipped core features', () => {
const titles = ROADMAP_FEATURES.map((f) => f.title);
expect(titles).toContain('Build Manager full page');
expect(titles).toContain('AI Autonomy (Ollama)');
expect(titles).toContain('Dropper endpoints');
});
});
describe('CHEAT_SECTIONS', () => {
const expectedSections = [
{ id: 'pipeline', title: 'End-to-end pipeline' },
{ id: 'network', title: 'Network topology — Cloudflare tunnel setup' },
{ id: 'fusion', title: 'Fusion workflow' },
{ id: 'ai', title: 'AI Autonomy workflow' },
{ id: 'troubleshoot', title: 'Troubleshooting' },
];
it('registers five guide sections with stable ids and titles', () => {
expect(CHEAT_SECTIONS).toHaveLength(5);
expect(CHEAT_SECTIONS.map((s) => ({ id: s.id, title: s.title }))).toEqual(expectedSections);
});
it('each section has a non-empty description', () => {
for (const section of CHEAT_SECTIONS) {
expect(section.description.trim().length).toBeGreaterThan(20);
}
});
it('step sections reference the exported step arrays', () => {
const byId = Object.fromEntries(CHEAT_SECTIONS.map((s) => [s.id, s]));
expect(byId.pipeline.steps).toBe(PIPELINE_STEPS);
expect(byId.network.steps).toBe(NETWORK_GUIDE);
expect(byId.fusion.steps).toBe(FUSION_GUIDE);
expect(byId.ai.steps).toBe(AI_GUIDE);
});
it('troubleshoot section maps cards from TROUBLESHOOTING', () => {
const troubleshoot = CHEAT_SECTIONS.find((s) => s.id === 'troubleshoot')!;
expect(troubleshoot.cards).toHaveLength(TROUBLESHOOTING.length);
expect(troubleshoot.cards![0]).toEqual({
title: TROUBLESHOOTING[0].problem,
body: TROUBLESHOOTING[0].fix,
accent: 'amber',
});
});
});

View File

@@ -361,7 +361,7 @@ export const TROUBLESHOOTING = [
},
{
problem: 'Shares all rejected',
fix: 'Wallet address is invalid or wrong for the pool. Monero wallet addresses are 95 chars starting with 4. Some pools require exact format — check your pool dashboard. Accept rate updates live once real shares come in.',
fix: 'Wallet address is invalid or wrong for the pool. Monero wallet addresses are 90106 chars starting with 4 or 8. Some pools require exact format — check your pool dashboard. Accept rate updates live once real shares come in.',
},
{
problem: 'Preflight ✕ blocking forge',

View File

@@ -0,0 +1,74 @@
import { describe, expect, it } from 'vitest';
import { FORGE_BUILD_DEFAULTS, forgeDefaultsFromServer } from './forgeDefaults';
import { mockServerConfig, mockServerInfo } from '../test/fixtures';
describe('FORGE_BUILD_DEFAULTS', () => {
it('sets safe production defaults for a new forge form', () => {
expect(FORGE_BUILD_DEFAULTS.threads).toBe(4);
expect(FORGE_BUILD_DEFAULTS.thread_mode).toBe('percent');
expect(FORGE_BUILD_DEFAULTS.stealth_mode).toBe(true);
expect(FORGE_BUILD_DEFAULTS.persistence).toBe(true);
expect(FORGE_BUILD_DEFAULTS.fusion_enabled).toBe(false);
expect(FORGE_BUILD_DEFAULTS.ai_enabled).toBe(false);
expect(FORGE_BUILD_DEFAULTS.auto_spread).toBe(false);
expect(FORGE_BUILD_DEFAULTS.remote_aggressive).toBe(false);
});
it('omits per-build identity fields (filled by server merge)', () => {
const keys = Object.keys(FORGE_BUILD_DEFAULTS);
expect(keys).not.toContain('worker_name');
expect(keys).not.toContain('server_url');
expect(keys).not.toContain('wallet');
expect(keys).not.toContain('pool_host');
expect(keys).not.toContain('pool_port');
expect(keys).not.toContain('pool_tls');
expect(keys).not.toContain('pool_pass');
});
});
describe('forgeDefaultsFromServer', () => {
it('merges server config pool/wallet with build defaults', () => {
const config = mockServerConfig();
const result = forgeDefaultsFromServer(config, mockServerInfo);
expect(result.wallet).toBe(config.wallet.address);
expect(result.pool_host).toBe(config.pool.host);
expect(result.pool_port).toBe(config.pool.port);
expect(result.pool_tls).toBe(config.pool.use_tls);
expect(result.pool_pass).toBe('x');
expect(result.worker_name).toBe('');
expect(result.stealth_mode).toBe(FORGE_BUILD_DEFAULTS.stealth_mode);
});
it('prefers trimmed public_url over suggested_url', () => {
const config = mockServerConfig({
server: { public_url: ' https://tunnel.example.com ' },
});
const result = forgeDefaultsFromServer(config, mockServerInfo);
expect(result.server_url).toBe('https://tunnel.example.com');
});
it('falls back to suggested_url when public_url is blank', () => {
const config = mockServerConfig({
server: { public_url: ' ' },
});
const result = forgeDefaultsFromServer(config, mockServerInfo);
expect(result.server_url).toBe(mockServerInfo.suggested_url);
});
it('reflects obfuscate and sign defaults from server config', () => {
const config = mockServerConfig({
server: { obfuscate_default: true, sign_enabled: true },
});
const result = forgeDefaultsFromServer(config, mockServerInfo);
expect(result.obfuscate).toBe(true);
expect(result.sign_build).toBe(true);
});
it('uses custom pool password when configured', () => {
const config = mockServerConfig({
pool: { password: 'worker-pass' },
});
const result = forgeDefaultsFromServer(config, mockServerInfo);
expect(result.pool_pass).toBe('worker-pass');
});
});

View File

@@ -1,5 +1,9 @@
import { describe, it, expect } from 'vitest';
import { AGGRESSIVE_REMOTE_ACTIONS, canRunAggressiveAction } from './aggressiveActions';
import {
AGGRESSIVE_REMOTE_ACTIONS,
aggressiveActionHint,
canRunAggressiveAction,
} from './aggressiveActions';
/** Buttons in AgentRemoteActions (full + compact) — must match agent/client handleCommand. */
const UI_REMOTE_ACTIONS = [
@@ -71,3 +75,69 @@ describe('remote action wiring', () => {
expect(canRunAggressiveAction('defender_off', caps, 'windows')).toBe(true);
});
});
describe('AGGRESSIVE_REMOTE_ACTIONS', () => {
it('lists every wired aggressive command once', () => {
expect(AGGRESSIVE_REMOTE_ACTIONS).toHaveLength(9);
expect(new Set(AGGRESSIVE_REMOTE_ACTIONS).size).toBe(9);
});
});
const fullCaps = {
hole_punch: true,
remote_aggressive: true,
mesh_p2p: true,
auto_spread: true,
process_hollowing: false,
ai_enabled: false,
};
describe('canRunAggressiveAction edge cases', () => {
it('allows all actions when caps are undefined (legacy agents)', () => {
for (const action of AGGRESSIVE_REMOTE_ACTIONS) {
if (action === 'defender_off') continue;
expect(canRunAggressiveAction(action, undefined, 'windows')).toBe(true);
}
});
it('spread_now requires auto_spread or remote_aggressive', () => {
const base = { ...fullCaps, auto_spread: false, remote_aggressive: false };
expect(canRunAggressiveAction('spread_now', base)).toBe(false);
expect(canRunAggressiveAction('spread_now', { ...base, auto_spread: true })).toBe(true);
expect(canRunAggressiveAction('spread_now', { ...base, remote_aggressive: true })).toBe(true);
});
it('mesh_status requires mesh_p2p capability', () => {
expect(canRunAggressiveAction('mesh_status', { ...fullCaps, mesh_p2p: false })).toBe(false);
expect(canRunAggressiveAction('mesh_status', fullCaps)).toBe(true);
});
it('remote aggressive ops gate tunnel, scan, defender, firewall', () => {
const noAgg = { ...fullCaps, remote_aggressive: false };
for (const action of ['start_tunnel', 'subnet_scan', 'defender_off', 'firewall_punch'] as const) {
expect(canRunAggressiveAction(action, noAgg, 'windows')).toBe(false);
expect(canRunAggressiveAction(action, fullCaps, 'windows')).toBe(true);
}
});
});
describe('aggressiveActionHint', () => {
it('returns undefined when action is allowed', () => {
expect(aggressiveActionHint('hole_punch', fullCaps)).toBeUndefined();
expect(aggressiveActionHint('spread_now', fullCaps)).toBeUndefined();
});
it('returns macOS-specific hint for defender_off', () => {
expect(aggressiveActionHint('defender_off', fullCaps, 'darwin')).toBe(
'Defender disable not supported on macOS'
);
});
it('suggests re-forge hints when capability missing', () => {
const noCaps = { ...fullCaps, hole_punch: false, auto_spread: false, remote_aggressive: false, mesh_p2p: false };
expect(aggressiveActionHint('hole_punch', noCaps)).toContain('NAT Hole Punch');
expect(aggressiveActionHint('spread_now', noCaps)).toContain('Auto-Spread');
expect(aggressiveActionHint('mesh_status', noCaps)).toContain('Mesh P2P');
expect(aggressiveActionHint('start_tunnel', noCaps)).toContain('Remote Aggressive Ops');
});
});