✨ Update README with configuration details, add default test configuration, and implement TelegramMapper tests. Enhance message processing to set timestamps and improve error handling in the processor. Refactor NATS consumer configuration and ensure proper handling of message attributes.
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"caatsm/internal/domain"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -42,28 +44,87 @@ func (m *TelegramMapper) ToDBRow(msg *domain.ParsedMessage) ([]interface{}, erro
|
||||
secondaryAddresses := msg.SecondaryAddresses
|
||||
|
||||
return []interface{}{
|
||||
msgUUID, // uuid
|
||||
msg.MessageID, // message_id
|
||||
msg.DateTime, // date_time
|
||||
msg.PriorityIndicator, // priority_indicator
|
||||
msg.PrimaryAddress, // primary_address
|
||||
secondaryAddresses, // secondary_addresses (TEXT)
|
||||
msg.Originator, // originator
|
||||
msg.OriginatorDateTime, // originator_date_time
|
||||
msg.Category, // category
|
||||
msg.Content, // content (TEXT, original message)
|
||||
bodyDataJSON, // body_data (JSONB)
|
||||
msg.ReceivedAt, // received_at
|
||||
msg.ParsedAt, // parsed_at
|
||||
msg.DispatchedAt, // dispatched_at
|
||||
msg.NeedDispatch, // need_dispatch
|
||||
msgUUID, // uuid
|
||||
msg.MessageID, // message_id
|
||||
msg.DateTime, // date_time
|
||||
msg.PriorityIndicator, // priority_indicator
|
||||
msg.PrimaryAddress, // primary_address
|
||||
secondaryAddresses, // secondary_addresses (TEXT)
|
||||
msg.Originator, // originator
|
||||
msg.OriginatorDateTime, // originator_date_time
|
||||
msg.Category, // category
|
||||
msg.Content, // content (TEXT, original message)
|
||||
bodyDataJSON, // body_data (JSONB)
|
||||
msg.ReceivedAt, // received_at
|
||||
msg.ParsedAt, // parsed_at
|
||||
msg.DispatchedAt, // dispatched_at
|
||||
msg.NeedDispatch, // need_dispatch
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FromDBRow converts a database row to a domain.ParsedMessage
|
||||
func (m *TelegramMapper) FromDBRow(row []interface{}) (*domain.ParsedMessage, error) {
|
||||
// This is a placeholder - will be implemented if needed for queries
|
||||
// For now, we only need ToDBRow for inserts
|
||||
return nil, fmt.Errorf("FromDBRow not implemented")
|
||||
}
|
||||
const expectedColumns = 15
|
||||
if len(row) < expectedColumns {
|
||||
return nil, fmt.Errorf("expected %d columns, got %d", expectedColumns, len(row))
|
||||
}
|
||||
|
||||
msgUUID, ok := row[0].(uuid.UUID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("column 0 must be uuid.UUID")
|
||||
}
|
||||
|
||||
toString := func(v interface{}) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
parseTime := func(v interface{}) time.Time {
|
||||
if v == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
if t, ok := v.(time.Time); ok {
|
||||
return t
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
var bodyData interface{}
|
||||
if raw := row[10]; raw != nil {
|
||||
switch val := raw.(type) {
|
||||
case []byte:
|
||||
if len(val) > 0 {
|
||||
if err := json.Unmarshal(val, &bodyData); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal body data: %w", err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
bodyData = val
|
||||
}
|
||||
}
|
||||
|
||||
needDispatch, _ := row[14].(bool)
|
||||
|
||||
return &domain.ParsedMessage{
|
||||
Uuid: msgUUID.String(),
|
||||
MessageID: toString(row[1]),
|
||||
DateTime: toString(row[2]),
|
||||
PriorityIndicator: toString(row[3]),
|
||||
PrimaryAddress: toString(row[4]),
|
||||
SecondaryAddresses: toString(row[5]),
|
||||
Originator: toString(row[6]),
|
||||
OriginatorDateTime: toString(row[7]),
|
||||
Category: toString(row[8]),
|
||||
Content: toString(row[9]),
|
||||
BodyData: bodyData,
|
||||
ReceivedAt: parseTime(row[11]),
|
||||
ParsedAt: parseTime(row[12]),
|
||||
DispatchedAt: parseTime(row[13]),
|
||||
NeedDispatch: needDispatch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package mapper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"caatsm/internal/domain"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestTelegramMapper_ToDBRow_GeneratesUUIDWhenEmpty(t *testing.T) {
|
||||
mapper := NewTelegramMapper()
|
||||
msg := &domain.ParsedMessage{}
|
||||
|
||||
row, err := mapper.ToDBRow(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
value, ok := row[0].(uuid.UUID)
|
||||
if !ok {
|
||||
t.Fatalf("expected first column to be uuid.UUID, got %T", row[0])
|
||||
}
|
||||
if value == uuid.Nil {
|
||||
t.Fatalf("expected generated uuid to be non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTelegramMapper_FromDBRow_RoundTrip(t *testing.T) {
|
||||
mapper := NewTelegramMapper()
|
||||
now := time.Now().UTC()
|
||||
|
||||
original := &domain.ParsedMessage{
|
||||
Uuid: uuid.NewString(),
|
||||
MessageID: "TMQ1324",
|
||||
DateTime: "150631",
|
||||
PriorityIndicator: "FF",
|
||||
PrimaryAddress: "ZBTJZPZX",
|
||||
SecondaryAddresses: "150630 ZBACZQZX",
|
||||
Originator: "ORIGIN",
|
||||
OriginatorDateTime: "150630",
|
||||
Category: "FPL",
|
||||
Content: "raw telegram",
|
||||
BodyData: map[string]string{"key": "value"},
|
||||
ReceivedAt: now,
|
||||
ParsedAt: now,
|
||||
DispatchedAt: now,
|
||||
NeedDispatch: true,
|
||||
}
|
||||
|
||||
row, err := mapper.ToDBRow(original)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
roundTrip, err := mapper.FromDBRow(row)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading row: %v", err)
|
||||
}
|
||||
|
||||
if roundTrip.Uuid != original.Uuid {
|
||||
t.Fatalf("expected uuid %s, got %s", original.Uuid, roundTrip.Uuid)
|
||||
}
|
||||
if roundTrip.MessageID != original.MessageID {
|
||||
t.Fatalf("expected message_id %s, got %s", original.MessageID, roundTrip.MessageID)
|
||||
}
|
||||
if roundTrip.NeedDispatch != original.NeedDispatch {
|
||||
t.Fatalf("expected need_dispatch %v, got %v", original.NeedDispatch, roundTrip.NeedDispatch)
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ import "caatsm/internal/domain"
|
||||
type Parser interface {
|
||||
// Parse parses a raw message string and returns a ParsedMessage
|
||||
Parse(rawText string) *domain.ParsedMessage
|
||||
}
|
||||
|
||||
// TODO: consider returning (*domain.ParsedMessage, error) to surface parse failures explicitly.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user