You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

255 lines
6.6 KiB

  1. package bridge
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  6. "github.com/AFASystems/presence/internal/pkg/model"
  7. "github.com/segmentio/kafka-go"
  8. )
  9. func TestMQTTHandler_SingleReading(t *testing.T) {
  10. // Setup
  11. appState := appcontext.NewAppState()
  12. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  13. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  14. // Create a test message with single reading
  15. reading := model.RawReading{
  16. Timestamp: "2025-01-16T10:00:00Z",
  17. Type: "BLE",
  18. MAC: "AA:BB:CC:DD:EE:FF",
  19. RSSI: -65,
  20. RawData: "0201060302A0",
  21. }
  22. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  23. // Execute
  24. mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState)
  25. // Assert
  26. if len(mockWriter.Messages) != 1 {
  27. t.Errorf("Expected 1 message, got %d", len(mockWriter.Messages))
  28. }
  29. var adv model.BeaconAdvertisement
  30. err := json.Unmarshal(mockWriter.Messages[0].Value, &adv)
  31. if err != nil {
  32. t.Fatalf("Failed to unmarshal beacon advertisement: %v", err)
  33. }
  34. if adv.ID != "test-beacon-1" {
  35. t.Errorf("Expected ID 'test-beacon-1', got '%s'", adv.ID)
  36. }
  37. if adv.Hostname != "gateway-1" {
  38. t.Errorf("Expected hostname 'gateway-1', got '%s'", adv.Hostname)
  39. }
  40. if adv.MAC != "AA:BB:CC:DD:EE:FF" {
  41. t.Errorf("Expected MAC 'AA:BB:CC:DD:EE:FF', got '%s'", adv.MAC)
  42. }
  43. if adv.RSSI != -65 {
  44. t.Errorf("Expected RSSI -65, got %d", adv.RSSI)
  45. }
  46. }
  47. func TestMQTTHandler_MultipleReadings(t *testing.T) {
  48. // Setup
  49. appState := appcontext.NewAppState()
  50. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  51. appState.AddBeaconToLookup("11:22:33:44:55:66", "test-beacon-2")
  52. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  53. // Create a test message with multiple readings
  54. readings := []model.RawReading{
  55. {
  56. Timestamp: "2025-01-16T10:00:00Z",
  57. Type: "BLE",
  58. MAC: "AA:BB:CC:DD:EE:FF",
  59. RSSI: -65,
  60. RawData: "0201060302A0",
  61. },
  62. {
  63. Timestamp: "2025-01-16T10:00:01Z",
  64. Type: "BLE",
  65. MAC: "11:22:33:44:55:66",
  66. RSSI: -72,
  67. RawData: "0201060302A1",
  68. },
  69. }
  70. messageBytes, _ := json.Marshal(readings)
  71. // Execute
  72. mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState)
  73. // Assert
  74. if len(mockWriter.Messages) != 2 {
  75. t.Errorf("Expected 2 messages, got %d", len(mockWriter.Messages))
  76. }
  77. }
  78. func TestMQTTHandler_GatewayTypeSkipped(t *testing.T) {
  79. // Setup
  80. appState := appcontext.NewAppState()
  81. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  82. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  83. // Create a test message with Gateway type reading
  84. reading := model.RawReading{
  85. Timestamp: "2025-01-16T10:00:00Z",
  86. Type: "Gateway",
  87. MAC: "AA:BB:CC:DD:EE:FF",
  88. RSSI: -65,
  89. RawData: "0201060302A0",
  90. }
  91. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  92. // Execute
  93. mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState)
  94. // Assert
  95. if len(mockWriter.Messages) != 0 {
  96. t.Errorf("Expected 0 messages (Gateway type should be skipped), got %d", len(mockWriter.Messages))
  97. }
  98. }
  99. func TestMQTTHandler_UnknownBeaconSkipped(t *testing.T) {
  100. // Setup
  101. appState := appcontext.NewAppState()
  102. // Don't add beacon to lookup
  103. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  104. // Create a test message
  105. reading := model.RawReading{
  106. Timestamp: "2025-01-16T10:00:00Z",
  107. Type: "BLE",
  108. MAC: "AA:BB:CC:DD:EE:FF",
  109. RSSI: -65,
  110. RawData: "0201060302A0",
  111. }
  112. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  113. // Execute
  114. mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState)
  115. // Assert
  116. if len(mockWriter.Messages) != 0 {
  117. t.Errorf("Expected 0 messages (unknown beacon should be skipped), got %d", len(mockWriter.Messages))
  118. }
  119. }
  120. func TestMQTTHandler_InvalidJSON(t *testing.T) {
  121. // Setup
  122. appState := appcontext.NewAppState()
  123. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  124. // Create invalid JSON
  125. invalidJSON := []byte("{invalid json")
  126. // Execute - should not panic
  127. mqtthandler(mockWriter, "publish_out/gateway-1", invalidJSON, appState)
  128. // Assert
  129. if len(mockWriter.Messages) != 0 {
  130. t.Errorf("Expected 0 messages (invalid JSON), got %d", len(mockWriter.Messages))
  131. }
  132. }
  133. func TestMQTTHandler_HostnameExtraction(t *testing.T) {
  134. // Setup
  135. appState := appcontext.NewAppState()
  136. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  137. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  138. // Test various topic formats
  139. testCases := []struct {
  140. topic string
  141. expectedHost string
  142. }{
  143. {"publish_out/gateway-1", "gateway-1"},
  144. {"publish_out/gateway-prod-02", "gateway-prod-02"},
  145. {"publish_out/192-168-1-100", "192-168-1-100"},
  146. }
  147. for _, tc := range testCases {
  148. t.Run(tc.topic, func(t *testing.T) {
  149. mockWriter.Messages = []kafka.Message{}
  150. reading := model.RawReading{
  151. Timestamp: "2025-01-16T10:00:00Z",
  152. Type: "BLE",
  153. MAC: "AA:BB:CC:DD:EE:FF",
  154. RSSI: -65,
  155. RawData: "0201060302A0",
  156. }
  157. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  158. mqtthandler(mockWriter, tc.topic, messageBytes, appState)
  159. if len(mockWriter.Messages) != 1 {
  160. t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages))
  161. }
  162. var adv model.BeaconAdvertisement
  163. err := json.Unmarshal(mockWriter.Messages[0].Value, &adv)
  164. if err != nil {
  165. t.Fatalf("Failed to unmarshal beacon advertisement: %v", err)
  166. }
  167. if adv.Hostname != tc.expectedHost {
  168. t.Errorf("Expected hostname '%s', got '%s'", tc.expectedHost, adv.Hostname)
  169. }
  170. })
  171. }
  172. }
  173. func TestMQTTHandler_PreservesRawData(t *testing.T) {
  174. // Setup
  175. appState := appcontext.NewAppState()
  176. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  177. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  178. // Create a test message with specific raw data
  179. rawData := "1A0201060302A0F4"
  180. reading := model.RawReading{
  181. Timestamp: "2025-01-16T10:00:00Z",
  182. Type: "BLE",
  183. MAC: "AA:BB:CC:DD:EE:FF",
  184. RSSI: -65,
  185. RawData: rawData,
  186. }
  187. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  188. // Execute
  189. mqtthandler(mockWriter, "publish_out/gateway-1", messageBytes, appState)
  190. // Assert
  191. if len(mockWriter.Messages) != 1 {
  192. t.Fatalf("Expected 1 message, got %d", len(mockWriter.Messages))
  193. }
  194. var adv model.BeaconAdvertisement
  195. err := json.Unmarshal(mockWriter.Messages[0].Value, &adv)
  196. if err != nil {
  197. t.Fatalf("Failed to unmarshal beacon advertisement: %v", err)
  198. }
  199. if adv.Data != rawData {
  200. t.Errorf("Expected Data '%s', got '%s'", rawData, adv.Data)
  201. }
  202. }