package decoder import ( "bytes" "testing" "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/model" "github.com/segmentio/kafka-go" ) func TestDecodeBeacon_EmptyData(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "", // Empty data } // Execute err := decodeBeacon(adv, appState, mockWriter, parserRegistry) // Assert if err != nil { t.Errorf("Expected no error for empty data, got %v", err) } if len(mockWriter.Messages) != 0 { t.Errorf("Expected no messages for empty data, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_WhitespaceOnly(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: " ", // Whitespace only } // Execute err := decodeBeacon(adv, appState, mockWriter, parserRegistry) // Assert if err != nil { t.Errorf("Expected no error for whitespace-only data, got %v", err) } if len(mockWriter.Messages) != 0 { t.Errorf("Expected no messages for whitespace-only data, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_InvalidHex(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "INVALID_HEX_DATA!!!", } // Execute err := decodeBeacon(adv, appState, mockWriter, parserRegistry) // Assert if err == nil { t.Error("Expected error for invalid hex data, got nil") } if len(mockWriter.Messages) != 0 { t.Errorf("Expected no messages for invalid hex, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_ValidHexNoParser(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // No parsers registered // Valid hex but no matching parser adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "0201060302A0", // Valid AD structure } // Execute err := decodeBeacon(adv, appState, mockWriter, parserRegistry) // Assert if err != nil { t.Errorf("Expected no error when no parser matches, got %v", err) } if len(mockWriter.Messages) != 0 { t.Errorf("Expected no messages when no parser matches, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_Deduplication(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Register a test parser config := model.Config{ Name: "test-parser", Prefix: "02", Length: 2, } parserRegistry.Register("test-parser", config) // Create an event that will be parsed adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", // Simple AD structure } // First processing - should publish err := decodeBeacon(adv, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("First processing failed: %v", err) } firstMessageCount := len(mockWriter.Messages) // Second processing with identical data - should deduplicate err = decodeBeacon(adv, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("Second processing failed: %v", err) } // Assert - message count should not have changed if len(mockWriter.Messages) != firstMessageCount { t.Errorf("Expected deduplication, got %d messages (should be %d)", len(mockWriter.Messages), firstMessageCount) } } func TestDecodeBeacon_DifferentDataPublishes(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Register a test parser config := model.Config{ Name: "test-parser", Prefix: "02", Length: 2, } parserRegistry.Register("test-parser", config) // First processing adv1 := model.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } err := decodeBeacon(adv1, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("First processing failed: %v", err) } firstMessageCount := len(mockWriter.Messages) // Second processing with different data - should publish again adv2 := model.BeaconAdvertisement{ ID: "test-beacon", Data: "020107", // Different data } err = decodeBeacon(adv2, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("Second processing failed: %v", err) } // Assert - message count should have increased if len(mockWriter.Messages) != firstMessageCount+1 { t.Errorf("Expected new message for different data, got %d messages (expected %d)", len(mockWriter.Messages), firstMessageCount+1) } } func TestDecodeBeacon_WithFlagBytes(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Register a test parser config := model.Config{ Name: "test-parser", Prefix: "02", Length: 2, } parserRegistry.Register("test-parser", config) // Data with flag bytes (0x01 at position 1) adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "0201060302A0", // Will have flags removed } // Execute err := decodeBeacon(adv, appState, mockWriter, parserRegistry) // Assert - should process successfully after flag removal if err != nil { t.Errorf("Expected no error with flag bytes, got %v", err) } } func TestDecodeBeacon_MultipleBeacons(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Register a test parser config := model.Config{ Name: "test-parser", Prefix: "02", Length: 2, } parserRegistry.Register("test-parser", config) // Process multiple different beacons beacons := []model.BeaconAdvertisement{ {ID: "beacon-1", Data: "020106"}, {ID: "beacon-2", Data: "020107"}, {ID: "beacon-3", Data: "020108"}, } for _, adv := range beacons { err := decodeBeacon(adv, appState, mockWriter, parserRegistry) if err != nil { t.Errorf("Failed to process beacon %s: %v", adv.ID, err) } } // Each unique beacon should produce a message if len(mockWriter.Messages) != len(beacons) { t.Errorf("Expected %d messages, got %d", len(beacons), len(mockWriter.Messages)) } } func TestProcessIncoming_ErrorHandling(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Invalid data that will cause an error adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "INVALID_HEX", } // Execute - should not panic, just handle error processIncoming(adv, appState, mockWriter, parserRegistry) // Assert - no messages should be written if len(mockWriter.Messages) != 0 { t.Errorf("Expected no messages on error, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_EventHashing(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} // Register a test parser that creates consistent events config := model.Config{ Name: "test-parser", Prefix: "02", Length: 2, } parserRegistry.Register("test-parser", config) adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } // First processing err := decodeBeacon(adv, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("First processing failed: %v", err) } // Get the event from appState event, exists := appState.GetBeaconEvent("test-beacon") if !exists { t.Fatal("Event should exist in appState") } // Verify hash is created hash := event.Hash() if hash == nil || len(hash) == 0 { t.Error("Expected non-empty hash") } // Second processing should be deduplicated based on hash err = decodeBeacon(adv, appState, mockWriter, parserRegistry) if err != nil { t.Fatalf("Second processing failed: %v", err) } // Should still have only one message if len(mockWriter.Messages) != 1 { t.Errorf("Expected 1 message after deduplication, got %d", len(mockWriter.Messages)) } } func TestDecodeBeacon_VariousHexFormats(t *testing.T) { // Setup appState := appcontext.NewAppState() mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{} testCases := []struct { name string hexData string shouldError bool }{ {"lowercase hex", "020106aa", false}, {"uppercase hex", "020106AA", false}, {"mixed case", "020106AaFf", false}, {"with spaces", " 020106 ", false}, {"odd length", "02016", true}, {"invalid chars", "020106ZZ", true}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { adv := model.BeaconAdvertisement{ ID: "test-beacon", Data: tc.hexData, } err := decodeBeacon(adv, appState, mockWriter, parserRegistry) if tc.shouldError && err == nil { t.Errorf("Expected error for %s, got nil", tc.name) } if !tc.shouldError && err != nil && !bytes.Contains(err.Error(), []byte("no parser")) { // Error is OK if it's "no parser", but not for hex decoding t.Logf("Got expected error for %s: %v", tc.name, err) } }) } }