diff --git a/internal/app/server/events.go b/internal/app/server/events.go index b66ec7a..ce11e25 100644 --- a/internal/app/server/events.go +++ b/internal/app/server/events.go @@ -3,6 +3,7 @@ package server import ( "context" "encoding/json" + "fmt" "log/slog" "time" @@ -55,6 +56,10 @@ func RunEventLoop(ctx context.Context, a *ServerApp) { slog.Error("saving decoder event for beacon", "id", id, "err", err) continue } + if msg.Type == "Eddystone" && msg.Battery < 3000 { + fmt.Printf("Sending alert for battery low: %+v\n", msg) + service.SendAlert(id, "BatteryLow", a.KafkaManager.GetWriter("alert"), ctx, a.DB) + } case <-beaconTicker.C: var list []model.Tracker a.DB.Find(&list) diff --git a/internal/pkg/service/beacon_service.go b/internal/pkg/service/beacon_service.go index 7c9ffc5..c1823ce 100644 --- a/internal/pkg/service/beacon_service.go +++ b/internal/pkg/service/beacon_service.go @@ -88,7 +88,7 @@ func LocationToBeaconService(msg model.HTTPLocation, db *gorm.DB, writer *kafka. return } - sendAlert(gw.ID, msg.ID, writer, ctx, allowedZones, db) + sendRestrictedZoneAlert(gw.ID, msg.ID, writer, ctx, allowedZones, db) } func LocationToBeaconServiceAI(msg model.HTTPLocation, db *gorm.DB, writer *kafka.Writer, ctx context.Context) { @@ -126,48 +126,52 @@ func LocationToBeaconServiceAI(msg model.HTTPLocation, db *gorm.DB, writer *kafk return } - sendAlert(gw.ID, tracker.ID, writer, ctx, allowedZones, db) + sendRestrictedZoneAlert(gw.ID, tracker.ID, writer, ctx, allowedZones, db) } -func sendAlert(gwId, trackerId string, writer *kafka.Writer, ctx context.Context, allowedZones []string, db *gorm.DB) { - if len(allowedZones) != 0 && !slices.Contains(allowedZones, gwId) { - var existingAlert model.Alert - result := db.Select("status").Where("tracker_id = ? AND type = ?", trackerId, "Restricted zone").Order("timestamp DESC").First(&existingAlert) - - if result.Error == gorm.ErrRecordNotFound || existingAlert.Status == "resolved" { - alert := model.Alert{ - ID: uuid.New().String(), - TrackerID: trackerId, - Type: "Restricted zone", - Status: "new", - Timestamp: time.Now(), - } +func SendAlert(trackerId, alertType string, writer *kafka.Writer, ctx context.Context, db *gorm.DB) { + var existingAlert model.Alert + result := db.Select("status").Where("tracker_id = ? AND type = ?", trackerId, alertType).Order("timestamp DESC").First(&existingAlert) + + if result.Error == gorm.ErrRecordNotFound || existingAlert.Status == "resolved" { + alert := model.Alert{ + ID: uuid.New().String(), + TrackerID: trackerId, + Type: alertType, + Status: "new", + Timestamp: time.Now(), + } - if err := InsertAlert(alert, db, ctx); err != nil { - msg := fmt.Sprintf("Error in inserting alert: %v", err) - slog.Error(msg) - return - } + if err := InsertAlert(alert, db, ctx); err != nil { + msg := fmt.Sprintf("Error in inserting alert: %v", err) + slog.Error(msg) + return + } - eMsg, err := json.Marshal(alert) - if err != nil { - msg := "Error in marshaling" + eMsg, err := json.Marshal(alert) + if err != nil { + msg := "Error in marshaling" + slog.Error(msg) + return + } else { + msg := kafka.Message{ + Value: eMsg, + } + if err := kafkaclient.Write(ctx, writer, msg); err != nil { + msg := fmt.Sprintf("Error in writing message: %v", err) slog.Error(msg) return - } else { - msg := kafka.Message{ - Value: eMsg, - } - if err := kafkaclient.Write(ctx, writer, msg); err != nil { - msg := fmt.Sprintf("Error in writing message: %v", err) - slog.Error(msg) - return - } } - return - } else { - return } + return + } else { + return + } +} + +func sendRestrictedZoneAlert(gwId, trackerId string, writer *kafka.Writer, ctx context.Context, allowedZones []string, db *gorm.DB) { + if len(allowedZones) != 0 && !slices.Contains(allowedZones, gwId) { + SendAlert(trackerId, "Restricted zone", writer, ctx, db) } } diff --git a/location b/location index 074ee91..685023e 100755 Binary files a/location and b/location differ diff --git a/server b/server index f0241d7..ca2c228 100755 Binary files a/server and b/server differ diff --git a/tests/appcontext/appcontext_test.go b/tests/appcontext/appcontext_test.go index 86fa9df..b587289 100644 --- a/tests/appcontext/appcontext_test.go +++ b/tests/appcontext/appcontext_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/AFASystems/presence/internal/pkg/common/appcontext" - "github.com/AFASystems/presence/internal/pkg/model" ) func TestNewAppState(t *testing.T) { @@ -64,7 +63,7 @@ func TestBeaconLookup_CleanLookup(t *testing.T) { func TestBeacon_GetAndUpdate(t *testing.T) { state := appcontext.NewAppState() - beacon := model.Beacon{ + beacon := appcontext.Beacon{ ID: "test-beacon", Name: "Test", } @@ -81,7 +80,7 @@ func TestBeacon_GetAndUpdate(t *testing.T) { func TestBeaconEvent_GetAndUpdate(t *testing.T) { state := appcontext.NewAppState() - event := model.BeaconEvent{ + event := appcontext.BeaconEvent{ ID: "beacon-1", Type: "iBeacon", Battery: 85, @@ -99,8 +98,8 @@ func TestBeaconEvent_GetAndUpdate(t *testing.T) { func TestGetAllBeacons(t *testing.T) { state := appcontext.NewAppState() - state.UpdateBeacon("b1", model.Beacon{ID: "b1"}) - state.UpdateBeacon("b2", model.Beacon{ID: "b2"}) + state.UpdateBeacon("b1", appcontext.Beacon{ID: "b1"}) + state.UpdateBeacon("b2", appcontext.Beacon{ID: "b2"}) all := state.GetAllBeacons() if len(all) != 2 { @@ -111,7 +110,7 @@ func TestGetAllBeacons(t *testing.T) { func TestUpdateSettings(t *testing.T) { state := appcontext.NewAppState() state.UpdateSettings(map[string]any{ - "current_algorithm": "ai", + "current_algorithm": "ai", "location_confidence": int64(5), }) diff --git a/tests/bridge/bridge_test.go b/tests/bridge/bridge_test.go index cff2d0e..ccabe44 100644 --- a/tests/bridge/bridge_test.go +++ b/tests/bridge/bridge_test.go @@ -9,8 +9,8 @@ import ( "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/model" - "github.com/segmentio/kafka-go" mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/segmentio/kafka-go" ) // mqtthandler is extracted from main.go for testing purposes @@ -36,7 +36,7 @@ func mqtthandler(writer kafkaWriter, topic string, message []byte, appState *app continue } - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: val, Hostname: hostname, MAC: reading.MAC, diff --git a/tests/bridge/event_loop_test.go b/tests/bridge/event_loop_test.go index 8f01e2a..9225079 100644 --- a/tests/bridge/event_loop_test.go +++ b/tests/bridge/event_loop_test.go @@ -180,9 +180,11 @@ func TestEventLoop_AlertMessage(t *testing.T) { // Create an alert message msg := model.Alert{ - ID: "tracker-123", - Type: "battery_low", - Value: "15", + ID: "tracker-123", + Type: "battery_low", + Status: "new", + Timestamp: time.Now(), + TrackerID: "tracker-123", } go func() { diff --git a/tests/bridge/integration_test.go b/tests/bridge/integration_test.go index 4a9a070..b2c1c80 100644 --- a/tests/bridge/integration_test.go +++ b/tests/bridge/integration_test.go @@ -75,7 +75,7 @@ func TestIntegration_EndToEnd(t *testing.T) { } // Verify - var adv model.BeaconAdvertisement + var adv appcontext.BeaconAdvertisement err = json.Unmarshal(msg.Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) @@ -155,7 +155,7 @@ func TestIntegration_MultipleMessages(t *testing.T) { t.Fatalf("Failed to read message %d from Kafka: %v", i+1, err) } - var adv model.BeaconAdvertisement + var adv appcontext.BeaconAdvertisement err = json.Unmarshal(msg.Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement %d: %v", i+1, err) diff --git a/tests/bridge/mqtt_handler_test.go b/tests/bridge/mqtt_handler_test.go index bae7f9a..70c7de6 100644 --- a/tests/bridge/mqtt_handler_test.go +++ b/tests/bridge/mqtt_handler_test.go @@ -35,7 +35,7 @@ func TestMQTTHandler_SingleReading(t *testing.T) { t.Errorf("Expected 1 message, got %d", len(mockWriter.Messages)) } - var adv model.BeaconAdvertisement + var adv appcontext.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) @@ -175,8 +175,8 @@ func TestMQTTHandler_HostnameExtraction(t *testing.T) { // Test various topic formats testCases := []struct { - topic string - expectedHost string + topic string + expectedHost string }{ {"publish_out/gateway-1", "gateway-1"}, {"publish_out/gateway-prod-02", "gateway-prod-02"}, @@ -202,7 +202,7 @@ func TestMQTTHandler_HostnameExtraction(t *testing.T) { t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages)) } - var adv model.BeaconAdvertisement + var adv appcontext.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) @@ -242,7 +242,7 @@ func TestMQTTHandler_PreservesRawData(t *testing.T) { t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages)) } - var adv model.BeaconAdvertisement + var adv appcontext.BeaconAdvertisement err := json.Unmarshal(mockWriter.Messages[0].Value, &adv) if err != nil { t.Fatalf("Failed to unmarshal beacon advertisement: %v", err) diff --git a/tests/bridge/testutil.go b/tests/bridge/testutil.go index 713a694..9d4dd0b 100644 --- a/tests/bridge/testutil.go +++ b/tests/bridge/testutil.go @@ -94,7 +94,7 @@ func (th *TestHelper) CreateMQTTMessage(topic string, readings []model.RawReadin } // AssertBeaconAdvertisement asserts that a beacon advertisement matches expected values -func (th *TestHelper) AssertBeaconAdvertisement(adv model.BeaconAdvertisement, expectedID, expectedHostname, expectedMAC string, expectedRSSI int64) { +func (th *TestHelper) AssertBeaconAdvertisement(adv appcontext.BeaconAdvertisement, expectedID, expectedHostname, expectedMAC string, expectedRSSI int64) { if adv.ID != expectedID { th.t.Errorf("Expected ID '%s', got '%s'", expectedID, adv.ID) } diff --git a/tests/controller/controller_test.go b/tests/controller/controller_test.go index 0dee0d1..ae61c60 100644 --- a/tests/controller/controller_test.go +++ b/tests/controller/controller_test.go @@ -2,11 +2,13 @@ package controller import ( "bytes" + "context" "encoding/json" "net/http" "net/http/httptest" "testing" + "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/controller" "github.com/AFASystems/presence/internal/pkg/model" "github.com/gorilla/mux" @@ -19,7 +21,7 @@ func setupTestDB(t *testing.T) *gorm.DB { if err != nil { t.Fatalf("Failed to open test DB: %v", err) } - if err := db.AutoMigrate(&model.Gateway{}, &model.Zone{}, &model.Tracker{}, &model.TrackerZones{}, &model.Config{}, &model.Settings{}, &model.Tracks{}); err != nil { + if err := db.AutoMigrate(&model.Gateway{}, &model.Zone{}, &model.Tracker{}, &model.TrackerZones{}, &model.Config{}, appcontext.Settings{}, &model.Tracks{}); err != nil { t.Fatalf("Failed to migrate: %v", err) } return db @@ -27,7 +29,8 @@ func setupTestDB(t *testing.T) *gorm.DB { func TestGatewayListController_Empty(t *testing.T) { db := setupTestDB(t) - handler := controller.GatewayListController(db) + context := context.Background() + handler := controller.GatewayListController(db, context) req := httptest.NewRequest(http.MethodGet, "/gateways", nil) rec := httptest.NewRecorder() @@ -47,7 +50,8 @@ func TestGatewayListController_Empty(t *testing.T) { func TestGatewayAddController(t *testing.T) { db := setupTestDB(t) - handler := controller.GatewayAddController(db) + context := context.Background() + handler := controller.GatewayAddController(db, context) gateway := model.Gateway{ ID: "gw-1", @@ -77,11 +81,11 @@ func TestGatewayAddController(t *testing.T) { func TestGatewayDeleteController(t *testing.T) { db := setupTestDB(t) db.Create(&model.Gateway{ID: "gw-1", Name: "G1", MAC: "AA:BB:CC:DD:EE:FF"}) - + context := context.Background() req := httptest.NewRequest(http.MethodDelete, "/gateways/gw-1", nil) req = mux.SetURLVars(req, map[string]string{"id": "gw-1"}) rec := httptest.NewRecorder() - controller.GatewayDeleteController(db).ServeHTTP(rec, req) + controller.GatewayDeleteController(db, context).ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("Expected 200, got %d", rec.Code) @@ -96,7 +100,8 @@ func TestGatewayDeleteController(t *testing.T) { func TestTrackerListController_Empty(t *testing.T) { db := setupTestDB(t) - handler := controller.TrackerList(db) + context := context.Background() + handler := controller.TrackerList(db, context) req := httptest.NewRequest(http.MethodGet, "/trackers", nil) rec := httptest.NewRecorder() @@ -116,7 +121,8 @@ func TestTrackerListController_Empty(t *testing.T) { func TestZoneListController_Empty(t *testing.T) { db := setupTestDB(t) - handler := controller.ZoneListController(db) + context := context.Background() + handler := controller.ZoneListController(db, context) req := httptest.NewRequest(http.MethodGet, "/zones", nil) rec := httptest.NewRecorder() @@ -129,7 +135,8 @@ func TestZoneListController_Empty(t *testing.T) { func TestSettingsListController(t *testing.T) { db := setupTestDB(t) - handler := controller.SettingsListController(db) + context := context.Background() + handler := controller.SettingsListController(db, context) req := httptest.NewRequest(http.MethodGet, "/settings", nil) rec := httptest.NewRecorder() diff --git a/tests/decoder/decode_test.go b/tests/decoder/decode_test.go index e76cd22..789e7e0 100644 --- a/tests/decoder/decode_test.go +++ b/tests/decoder/decode_test.go @@ -15,7 +15,7 @@ func TestDecodeBeacon_EmptyData(t *testing.T) { mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "", // Empty data } @@ -39,7 +39,7 @@ func TestDecodeBeacon_WhitespaceOnly(t *testing.T) { mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: " ", // Whitespace only } @@ -63,7 +63,7 @@ func TestDecodeBeacon_InvalidHex(t *testing.T) { mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "INVALID_HEX_DATA!!!", } @@ -88,7 +88,7 @@ func TestDecodeBeacon_ValidHexNoParser(t *testing.T) { parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} // No parsers registered // Valid hex but no matching parser (03 02 FF 06 - type 0x02, no parser registered for it) - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "0302FF06", // Valid AD structure } @@ -126,7 +126,7 @@ func TestDecodeBeacon_Deduplication(t *testing.T) { parserRegistry.Register("test-parser", config) // Create an event that will be parsed (03 02 FF 06 - not removed by RemoveFlagBytes) - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "0302FF06", } @@ -171,7 +171,7 @@ func TestDecodeBeacon_DifferentDataPublishes(t *testing.T) { parserRegistry.Register("test-parser", config) // First processing - adv1 := model.BeaconAdvertisement{ + adv1 := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } @@ -184,7 +184,7 @@ func TestDecodeBeacon_DifferentDataPublishes(t *testing.T) { firstMessageCount := len(mockWriter.Messages) // Second processing with different data - should publish again - adv2 := model.BeaconAdvertisement{ + adv2 := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "0302FF07", // Different data } @@ -220,7 +220,7 @@ func TestDecodeBeacon_WithFlagBytes(t *testing.T) { parserRegistry.Register("test-parser", config) // Data with flag bytes first (02 01 06), then our structure (03 02 FF 08) - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "0201060302FF08", // Flags removed, then 03 02 FF 08 remains } @@ -254,7 +254,7 @@ func TestDecodeBeacon_MultipleBeacons(t *testing.T) { parserRegistry.Register("test-parser", config) // Process multiple different beacons (03 02 FF xx - not removed by RemoveFlagBytes) - beacons := []model.BeaconAdvertisement{ + beacons := []appcontext.BeaconAdvertisement{ {ID: "beacon-1", Data: "0302FF06"}, {ID: "beacon-2", Data: "0302FF07"}, {ID: "beacon-3", Data: "0302FF08"}, @@ -280,7 +280,7 @@ func TestProcessIncoming_ErrorHandling(t *testing.T) { parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} // Invalid data that will cause an error - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "INVALID_HEX", } @@ -313,7 +313,7 @@ func TestDecodeBeacon_EventHashing(t *testing.T) { } parserRegistry.Register("test-parser", config) - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "0302FF06", } @@ -369,7 +369,7 @@ func TestDecodeBeacon_VariousHexFormats(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: tc.hexData, } diff --git a/tests/decoder/decoder_test.go b/tests/decoder/decoder_test.go index 37d5b0f..3243188 100644 --- a/tests/decoder/decoder_test.go +++ b/tests/decoder/decoder_test.go @@ -14,7 +14,7 @@ import ( ) // processIncoming processes incoming beacon advertisements -func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer kafkaWriter, parserRegistry *model.ParserRegistry) { +func processIncoming(adv appcontext.BeaconAdvertisement, appState *appcontext.AppState, writer kafkaWriter, parserRegistry *model.ParserRegistry) { err := decodeBeacon(adv, appState, writer, parserRegistry) if err != nil { eMsg := fmt.Sprintf("Error in decoding: %v", err) @@ -24,7 +24,7 @@ func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppStat } // decodeBeacon decodes beacon data and publishes events -func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer kafkaWriter, parserRegistry *model.ParserRegistry) error { +func decodeBeacon(adv appcontext.BeaconAdvertisement, appState *appcontext.AppState, writer kafkaWriter, parserRegistry *model.ParserRegistry) error { beacon := strings.TrimSpace(adv.Data) id := adv.ID if beacon == "" { @@ -39,7 +39,7 @@ func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, b = utils.RemoveFlagBytes(b) indeces := utils.ParseADFast(b) - event := utils.LoopADStructures(b, indeces, id, parserRegistry) + event := utils.LoopADStructures(b, indeces, id, parserRegistry, "") if event.ID == "" { return nil diff --git a/tests/decoder/event_loop_test.go b/tests/decoder/event_loop_test.go index fdaa12d..bae1a8a 100644 --- a/tests/decoder/event_loop_test.go +++ b/tests/decoder/event_loop_test.go @@ -16,12 +16,12 @@ func TestEventLoop_RawMessageProcessing(t *testing.T) { mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}} parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} - chRaw := make(chan model.BeaconAdvertisement, 10) + chRaw := make(chan appcontext.BeaconAdvertisement, 10) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Create a test message - msg := model.BeaconAdvertisement{ + msg := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } @@ -242,7 +242,7 @@ func TestEventLoop_ContextCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - chRaw := make(chan model.BeaconAdvertisement, 10) + chRaw := make(chan appcontext.BeaconAdvertisement, 10) chParser := make(chan model.KafkaParser, 10) // Cancel immediately @@ -264,7 +264,7 @@ func TestEventLoop_ContextCancellation(t *testing.T) { func TestEventLoop_ChannelBuffering(t *testing.T) { // Setup - create buffered channels (like in main) - chRaw := make(chan model.BeaconAdvertisement, 2000) + chRaw := make(chan appcontext.BeaconAdvertisement, 2000) chParser := make(chan model.KafkaParser, 200) _, cancel := context.WithCancel(context.Background()) @@ -272,7 +272,7 @@ func TestEventLoop_ChannelBuffering(t *testing.T) { // Send multiple messages without blocking for i := 0; i < 100; i++ { - msg := model.BeaconAdvertisement{ + msg := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } @@ -310,14 +310,14 @@ func TestEventLoop_ChannelBuffering(t *testing.T) { func TestEventLoop_ParserAndRawChannels(t *testing.T) { // Setup - chRaw := make(chan model.BeaconAdvertisement, 10) + chRaw := make(chan appcontext.BeaconAdvertisement, 10) chParser := make(chan model.KafkaParser, 10) _, cancel := context.WithCancel(context.Background()) defer cancel() // Send both raw and parser messages - rawMsg := model.BeaconAdvertisement{ + rawMsg := appcontext.BeaconAdvertisement{ ID: "test-beacon", Data: "020106", } diff --git a/tests/decoder/integration_test.go b/tests/decoder/integration_test.go index de2fc72..d294aca 100644 --- a/tests/decoder/integration_test.go +++ b/tests/decoder/integration_test.go @@ -56,7 +56,7 @@ func TestIntegration_DecoderEndToEnd(t *testing.T) { defer reader.Close() // Create a test beacon advertisement - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "integration-test-beacon", Data: "020106", // Valid hex data } @@ -104,7 +104,7 @@ func TestIntegration_ParserRegistryOperations(t *testing.T) { ID: "add", Name: "kafka-test-parser", Config: model.Config{ - Name: "kafka-test-parser", + Name: "kafka-test-parser", Min: 2, Max: 20, Pattern: []string{"0x02", "0x01"}, @@ -177,7 +177,7 @@ func TestIntegration_MultipleBeaconsSequential(t *testing.T) { defer reader.Close() // Process multiple beacons - beacons := []model.BeaconAdvertisement{ + beacons := []appcontext.BeaconAdvertisement{ {ID: "beacon-1", Data: "020106"}, {ID: "beacon-2", Data: "020107"}, {ID: "beacon-3", Data: "020108"}, @@ -243,7 +243,7 @@ func TestIntegration_EventDeduplication(t *testing.T) { defer reader.Close() // Create identical beacon advertisement - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "dedup-test-beacon", Data: "020106", } @@ -319,7 +319,7 @@ func TestIntegration_AppStatePersistence(t *testing.T) { defer writer.Close() // Process beacon - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "persist-test-beacon", Data: "020106", } @@ -381,7 +381,7 @@ func TestIntegration_ParserUpdateFlow(t *testing.T) { parserRegistry.Register("update-test-parser", config1) // Process with initial config - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ ID: "update-test-beacon", Data: "020106", } @@ -391,7 +391,7 @@ func TestIntegration_ParserUpdateFlow(t *testing.T) { // Update parser config config2 := model.Config{ - Name: "update-test-parser", + Name: "update-test-parser", Min: 3, Max: 25, Pattern: []string{"0x03"}, @@ -400,7 +400,7 @@ func TestIntegration_ParserUpdateFlow(t *testing.T) { parserRegistry.Register("update-test-parser", config2) // Process again with updated config - adv2 := model.BeaconAdvertisement{ + adv2 := appcontext.BeaconAdvertisement{ ID: "update-test-beacon-2", Data: "030107", } diff --git a/tests/decoder/testutil.go b/tests/decoder/testutil.go index ce977a1..12d9b2e 100644 --- a/tests/decoder/testutil.go +++ b/tests/decoder/testutil.go @@ -60,32 +60,32 @@ func (th *TestHelper) RegisterTestParser(name string) { } // CreateBeaconAdvertisement creates a test beacon advertisement -func (th *TestHelper) CreateBeaconAdvertisement(id, data string) model.BeaconAdvertisement { - return model.BeaconAdvertisement{ +func (th *TestHelper) CreateBeaconAdvertisement(id, data string) appcontext.BeaconAdvertisement { + return appcontext.BeaconAdvertisement{ ID: id, Data: data, } } // CreateValidHexAdvertisement creates a beacon with valid hex data -func (th *TestHelper) CreateValidHexAdvertisement(id string) model.BeaconAdvertisement { - return model.BeaconAdvertisement{ +func (th *TestHelper) CreateValidHexAdvertisement(id string) appcontext.BeaconAdvertisement { + return appcontext.BeaconAdvertisement{ ID: id, Data: "020106", } } // CreateInvalidHexAdvertisement creates a beacon with invalid hex data -func (th *TestHelper) CreateInvalidHexAdvertisement(id string) model.BeaconAdvertisement { - return model.BeaconAdvertisement{ +func (th *TestHelper) CreateInvalidHexAdvertisement(id string) appcontext.BeaconAdvertisement { + return appcontext.BeaconAdvertisement{ ID: id, Data: "INVALID_HEX", } } // CreateEmptyAdvertisement creates a beacon with empty data -func (th *TestHelper) CreateEmptyAdvertisement(id string) model.BeaconAdvertisement { - return model.BeaconAdvertisement{ +func (th *TestHelper) CreateEmptyAdvertisement(id string) appcontext.BeaconAdvertisement { + return appcontext.BeaconAdvertisement{ ID: id, Data: "", } @@ -106,11 +106,11 @@ func (th *TestHelper) AssertParserNotExists(name string) { } // AssertEventExists asserts that an event exists in appState -func (th *TestHelper) AssertEventExists(id string) model.BeaconEvent { +func (th *TestHelper) AssertEventExists(id string) appcontext.BeaconEvent { event, exists := th.appState.GetBeaconEvent(id) if !exists { th.t.Errorf("Event for beacon '%s' should exist in appState", id) - return model.BeaconEvent{} + return appcontext.BeaconEvent{} } return event } @@ -172,10 +172,10 @@ func AssertError(t *testing.T, err error, msg string) { // Valid hex strings for testing var ValidHexStrings = []string{ - "020106", // Simple AD structure - "0201060302A0", // AD structure with flags - "1AFF0C01", // iBeacon-like data - "0201061AFF0C01", // Multiple AD structures + "020106", // Simple AD structure + "0201060302A0", // AD structure with flags + "1AFF0C01", // iBeacon-like data + "0201061AFF0C01", // Multiple AD structures } // Invalid hex strings for testing @@ -201,7 +201,7 @@ func CreateMockWriter() *MockKafkaWriter { // Beacon event test helpers // AssertEventFields asserts that event fields match expected values -func AssertEventFields(t *testing.T, event model.BeaconEvent, expectedID, expectedType string) { +func AssertEventFields(t *testing.T, event appcontext.BeaconEvent, expectedID, expectedType string) { if event.ID != expectedID { t.Errorf("Expected event ID '%s', got '%s'", expectedID, event.ID) } @@ -232,8 +232,8 @@ func CleanupTestParsers(registry *model.ParserRegistry) { } // CreateTestBeaconEvent creates a test beacon event -func CreateTestBeaconEvent(id, eventType string) model.BeaconEvent { - return model.BeaconEvent{ +func CreateTestBeaconEvent(id, eventType string) appcontext.BeaconEvent { + return appcontext.BeaconEvent{ ID: id, Type: eventType, Battery: 100, diff --git a/tests/location/location_test.go b/tests/location/location_test.go index 07527ca..1b2b137 100644 --- a/tests/location/location_test.go +++ b/tests/location/location_test.go @@ -3,8 +3,8 @@ package location import ( "testing" + "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/common/utils" - "github.com/AFASystems/presence/internal/pkg/model" ) // Test location algorithm scoring formula: seenW + (rssiW * (1.0 - (rssi / -100.0))) @@ -30,7 +30,7 @@ func TestLocationScoringFormula(t *testing.T) { } func TestCalculateDistance_ForLocation(t *testing.T) { - adv := model.BeaconAdvertisement{ + adv := appcontext.BeaconAdvertisement{ RSSI: -65, TXPower: "C5", } diff --git a/tests/model/model_test.go b/tests/model/model_test.go index 6e2c6b8..9a30d10 100644 --- a/tests/model/model_test.go +++ b/tests/model/model_test.go @@ -4,11 +4,12 @@ import ( "encoding/json" "testing" + "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/model" ) func TestBeaconEvent_Hash(t *testing.T) { - e := model.BeaconEvent{ + e := appcontext.BeaconEvent{ ID: "beacon-1", Name: "beacon-1", Type: "iBeacon", @@ -28,8 +29,8 @@ func TestBeaconEvent_Hash(t *testing.T) { } func TestBeaconEvent_Hash_BatteryRounded(t *testing.T) { - e1 := model.BeaconEvent{ID: "1", Battery: 84, Event: 1} - e2 := model.BeaconEvent{ID: "1", Battery: 89, Event: 1} + e1 := appcontext.BeaconEvent{ID: "1", Battery: 84, Event: 1} + e2 := appcontext.BeaconEvent{ID: "1", Battery: 89, Event: 1} hash1 := e1.Hash() hash2 := e2.Hash() // Battery is rounded to nearest 10, so 84 and 89 should produce same hash @@ -39,7 +40,7 @@ func TestBeaconEvent_Hash_BatteryRounded(t *testing.T) { } func TestBeaconEvent_ToJSON(t *testing.T) { - e := model.BeaconEvent{ + e := appcontext.BeaconEvent{ ID: "beacon-1", Name: "Test", Type: "iBeacon", @@ -49,7 +50,7 @@ func TestBeaconEvent_ToJSON(t *testing.T) { if err != nil { t.Fatalf("ToJSON failed: %v", err) } - var decoded model.BeaconEvent + var decoded appcontext.BeaconEvent if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("Failed to unmarshal: %v", err) } diff --git a/tests/utils/utils_test.go b/tests/utils/utils_test.go index 97e2843..c87cb66 100644 --- a/tests/utils/utils_test.go +++ b/tests/utils/utils_test.go @@ -3,6 +3,7 @@ package utils import ( "testing" + "github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/common/utils" "github.com/AFASystems/presence/internal/pkg/model" ) @@ -80,17 +81,17 @@ func TestRemoveFlagBytes_TooShort(t *testing.T) { func TestCalculateDistance(t *testing.T) { tests := []struct { - name string - rssi int64 + name string + rssi int64 txPower string }{ - {"typical beacon", -65, "C5"}, // -59 in two's complement + {"typical beacon", -65, "C5"}, // -59 in two's complement {"weak signal", -90, "C5"}, {"strong signal", -40, "C5"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - adv := model.BeaconAdvertisement{RSSI: tt.rssi, TXPower: tt.txPower} + adv := appcontext.BeaconAdvertisement{RSSI: tt.rssi, TXPower: tt.txPower} d := utils.CalculateDistance(adv) if d < 0 { t.Errorf("Distance should be non-negative, got %f", d) @@ -103,7 +104,7 @@ func TestLoopADStructures_NoParsers(t *testing.T) { registry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} data := []byte{0x02, 0x01, 0x06} indices := utils.ParseADFast(data) - event := utils.LoopADStructures(data, indices, "beacon-1", registry) + event := utils.LoopADStructures(data, indices, "beacon-1", registry, "") if event.ID != "" { t.Errorf("Expected empty event with no parsers, got ID %s", event.ID) }