Mining/pkg/mining/events_test.go
snider b454bbd6d6 feat: Add context propagation, state sync, and tests
- Add context.Context to ManagerInterface methods (StartMiner, StopMiner, UninstallMiner)
- Add WebSocket state sync on client connect (sends current miner states)
- Add EventStateSync event type and SetStateProvider method
- Add manager lifecycle tests (idempotent stop, context cancellation, shutdown timeout)
- Add database tests (initialization, hashrate storage, stats)
- Add EventHub tests (creation, broadcast, client count, state provider)
- Update all test files for new context-aware API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 10:10:39 +00:00

201 lines
4 KiB
Go

package mining
import (
"encoding/json"
"sync"
"testing"
"time"
"github.com/gorilla/websocket"
)
func TestNewEventHub(t *testing.T) {
hub := NewEventHub()
if hub == nil {
t.Fatal("NewEventHub returned nil")
}
if hub.clients == nil {
t.Error("clients map should be initialized")
}
if hub.maxConnections != DefaultMaxConnections {
t.Errorf("Expected maxConnections %d, got %d", DefaultMaxConnections, hub.maxConnections)
}
}
func TestNewEventHubWithOptions(t *testing.T) {
hub := NewEventHubWithOptions(50)
if hub.maxConnections != 50 {
t.Errorf("Expected maxConnections 50, got %d", hub.maxConnections)
}
// Test with invalid value
hub2 := NewEventHubWithOptions(0)
if hub2.maxConnections != DefaultMaxConnections {
t.Errorf("Expected default maxConnections for 0, got %d", hub2.maxConnections)
}
hub3 := NewEventHubWithOptions(-1)
if hub3.maxConnections != DefaultMaxConnections {
t.Errorf("Expected default maxConnections for -1, got %d", hub3.maxConnections)
}
}
func TestEventHubBroadcast(t *testing.T) {
hub := NewEventHub()
go hub.Run()
defer hub.Stop()
// Create an event
event := Event{
Type: EventMinerStarted,
Timestamp: time.Now(),
Data: MinerEventData{Name: "test-miner"},
}
// Broadcast should not block even with no clients
done := make(chan struct{})
go func() {
hub.Broadcast(event)
close(done)
}()
select {
case <-done:
// Success
case <-time.After(time.Second):
t.Error("Broadcast blocked unexpectedly")
}
}
func TestEventHubClientCount(t *testing.T) {
hub := NewEventHub()
go hub.Run()
defer hub.Stop()
// Initial count should be 0
if count := hub.ClientCount(); count != 0 {
t.Errorf("Expected 0 clients, got %d", count)
}
}
func TestEventHubStop(t *testing.T) {
hub := NewEventHub()
go hub.Run()
// Stop should not panic
defer func() {
if r := recover(); r != nil {
t.Errorf("Stop panicked: %v", r)
}
}()
hub.Stop()
// Give time for cleanup
time.Sleep(50 * time.Millisecond)
}
func TestNewEvent(t *testing.T) {
data := MinerEventData{Name: "test-miner"}
event := NewEvent(EventMinerStarted, data)
if event.Type != EventMinerStarted {
t.Errorf("Expected type %s, got %s", EventMinerStarted, event.Type)
}
if event.Timestamp.IsZero() {
t.Error("Timestamp should not be zero")
}
eventData, ok := event.Data.(MinerEventData)
if !ok {
t.Error("Data should be MinerEventData")
}
if eventData.Name != "test-miner" {
t.Errorf("Expected miner name 'test-miner', got '%s'", eventData.Name)
}
}
func TestEventJSON(t *testing.T) {
event := Event{
Type: EventMinerStats,
Timestamp: time.Now(),
Data: MinerStatsData{
Name: "test-miner",
Hashrate: 1000,
Shares: 10,
Rejected: 1,
Uptime: 3600,
},
}
data, err := json.Marshal(event)
if err != nil {
t.Fatalf("Failed to marshal event: %v", err)
}
var decoded Event
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("Failed to unmarshal event: %v", err)
}
if decoded.Type != EventMinerStats {
t.Errorf("Expected type %s, got %s", EventMinerStats, decoded.Type)
}
}
func TestSetStateProvider(t *testing.T) {
hub := NewEventHub()
go hub.Run()
defer hub.Stop()
called := false
var mu sync.Mutex
provider := func() interface{} {
mu.Lock()
called = true
mu.Unlock()
return map[string]string{"status": "ok"}
}
hub.SetStateProvider(provider)
// The provider should be set but not called until a client connects
mu.Lock()
wasCalled := called
mu.Unlock()
if wasCalled {
t.Error("Provider should not be called until client connects")
}
}
// MockWebSocketConn provides a minimal mock for testing
type MockWebSocketConn struct {
websocket.Conn
written [][]byte
mu sync.Mutex
}
func TestEventTypes(t *testing.T) {
types := []EventType{
EventMinerStarting,
EventMinerStarted,
EventMinerStopping,
EventMinerStopped,
EventMinerStats,
EventMinerError,
EventMinerConnected,
EventPong,
EventStateSync,
}
for _, et := range types {
if et == "" {
t.Error("Event type should not be empty")
}
}
}