package bridge import ( "context" "encoding/json" "testing" "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/model" "github.com/segmentio/kafka-go" ) // MockKafkaWriter is a mock implementation of kafkaWriter for testing type MockKafkaWriter struct { Messages []kafka.Message } func (m *MockKafkaWriter) WriteMessages(ctx context.Context, msgs ...kafka.Message) error { m.Messages = append(m.Messages, msgs...) return nil } func TestMQTTHandler_SingleReading(t *testing.T) { // Setup appState := appcontext.NewAppState() appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1") mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create a test message with single reading reading := model.RawReading{ Timestamp: "2025-01-16T10:00:00Z", Type: "BLE", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: "0201060302A0", } messageBytes, _ := json.Marshal([]model.RawReading{reading}) // Execute mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState) // Assert if len(mockWriter.Messages) != 1 { t.Errorf("Expected 1 message, got %d", len(mockWriter.Messages)) } var adv model.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) } if adv.ID != "test-beacon-1" { t.Errorf("Expected ID 'test-beacon-1', got '%s'", adv.ID) } if adv.Hostname != "gateway-1" { t.Errorf("Expected hostname 'gateway-1', got '%s'", adv.Hostname) } if adv.MAC != "AA:BB:CC:DD:EE:FF" { t.Errorf("Expected MAC 'AA:BB:CC:DD:EE:FF', got '%s'", adv.MAC) } if adv.RSSI != -65 { t.Errorf("Expected RSSI -65, got %d", adv.RSSI) } } func TestMQTTHandler_MultipleReadings(t *testing.T) { // Setup appState := appcontext.NewAppState() appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1") appState.AddBeaconToLookup("11:22:33:44:55:66", "test-beacon-2") mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create a test message with multiple readings readings := []model.RawReading{ { Timestamp: "2025-01-16T10:00:00Z", Type: "BLE", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: "0201060302A0", }, { Timestamp: "2025-01-16T10:00:01Z", Type: "BLE", MAC: "11:22:33:44:55:66", RSSI: -72, RawData: "0201060302A1", }, } messageBytes, _ := json.Marshal(readings) // Execute mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState) // Assert if len(mockWriter.Messages) != 2 { t.Errorf("Expected 2 messages, got %d", len(mockWriter.Messages)) } } func TestMQTTHandler_GatewayTypeSkipped(t *testing.T) { // Setup appState := appcontext.NewAppState() appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1") mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create a test message with Gateway type reading reading := model.RawReading{ Timestamp: "2025-01-16T10:00:00Z", Type: "Gateway", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: "0201060302A0", } messageBytes, _ := json.Marshal([]model.RawReading{reading}) // Execute mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState) // Assert if len(mockWriter.Messages) != 0 { t.Errorf("Expected 0 messages (Gateway type should be skipped), got %d", len(mockWriter.Messages)) } } func TestMQTTHandler_UnknownBeaconSkipped(t *testing.T) { // Setup appState := appcontext.NewAppState() // Don't add beacon to lookup mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create a test message reading := model.RawReading{ Timestamp: "2025-01-16T10:00:00Z", Type: "BLE", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: "0201060302A0", } messageBytes, _ := json.Marshal([]model.RawReading{reading}) // Execute mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState) // Assert if len(mockWriter.Messages) != 0 { t.Errorf("Expected 0 messages (unknown beacon should be skipped), got %d", len(mockWriter.Messages)) } } func TestMQTTHandler_InvalidJSON(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create invalid JSON invalidJSON := []byte("{invalid json") // Execute - should not panic mqtthandler(mockWriter, "publish_out/gateway-1", invalidJSON, appState) // Assert if len(mockWriter.Messages) != 0 { t.Errorf("Expected 0 messages (invalid JSON), got %d", len(mockWriter.Messages)) } } func TestMQTTHandler_HostnameExtraction(t *testing.T) { // Setup appState := appcontext.NewAppState() appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1") mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Test various topic formats testCases := []struct { topic string expectedHost string }{ {"publish_out/gateway-1", "gateway-1"}, {"publish_out/gateway-prod-02", "gateway-prod-02"}, {"publish_out/192-168-1-100", "192-168-1-100"}, } for _, tc := range testCases { t.Run(tc.topic, func(t *testing.T) { mockWriter.Messages = []kafka.Message{} reading := model.RawReading{ Timestamp: "2025-01-16T10:00:00Z", Type: "BLE", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: "0201060302A0", } messageBytes, _ := json.Marshal([]model.RawReading{reading}) mqtthandler(mockWriter, tc.topic, messageBytes, appState) if len(mockWriter.Messages) != 1 { t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages)) } var adv model.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) } if adv.Hostname != tc.expectedHost { t.Errorf("Expected hostname '%s', got '%s'", tc.expectedHost, adv.Hostname) } }) } } func TestMQTTHandler_PreservesRawData(t *testing.T) { // Setup appState := appcontext.NewAppState() appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1") mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} // Create a test message with specific raw data rawData := "1A0201060302A0F4" reading := model.RawReading{ Timestamp: "2025-01-16T10:00:00Z", Type: "BLE", MAC: "AA:BB:CC:DD:EE:FF", RSSI: -65, RawData: rawData, } messageBytes, _ := json.Marshal([]model.RawReading{reading}) // Execute mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState) // Assert if len(mockWriter.Messages) != 1 { t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages)) } var adv model.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) } if adv.Data != rawData { t.Errorf("Expected Data '%s', got '%s'", rawData, adv.Data) } }