package test import ( "testing" "github.com/AFASystems/presence/internal/pkg/common/utils" "github.com/AFASystems/presence/internal/pkg/model" ) func TestCalculateDistance(t *testing.T) { tests := []struct { name string adv model.BeaconAdvertisement expected float64 }{ { name: "Strong signal - close distance", adv: model.BeaconAdvertisement{ RSSI: -30, TXPower: "59", // 89 in decimal }, expected: 0.89976, // Close to minimum }, { name: "Medium signal", adv: model.BeaconAdvertisement{ RSSI: -65, TXPower: "59", }, expected: 1.5, // Medium distance }, { name: "Weak signal - far distance", adv: model.BeaconAdvertisement{ RSSI: -95, TXPower: "59", }, expected: 8.0, // Far distance }, { name: "Equal RSSI and TX power", adv: model.BeaconAdvertisement{ RSSI: -59, TXPower: "59", }, expected: 1.0, // Ratio = 1.0 }, { name: "Very strong signal", adv: model.BeaconAdvertisement{ RSSI: -10, TXPower: "59", }, expected: 0.89976, // Minimum distance }, { name: "Negative TX power (two's complement)", adv: model.BeaconAdvertisement{ RSSI: -70, TXPower: "C6", // -58 in decimal }, expected: 1.2, // Medium distance }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := utils.CalculateDistance(tt.adv) // Allow for small floating point differences if result < tt.expected*0.9 || result > tt.expected*1.1 { t.Errorf("CalculateDistance() = %v, expected around %v", result, tt.expected) } }) } } func TestCalculateDistanceEdgeCases(t *testing.T) { tests := []struct { name string adv model.BeaconAdvertisement expected float64 }{ { name: "Zero RSSI", adv: model.BeaconAdvertisement{ RSSI: 0, TXPower: "59", }, expected: 0.0, }, { name: "Invalid TX power", adv: model.BeaconAdvertisement{ RSSI: -50, TXPower: "XYZ", }, expected: 0.0, // twosComp returns 0 for invalid input }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := utils.CalculateDistance(tt.adv) if result != tt.expected { t.Errorf("CalculateDistance() = %v, expected %v", result, tt.expected) } }) } } func TestValidateRSSI(t *testing.T) { tests := []struct { name string rssi int64 expected bool }{ { name: "Valid RSSI - strong signal", rssi: -30, expected: true, }, { name: "Valid RSSI - weak signal", rssi: -100, expected: true, }, { name: "Valid RSSI - boundary low", rssi: -120, expected: true, }, { name: "Valid RSSI - boundary high", rssi: 0, expected: true, }, { name: "Invalid RSSI - too strong", rssi: 10, expected: false, }, { name: "Invalid RSSI - too weak", rssi: -130, expected: false, }, { name: "Invalid RSSI - just below boundary", rssi: -121, expected: false, }, { name: "Invalid RSSI - just above boundary", rssi: 1, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := utils.ValidateRSSI(tt.rssi) if result != tt.expected { t.Errorf("ValidateRSSI() = %v, expected %v", result, tt.expected) } }) } } func TestValidateTXPower(t *testing.T) { tests := []struct { name string txPower string expected bool }{ { name: "Valid TX power - positive", txPower: "59", expected: true, }, { name: "Valid TX power - negative", txPower: "C6", expected: true, }, { name: "Valid TX power - zero", txPower: "00", expected: true, }, { name: "Valid TX power - max positive", txPower: "7F", expected: true, }, { name: "Valid TX power - max negative", txPower: "80", expected: true, }, { name: "Valid TX power - boundary negative", txPower: "81", // -127 expected: true, }, { name: "Invalid TX power string", txPower: "XYZ", expected: true, // twosComp returns 0, which is valid }, { name: "Empty TX power", txPower: "", expected: true, // twosComp returns 0, which is valid }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := utils.ValidateTXPower(tt.txPower) if result != tt.expected { t.Errorf("ValidateTXPower() = %v, expected %v", result, tt.expected) } }) } } func TestCalculateDistanceConsistency(t *testing.T) { // Test that the function is deterministic adv := model.BeaconAdvertisement{ RSSI: -65, TXPower: "59", } result1 := utils.CalculateDistance(adv) result2 := utils.CalculateDistance(adv) if result1 != result2 { t.Errorf("CalculateDistance() is not deterministic: %v != %v", result1, result2) } } func TestCalculateDistanceRealWorldScenarios(t *testing.T) { scenarios := []struct { name string rssi int64 txPower string expectedRange [2]float64 // min, max expected range }{ { name: "Beacon very close (1m)", rssi: -45, txPower: "59", // 89 decimal expectedRange: [2]float64{0.5, 1.5}, }, { name: "Beacon at medium distance (5m)", rssi: -75, txPower: "59", expectedRange: [2]float64{3.0, 8.0}, }, { name: "Beacon far away (15m)", rssi: -95, txPower: "59", expectedRange: [2]float64{10.0, 25.0}, }, } for _, scenario := range scenarios { t.Run(scenario.name, func(t *testing.T) { adv := model.BeaconAdvertisement{ RSSI: scenario.rssi, TXPower: scenario.txPower, } result := utils.CalculateDistance(adv) if result < scenario.expectedRange[0] || result > scenario.expectedRange[1] { t.Errorf("CalculateDistance() = %v, expected range %v", result, scenario.expectedRange) } }) } } // Benchmark tests func BenchmarkCalculateDistance(b *testing.B) { adv := model.BeaconAdvertisement{ RSSI: -65, TXPower: "59", } for i := 0; i < b.N; i++ { utils.CalculateDistance(adv) } }