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.node != nodeManager { t.Error("worker.node 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 _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mock := &mockMinerInstance{name: "test", minerType: "xmrig"} result := convertMinerStats(mock, tt.rawStats) if result.Name != "test" { t.Errorf("expected name 'test', got '%s'", result.Name) } if result.Hashrate != tt.wantHash { t.Errorf("expected hashrate %f, got %f", tt.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 }