Mining/pkg/node/worker_test.go
Claude 63a9107f9c
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
ax(batch): add missing test files and fix pre-existing test compilation errors
Adds test files for 6 source files that had no corresponding test:
bufpool_test.go (mining + node), metrics_test.go, version_test.go,
supervisor_test.go, mining_profile_test.go. Each follows
TestFilename_Function_{Good,Bad,Ugly} convention.

Also fixes 2 pre-existing compilation errors:
- ratelimiter_test.go: rl -> rateLimiter (leftover from AX rename)
- worker_test.go: worker.node -> worker.nodeManager (field was renamed)

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 18:30:42 +01:00

969 lines
27 KiB
Go

package node
import (
"os"
"path/filepath"
"testing"
"time"
)
// setupTestEnv sets up a temporary environment for testing and returns cleanup function
func setupTestEnv(t *testing.T) func() {
tmpDir := t.TempDir()
os.Setenv("XDG_CONFIG_HOME", filepath.Join(tmpDir, "config"))
os.Setenv("XDG_DATA_HOME", filepath.Join(tmpDir, "data"))
return func() {
os.Unsetenv("XDG_CONFIG_HOME")
os.Unsetenv("XDG_DATA_HOME")
}
}
func TestWorker_NewWorker_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
if worker == nil {
t.Fatal("NewWorker returned nil")
}
if worker.nodeManager != nodeManager {
t.Error("worker.nodeManager not set correctly")
}
if worker.transport != transport {
t.Error("worker.transport not set correctly")
}
}
func TestWorker_SetMinerManager_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
mockManager := &mockMinerManager{}
worker.SetMinerManager(mockManager)
if worker.minerManager != mockManager {
t.Error("minerManager not set correctly")
}
}
func TestWorker_SetProfileManager_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
mockProfile := &mockProfileManager{}
worker.SetProfileManager(mockProfile)
if worker.profileManager != mockProfile {
t.Error("profileManager not set correctly")
}
}
func TestWorker_HandlePing_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a ping message
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
pingPayload := PingPayload{SentAt: time.Now().UnixMilli()}
pingMsg, err := NewMessage(MsgPing, "sender-id", identity.ID, pingPayload)
if err != nil {
t.Fatalf("failed to create ping message: %v", err)
}
// Call handlePing directly
response, err := worker.handlePing(pingMsg)
if err != nil {
t.Fatalf("handlePing returned error: %v", err)
}
if response == nil {
t.Fatal("handlePing returned nil response")
}
if response.Type != MsgPong {
t.Errorf("expected response type %s, got %s", MsgPong, response.Type)
}
var pong PongPayload
if err := response.ParsePayload(&pong); err != nil {
t.Fatalf("failed to parse pong payload: %v", err)
}
if pong.SentAt != pingPayload.SentAt {
t.Errorf("pong SentAt mismatch: expected %d, got %d", pingPayload.SentAt, pong.SentAt)
}
if pong.ReceivedAt == 0 {
t.Error("pong ReceivedAt not set")
}
}
func TestWorker_HandlePing_Bad(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Message with invalid payload JSON must return an error
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
badMsg, err := NewMessage(MsgPing, "sender-id", identity.ID, nil)
if err != nil {
t.Fatalf("failed to create message: %v", err)
}
// Corrupt the payload so ParsePayload fails
badMsg.Payload = []byte(`{not-valid-json`)
_, err = worker.handlePing(badMsg)
if err == nil {
t.Error("expected error for invalid ping payload")
}
}
func TestWorker_HandlePing_Ugly(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// SentAt of zero is an edge case — response must still include it verbatim
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
pingPayload := PingPayload{SentAt: 0}
pingMsg, err := NewMessage(MsgPing, "sender-id", identity.ID, pingPayload)
if err != nil {
t.Fatalf("failed to create ping message: %v", err)
}
response, err := worker.handlePing(pingMsg)
if err != nil {
t.Fatalf("handlePing returned error for zero SentAt: %v", err)
}
if response == nil {
t.Fatal("handlePing returned nil response for zero SentAt")
}
var pong PongPayload
if err := response.ParsePayload(&pong); err != nil {
t.Fatalf("failed to parse pong payload: %v", err)
}
if pong.SentAt != 0 {
t.Errorf("pong SentAt should echo zero value, got %d", pong.SentAt)
}
}
func TestWorker_HandleGetStats_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a get_stats message
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
msg, err := NewMessage(MsgGetStats, "sender-id", identity.ID, nil)
if err != nil {
t.Fatalf("failed to create get_stats message: %v", err)
}
// Call handleGetStats directly (without miner manager)
response, err := worker.handleGetStats(msg)
if err != nil {
t.Fatalf("handleGetStats returned error: %v", err)
}
if response == nil {
t.Fatal("handleGetStats returned nil response")
}
if response.Type != MsgStats {
t.Errorf("expected response type %s, got %s", MsgStats, response.Type)
}
var stats StatsPayload
if err := response.ParsePayload(&stats); err != nil {
t.Fatalf("failed to parse stats payload: %v", err)
}
if stats.NodeID != identity.ID {
t.Errorf("stats NodeID mismatch: expected %s, got %s", identity.ID, stats.NodeID)
}
if stats.NodeName != identity.Name {
t.Errorf("stats NodeName mismatch: expected %s, got %s", identity.Name, stats.NodeName)
}
}
func TestWorker_HandleGetStats_Bad(t *testing.T) {
tmpDir := t.TempDir()
// Identity deliberately not generated — worker must return error
nodeManager, err := NewNodeManagerWithPaths(
tmpDir+"/private.key",
tmpDir+"/node.json",
)
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
msg, err := NewMessage(MsgGetStats, "sender-id", "uninitialised-id", nil)
if err != nil {
t.Fatalf("failed to create get_stats message: %v", err)
}
_, err = worker.handleGetStats(msg)
if err == nil {
t.Error("expected error when node identity is not initialised")
}
}
func TestWorker_HandleGetStats_Ugly(t *testing.T) {
tmpDir := t.TempDir()
nodeManager, err := NewNodeManagerWithPaths(
tmpDir+"/private.key",
tmpDir+"/node.json",
)
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Miner manager returns stats errors — handleGetStats must skip failed miners and still succeed
failingManager := &mockMinerManager{
miners: []MinerInstance{&mockFailingMinerInstance{name: "broken", minerType: "xmrig"}},
}
worker.SetMinerManager(failingManager)
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
msg, err := NewMessage(MsgGetStats, "sender-id", identity.ID, nil)
if err != nil {
t.Fatalf("failed to create get_stats message: %v", err)
}
response, err := worker.handleGetStats(msg)
if err != nil {
t.Fatalf("handleGetStats should not fail when a miner stat fetch fails: %v", err)
}
if response == nil {
t.Fatal("expected a response even when miner stats fail")
}
var stats StatsPayload
if err := response.ParsePayload(&stats); err != nil {
t.Fatalf("failed to parse stats payload: %v", err)
}
// Broken miner is skipped — miners list is empty
if len(stats.Miners) != 0 {
t.Errorf("expected 0 miners (failed stat skipped), got %d", len(stats.Miners))
}
}
func TestWorker_HandleStartMiner_Bad(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a start_miner message
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
payload := StartMinerPayload{MinerType: "xmrig", ProfileID: "test-profile"}
msg, err := NewMessage(MsgStartMiner, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create start_miner message: %v", err)
}
// Without miner manager, should return error
_, err = worker.handleStartMiner(msg)
if err == nil {
t.Error("expected error when miner manager is nil")
}
}
func TestWorker_HandleStopMiner_Bad(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a stop_miner message
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
payload := StopMinerPayload{MinerName: "test-miner"}
msg, err := NewMessage(MsgStopMiner, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create stop_miner message: %v", err)
}
// Without miner manager, should return error
_, err = worker.handleStopMiner(msg)
if err == nil {
t.Error("expected error when miner manager is nil")
}
}
func TestWorker_HandleGetLogs_Bad(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a get_logs message
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
payload := GetLogsPayload{MinerName: "test-miner", Lines: 100}
msg, err := NewMessage(MsgGetLogs, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create get_logs message: %v", err)
}
// Without miner manager, should return error
_, err = worker.handleGetLogs(msg)
if err == nil {
t.Error("expected error when miner manager is nil")
}
}
func TestWorker_HandleStartMiner_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
worker.SetMinerManager(&mockMinerManager{})
worker.SetProfileManager(&mockProfileManager{})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleStartMiner(msg) — starts miner via profile manager config
payload := StartMinerPayload{MinerType: "xmrig", ProfileID: "pool-main"}
msg, err := NewMessage(MsgStartMiner, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create start_miner message: %v", err)
}
response, err := worker.handleStartMiner(msg)
if err != nil {
t.Fatalf("handleStartMiner returned unexpected error: %v", err)
}
if response == nil {
t.Fatal("handleStartMiner returned nil response")
}
if response.Type != MsgMinerAck {
t.Errorf("expected response type %s, got %s", MsgMinerAck, response.Type)
}
}
func TestWorker_HandleStartMiner_Ugly(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
worker.SetMinerManager(&mockMinerManager{})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleStartMiner(msg) — empty miner type triggers validation error
payload := StartMinerPayload{MinerType: ""}
msg, err := NewMessage(MsgStartMiner, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create start_miner message: %v", err)
}
_, err = worker.handleStartMiner(msg)
if err == nil {
t.Error("expected error for empty miner type")
}
}
func TestWorker_HandleStopMiner_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
worker.SetMinerManager(&mockMinerManager{})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleStopMiner(msg) — stops named miner, returns MinerAck with success
payload := StopMinerPayload{MinerName: "xmrig"}
msg, err := NewMessage(MsgStopMiner, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create stop_miner message: %v", err)
}
response, err := worker.handleStopMiner(msg)
if err != nil {
t.Fatalf("handleStopMiner returned unexpected error: %v", err)
}
if response == nil {
t.Fatal("handleStopMiner returned nil response")
}
if response.Type != MsgMinerAck {
t.Errorf("expected response type %s, got %s", MsgMinerAck, response.Type)
}
var ack MinerAckPayload
if err := response.ParsePayload(&ack); err != nil {
t.Fatalf("failed to parse ack payload: %v", err)
}
if !ack.Success {
t.Errorf("expected success=true, got false: %s", ack.Error)
}
}
func TestWorker_HandleStopMiner_Ugly(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
worker.SetMinerManager(&mockMinerManager{})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleStopMiner(msg) — corrupted JSON payload triggers parse error
msg := &Message{
ID: "test-id",
Type: MsgStopMiner,
From: "sender-id",
To: identity.ID,
Payload: []byte(`{invalid json`),
}
_, err = worker.handleStopMiner(msg)
if err == nil {
t.Error("expected error for malformed stop_miner payload")
}
}
func TestWorker_HandleGetLogs_Good(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
minerInstance := &mockMinerInstance{name: "xmrig", minerType: "xmrig"}
worker.SetMinerManager(&mockMinerManager{miners: []MinerInstance{minerInstance}})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleGetLogs(msg) — fetches console history for a running miner
payload := GetLogsPayload{MinerName: "xmrig", Lines: 50}
msg, err := NewMessage(MsgGetLogs, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create get_logs message: %v", err)
}
response, err := worker.handleGetLogs(msg)
if err != nil {
t.Fatalf("handleGetLogs returned unexpected error: %v", err)
}
if response == nil {
t.Fatal("handleGetLogs returned nil response")
}
if response.Type != MsgLogs {
t.Errorf("expected response type %s, got %s", MsgLogs, response.Type)
}
var logs LogsPayload
if err := response.ParsePayload(&logs); err != nil {
t.Fatalf("failed to parse logs payload: %v", err)
}
if logs.MinerName != "xmrig" {
t.Errorf("expected miner name xmrig, got %s", logs.MinerName)
}
}
func TestWorker_HandleGetLogs_Ugly(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
worker.SetMinerManager(&mockMinerManager{})
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
// worker.handleGetLogs(msg) — corrupted JSON payload triggers parse error
msg := &Message{
ID: "test-id",
Type: MsgGetLogs,
From: "sender-id",
To: identity.ID,
Payload: []byte(`{invalid json`),
}
_, err = worker.handleGetLogs(msg)
if err == nil {
t.Error("expected error for malformed get_logs payload")
}
}
func TestWorker_HandleDeploy_Bad(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a deploy message for profile
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
payload := DeployPayload{
BundleType: "profile",
Data: []byte(`{"id": "test", "name": "Test Profile"}`),
Name: "test-profile",
}
msg, err := NewMessage(MsgDeploy, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create deploy message: %v", err)
}
// Without profile manager, should return error
_, err = worker.handleDeploy(nil, msg)
if err == nil {
t.Error("expected error when profile manager is nil")
}
}
func TestWorker_HandleDeploy_Ugly(t *testing.T) {
cleanup := setupTestEnv(t)
defer cleanup()
nodeManager, err := NewNodeManager()
if err != nil {
t.Fatalf("failed to create node manager: %v", err)
}
if err := nodeManager.GenerateIdentity("test-worker", RoleWorker); err != nil {
t.Fatalf("failed to generate identity: %v", err)
}
peerRegistry, err := NewPeerRegistryWithPath(t.TempDir() + "/peers.json")
if err != nil {
t.Fatalf("failed to create peer registry: %v", err)
}
transport := NewTransport(nodeManager, peerRegistry, DefaultTransportConfig())
worker := NewWorker(nodeManager, transport)
// Create a deploy message with unknown type
identity := nodeManager.GetIdentity()
if identity == nil {
t.Fatal("expected identity to be generated")
}
payload := DeployPayload{
BundleType: "unknown",
Data: []byte(`{}`),
Name: "test",
}
msg, err := NewMessage(MsgDeploy, "sender-id", identity.ID, payload)
if err != nil {
t.Fatalf("failed to create deploy message: %v", err)
}
_, err = worker.handleDeploy(nil, msg)
if err == nil {
t.Error("expected error for unknown bundle type")
}
}
func TestWorker_ConvertMinerStats_Good(t *testing.T) {
tests := []struct {
name string
rawStats interface{}
wantHash float64
}{
{
name: "MapWithHashrate",
rawStats: map[string]interface{}{
"hashrate": 100.5,
"shares": 10,
"rejected": 2,
"uptime": 3600,
"pool": "test-pool",
"algorithm": "rx/0",
},
wantHash: 100.5,
},
{
name: "EmptyMap",
rawStats: map[string]interface{}{},
wantHash: 0,
},
{
name: "NonMap",
rawStats: "not a map",
wantHash: 0,
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
mock := &mockMinerInstance{name: "test", minerType: "xmrig"}
result := convertMinerStats(mock, testCase.rawStats)
if result.Name != "test" {
t.Errorf("expected name 'test', got '%s'", result.Name)
}
if result.Hashrate != testCase.wantHash {
t.Errorf("expected hashrate %f, got %f", testCase.wantHash, result.Hashrate)
}
})
}
}
// Mock implementations for testing
type mockMinerManager struct {
miners []MinerInstance
}
func (m *mockMinerManager) StartMiner(minerType string, config interface{}) (MinerInstance, error) {
return &mockMinerInstance{name: minerType, minerType: minerType}, nil
}
func (m *mockMinerManager) StopMiner(name string) error {
return nil
}
func (m *mockMinerManager) ListMiners() []MinerInstance {
return m.miners
}
func (m *mockMinerManager) GetMiner(name string) (MinerInstance, error) {
for _, miner := range m.miners {
if miner.GetName() == name {
return miner, nil
}
}
return nil, nil
}
type mockMinerInstance struct {
name string
minerType string
stats interface{}
}
func (m *mockMinerInstance) GetName() string { return m.name }
func (m *mockMinerInstance) GetType() string { return m.minerType }
func (m *mockMinerInstance) GetStats() (interface{}, error) { return m.stats, nil }
func (m *mockMinerInstance) GetConsoleHistory(lines int) []string { return []string{} }
type mockFailingMinerInstance struct {
name string
minerType string
}
func (m *mockFailingMinerInstance) GetName() string { return m.name }
func (m *mockFailingMinerInstance) GetType() string { return m.minerType }
func (m *mockFailingMinerInstance) GetStats() (interface{}, error) {
return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "stats unavailable"}
}
func (m *mockFailingMinerInstance) GetConsoleHistory(lines int) []string { return []string{} }
type mockProfileManager struct{}
func (m *mockProfileManager) GetProfile(id string) (interface{}, error) {
return nil, nil
}
func (m *mockProfileManager) SaveProfile(profile interface{}) error {
return nil
}