320 lines
7.8 KiB
Go
320 lines
7.8 KiB
Go
package node
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMessage_NewMessage_Good(t *testing.T) {
|
|
t.Run("BasicMessage", func(t *testing.T) {
|
|
msg, err := NewMessage(MessagePing, "sender-id", "receiver-id", nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create message: %v", err)
|
|
}
|
|
|
|
if msg.Type != MessagePing {
|
|
t.Errorf("expected type MessagePing, got %s", msg.Type)
|
|
}
|
|
|
|
if msg.From != "sender-id" {
|
|
t.Errorf("expected from 'sender-id', got '%s'", msg.From)
|
|
}
|
|
|
|
if msg.To != "receiver-id" {
|
|
t.Errorf("expected to 'receiver-id', got '%s'", msg.To)
|
|
}
|
|
|
|
if msg.ID == "" {
|
|
t.Error("message ID should not be empty")
|
|
}
|
|
|
|
if msg.Timestamp.IsZero() {
|
|
t.Error("timestamp should be set")
|
|
}
|
|
})
|
|
|
|
t.Run("MessageWithPayload", func(t *testing.T) {
|
|
payload := PingPayload{
|
|
SentAt: time.Now().UnixMilli(),
|
|
}
|
|
|
|
msg, err := NewMessage(MessagePing, "sender", "receiver", payload)
|
|
if err != nil {
|
|
t.Fatalf("failed to create message: %v", err)
|
|
}
|
|
|
|
if msg.Payload == nil {
|
|
t.Error("payload should not be nil")
|
|
}
|
|
|
|
var parsed PingPayload
|
|
err = msg.ParsePayload(&parsed)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse payload: %v", err)
|
|
}
|
|
|
|
if parsed.SentAt != payload.SentAt {
|
|
t.Errorf("expected SentAt %d, got %d", payload.SentAt, parsed.SentAt)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMessage_Reply_Good(t *testing.T) {
|
|
original, _ := NewMessage(MessagePing, "sender", "receiver", PingPayload{SentAt: 12345})
|
|
|
|
reply, err := original.Reply(MessagePong, PongPayload{
|
|
SentAt: 12345,
|
|
ReceivedAt: 12350,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create reply: %v", err)
|
|
}
|
|
|
|
if reply.ReplyTo != original.ID {
|
|
t.Errorf("reply should reference original message ID")
|
|
}
|
|
|
|
if reply.From != original.To {
|
|
t.Error("reply From should be original To")
|
|
}
|
|
|
|
if reply.To != original.From {
|
|
t.Error("reply To should be original From")
|
|
}
|
|
|
|
if reply.Type != MessagePong {
|
|
t.Errorf("expected type MessagePong, got %s", reply.Type)
|
|
}
|
|
}
|
|
|
|
func TestMessage_ParsePayload_Good(t *testing.T) {
|
|
t.Run("ValidPayload", func(t *testing.T) {
|
|
payload := StartMinerPayload{
|
|
MinerType: "xmrig",
|
|
ProfileID: "test-profile",
|
|
}
|
|
|
|
msg, _ := NewMessage(MessageStartMiner, "ctrl", "worker", payload)
|
|
|
|
var parsed StartMinerPayload
|
|
err := msg.ParsePayload(&parsed)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse payload: %v", err)
|
|
}
|
|
|
|
if parsed.ProfileID != "test-profile" {
|
|
t.Errorf("expected ProfileID 'test-profile', got '%s'", parsed.ProfileID)
|
|
}
|
|
})
|
|
|
|
t.Run("NilPayload", func(t *testing.T) {
|
|
msg, _ := NewMessage(MessageGetStats, "ctrl", "worker", nil)
|
|
|
|
var parsed StatsPayload
|
|
err := msg.ParsePayload(&parsed)
|
|
if err != nil {
|
|
t.Errorf("parsing nil payload should not error: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("ComplexPayload", func(t *testing.T) {
|
|
stats := StatsPayload{
|
|
NodeID: "node-123",
|
|
NodeName: "Test Node",
|
|
Miners: []MinerStatsItem{
|
|
{
|
|
Name: "xmrig-1",
|
|
Type: "xmrig",
|
|
Hashrate: 1234.56,
|
|
Shares: 100,
|
|
Rejected: 2,
|
|
Uptime: 3600,
|
|
Pool: "pool.example.com:3333",
|
|
Algorithm: "RandomX",
|
|
},
|
|
},
|
|
Uptime: 86400,
|
|
}
|
|
|
|
msg, _ := NewMessage(MessageStats, "worker", "ctrl", stats)
|
|
|
|
var parsed StatsPayload
|
|
err := msg.ParsePayload(&parsed)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse stats payload: %v", err)
|
|
}
|
|
|
|
if parsed.NodeID != "node-123" {
|
|
t.Errorf("expected NodeID 'node-123', got '%s'", parsed.NodeID)
|
|
}
|
|
|
|
if len(parsed.Miners) != 1 {
|
|
t.Fatalf("expected 1 miner, got %d", len(parsed.Miners))
|
|
}
|
|
|
|
if parsed.Miners[0].Hashrate != 1234.56 {
|
|
t.Errorf("expected hashrate 1234.56, got %f", parsed.Miners[0].Hashrate)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMessage_NewErrorMessage_Bad(t *testing.T) {
|
|
errMsg, err := NewErrorMessage("sender", "receiver", ErrorCodeOperationFailed, "something went wrong", "original-msg-id")
|
|
if err != nil {
|
|
t.Fatalf("failed to create error message: %v", err)
|
|
}
|
|
|
|
if errMsg.Type != MessageError {
|
|
t.Errorf("expected type MessageError, got %s", errMsg.Type)
|
|
}
|
|
|
|
if errMsg.ReplyTo != "original-msg-id" {
|
|
t.Errorf("expected ReplyTo 'original-msg-id', got '%s'", errMsg.ReplyTo)
|
|
}
|
|
|
|
var errPayload ErrorPayload
|
|
err = errMsg.ParsePayload(&errPayload)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse error payload: %v", err)
|
|
}
|
|
|
|
if errPayload.Code != ErrorCodeOperationFailed {
|
|
t.Errorf("expected code %d, got %d", ErrorCodeOperationFailed, errPayload.Code)
|
|
}
|
|
|
|
if errPayload.Message != "something went wrong" {
|
|
t.Errorf("expected message 'something went wrong', got '%s'", errPayload.Message)
|
|
}
|
|
}
|
|
|
|
func TestMessage_Serialization_Good(t *testing.T) {
|
|
original, _ := NewMessage(MessageStartMiner, "ctrl", "worker", StartMinerPayload{
|
|
MinerType: "xmrig",
|
|
ProfileID: "my-profile",
|
|
})
|
|
|
|
// Serialize
|
|
data := testJSONMarshal(t, original)
|
|
|
|
// Deserialize
|
|
var restored Message
|
|
testJSONUnmarshal(t, data, &restored)
|
|
|
|
if restored.ID != original.ID {
|
|
t.Error("ID mismatch after serialization")
|
|
}
|
|
|
|
if restored.Type != original.Type {
|
|
t.Error("Type mismatch after serialization")
|
|
}
|
|
|
|
if restored.From != original.From {
|
|
t.Error("From mismatch after serialization")
|
|
}
|
|
|
|
var payload StartMinerPayload
|
|
if err := restored.ParsePayload(&payload); err != nil {
|
|
t.Fatalf("failed to parse restored payload: %v", err)
|
|
}
|
|
|
|
if payload.ProfileID != "my-profile" {
|
|
t.Errorf("expected ProfileID 'my-profile', got '%s'", payload.ProfileID)
|
|
}
|
|
}
|
|
|
|
func TestMessage_Types_Good(t *testing.T) {
|
|
types := []MessageType{
|
|
MessageHandshake,
|
|
MessageHandshakeAck,
|
|
MessagePing,
|
|
MessagePong,
|
|
MessageDisconnect,
|
|
MessageGetStats,
|
|
MessageStats,
|
|
MessageStartMiner,
|
|
MessageStopMiner,
|
|
MessageMinerAck,
|
|
MessageDeploy,
|
|
MessageDeployAck,
|
|
MessageGetLogs,
|
|
MessageLogs,
|
|
MessageError,
|
|
}
|
|
|
|
for _, msgType := range types {
|
|
t.Run(string(msgType), func(t *testing.T) {
|
|
msg, err := NewMessage(msgType, "from", "to", nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create message of type %s: %v", msgType, err)
|
|
}
|
|
|
|
if msg.Type != msgType {
|
|
t.Errorf("expected type %s, got %s", msgType, msg.Type)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMessage_ErrorCodes_Bad(t *testing.T) {
|
|
codes := map[int]string{
|
|
ErrorCodeUnknown: "Unknown",
|
|
ErrorCodeInvalidMessage: "InvalidMessage",
|
|
ErrorCodeUnauthorized: "Unauthorized",
|
|
ErrorCodeNotFound: "NotFound",
|
|
ErrorCodeOperationFailed: "OperationFailed",
|
|
ErrorCodeTimeout: "Timeout",
|
|
}
|
|
|
|
for code, name := range codes {
|
|
t.Run(name, func(t *testing.T) {
|
|
if code < 1000 || code > 1999 {
|
|
t.Errorf("error code %d should be in 1000-1999 range", code)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMessage_NewMessage_NilPayload_Ugly(t *testing.T) {
|
|
msg, err := NewMessage(MessagePing, "from", "to", nil)
|
|
if err != nil {
|
|
t.Fatalf("NewMessage with nil payload should succeed: %v", err)
|
|
}
|
|
if msg.Payload != nil {
|
|
t.Error("payload should be nil for nil input")
|
|
}
|
|
}
|
|
|
|
func TestMessage_ParsePayload_Nil_Ugly(t *testing.T) {
|
|
msg := &Message{Payload: nil}
|
|
var target PingPayload
|
|
err := msg.ParsePayload(&target)
|
|
if err != nil {
|
|
t.Errorf("ParsePayload with nil payload should succeed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMessage_NewErrorMessage_Success_Bad(t *testing.T) {
|
|
msg, err := NewErrorMessage("from", "to", ErrorCodeOperationFailed, "something went wrong", "reply-123")
|
|
if err != nil {
|
|
t.Fatalf("NewErrorMessage failed: %v", err)
|
|
}
|
|
if msg.Type != MessageError {
|
|
t.Errorf("expected type %s, got %s", MessageError, msg.Type)
|
|
}
|
|
if msg.ReplyTo != "reply-123" {
|
|
t.Errorf("expected ReplyTo 'reply-123', got '%s'", msg.ReplyTo)
|
|
}
|
|
|
|
var payload ErrorPayload
|
|
err = msg.ParsePayload(&payload)
|
|
if err != nil {
|
|
t.Fatalf("ParsePayload failed: %v", err)
|
|
}
|
|
if payload.Code != ErrorCodeOperationFailed {
|
|
t.Errorf("expected code %d, got %d", ErrorCodeOperationFailed, payload.Code)
|
|
}
|
|
if payload.Message != "something went wrong" {
|
|
t.Errorf("expected message 'something went wrong', got '%s'", payload.Message)
|
|
}
|
|
}
|