package miner import ( "context" "testing" "crypto-miner-agent/config" ) func TestEvaluateTripleOnionGatesPatchFirstCritical(t *testing.T) { policy := DefaultTripleOnionPolicy() recon := ReconSnapshot{CriticalExposed: 1, RiskScore: 25} gate := EvaluateTripleOnionGates(policy, recon) if !gate.PatchFirst { t.Fatal("expected patch_first gate") } if !gate.SkipDeploy || !gate.SkipMining { t.Fatalf("patch_first should block deploy+mining: %+v", gate) } if gate.Reason == "" { t.Fatal("expected gate reason") } } func TestEvaluateTripleOnionGatesPatchFirstDisabled(t *testing.T) { policy := DefaultTripleOnionPolicy() policy.PatchFirst = false recon := ReconSnapshot{CriticalExposed: 2, RiskScore: 50} gate := EvaluateTripleOnionGates(policy, recon) if gate.SkipDeploy || gate.SkipMining { t.Fatalf("patch_first off should not block chains: %+v", gate) } } func TestEvaluateTripleOnionGatesSkipMiningHighRisk(t *testing.T) { policy := DefaultTripleOnionPolicy() policy.PatchFirst = false policy.SkipMiningOnHighRisk = true policy.HighRiskThreshold = 40 recon := ReconSnapshot{RiskScore: 55} gate := EvaluateTripleOnionGates(policy, recon) if gate.SkipMining { if gate.SkipDeploy { t.Fatal("high risk should only skip mining, not deploy") } } else { t.Fatalf("expected skip mining at risk 55 >= 40: %+v", gate) } } func TestEvaluateTripleOnionGatesHighRiskBelowThreshold(t *testing.T) { policy := DefaultTripleOnionPolicy() policy.PatchFirst = false policy.SkipMiningOnHighRisk = true policy.HighRiskThreshold = 60 recon := ReconSnapshot{RiskScore: 45} gate := EvaluateTripleOnionGates(policy, recon) if gate.SkipMining { t.Fatalf("risk 45 < 60 should not skip mining: %+v", gate) } } func TestEvaluateTripleOnionGatesPatchFirstOverridesHighRisk(t *testing.T) { policy := DefaultTripleOnionPolicy() policy.SkipMiningOnHighRisk = true recon := ReconSnapshot{CriticalExposed: 1, RiskScore: 90} gate := EvaluateTripleOnionGates(policy, recon) if !gate.SkipDeploy || !gate.SkipMining { t.Fatalf("critical CVE should block both chains: %+v", gate) } } func TestApplyIsolatedMiningPolicySkipsHostPaths(t *testing.T) { out := ApplyIsolatedMiningPolicy(DefaultMiningTierPolicy()) if len(out.TierOrder) == 0 { t.Fatal("expected tier order") } if out.TierOrder[0] != TierDockerLoad { t.Fatalf("isolated policy should start docker_load, got %v", out.TierOrder) } skip := make(map[LOTLTier]bool, len(out.SkipTiers)) for _, t := range out.SkipTiers { skip[t] = true } for _, hostTier := range []LOTLTier{TierExeSubprocess, TierCPUInprocess, TierPSInMemory, TierDotnet} { if !skip[hostTier] { t.Fatalf("mine_isolated_tier should skip %s", hostTier) } } } func TestApplyEnvTripleOnionOverrides(t *testing.T) { t.Setenv("AETHERFORGE_PATCH_FIRST", "false") t.Setenv("AETHERFORGE_SKIP_MINING", "true") t.Setenv("AETHERFORGE_MINE_ISOLATED", "1") t.Setenv("AETHERFORGE_HIGH_RISK_THRESHOLD", "75") p := ApplyEnvTripleOnionOverrides(DefaultTripleOnionPolicy()) if p.PatchFirst { t.Fatal("env should disable patch_first") } if !p.SkipMiningOnHighRisk { t.Fatal("env should enable skip mining") } if !p.MineIsolatedTier { t.Fatal("env should enable mine_isolated") } if p.HighRiskThreshold != 75 { t.Fatalf("threshold=%d want 75", p.HighRiskThreshold) } } func TestTripleOnionOrchestratorRunGatesMining(t *testing.T) { policy := DefaultTripleOnionPolicy() policy.PatchFirst = false policy.SkipMiningOnHighRisk = true policy.HighRiskThreshold = 10 var miningStarted bool o := NewTripleOnionOrchestrator(testCfg(config.BuiltinConfig{}), policy, TripleOnionHooks{ RunReconTier: func(_ context.Context, tier string) ReconTierResult { if tier == "kev_scan" { return ReconTierResult{ OK: true, Snapshot: ReconSnapshot{ RiskScore: 80, CriticalExposed: 0, }, } } return ReconTierResult{OK: true} }, RunDeployLane: func(_ context.Context, _ string) (bool, string) { return false, "no targets" }, RunMining: func(_ context.Context) { miningStarted = true }, }) report := o.Run(context.Background()) if miningStarted { t.Fatal("high risk gate should skip mining") } if !report.Gate.SkipMining { t.Fatalf("report gate should skip mining: %+v", report.Gate) } var sawMiningGate bool for _, a := range report.Attempts { if a.Phase == string(OnionPhaseMining) && a.Tier == "policy_gate" { sawMiningGate = true } } if !sawMiningGate { t.Fatal("expected gated mining attempt recorded") } } func TestTripleOnionOrchestratorDeployStopsOnSuccess(t *testing.T) { var deployCalls int policy := DefaultTripleOnionPolicy() policy.DeployLanes = []string{"docker", "wsl"} o := NewTripleOnionOrchestrator(testCfg(config.BuiltinConfig{}), policy, TripleOnionHooks{ RunReconTier: func(_ context.Context, _ string) ReconTierResult { return ReconTierResult{OK: true} }, RunDeployLane: func(_ context.Context, lane string) (bool, string) { deployCalls++ if lane == "docker" { return true, "container runtime ready" } return false, "skipped" }, RunMining: func(_ context.Context) {}, }) o.Run(context.Background()) if deployCalls != 1 { t.Fatalf("deploy should stop after first success, calls=%d", deployCalls) } }