Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

269 řádky
6.5 KiB

  1. package bridge
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "testing"
  8. "time"
  9. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  10. "github.com/AFASystems/presence/internal/pkg/model"
  11. "github.com/segmentio/kafka-go"
  12. )
  13. // TestIntegration_EndToEnd tests the complete flow from MQTT message to Kafka
  14. // This test requires real Kafka and doesn't mock the writer
  15. func TestIntegration_EndToEnd(t *testing.T) {
  16. if testing.Short() {
  17. t.Skip("Skipping integration test in short mode")
  18. }
  19. // Check if Kafka is available
  20. kafkaURL := os.Getenv("KAFKA_URL")
  21. if kafkaURL == "" {
  22. kafkaURL = "localhost:9092"
  23. }
  24. // Create a test topic
  25. testTopic := "test-rawbeacons-" + time.Now().Format("20060102150405")
  26. // Setup
  27. appState := appcontext.NewAppState()
  28. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  29. // Create real Kafka writer
  30. writer := kafka.NewWriter(kafka.WriterConfig{
  31. Brokers: []string{kafkaURL},
  32. Topic: testTopic,
  33. })
  34. defer writer.Close()
  35. // Create Kafka reader to verify messages
  36. reader := kafka.NewReader(kafka.ReaderConfig{
  37. Brokers: []string{kafkaURL},
  38. Topic: testTopic,
  39. GroupID: "test-group-" + time.Now().Format("20060102150405"),
  40. })
  41. defer reader.Close()
  42. // Create a test message
  43. reading := model.RawReading{
  44. Timestamp: time.Now().Format(time.RFC3339),
  45. Type: "BLE",
  46. MAC: "AA:BB:CC:DD:EE:FF",
  47. RSSI: -65,
  48. RawData: "0201060302A0",
  49. }
  50. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  51. // Execute
  52. mqtthandler(writer, "publish_out/gateway-1", messageBytes, appState)
  53. // Give Kafka time to propagate
  54. time.Sleep(1 * time.Second)
  55. // Read back the message
  56. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  57. defer cancel()
  58. msg, err := reader.ReadMessage(ctx)
  59. if err != nil {
  60. t.Fatalf("Failed to read message from Kafka: %v", err)
  61. }
  62. // Verify
  63. var adv model.BeaconAdvertisement
  64. err = json.Unmarshal(msg.Value, &adv)
  65. if err != nil {
  66. t.Fatalf("Failed to unmarshal beacon advertisement: %v", err)
  67. }
  68. if adv.ID != "test-beacon-1" {
  69. t.Errorf("Expected ID 'test-beacon-1', got '%s'", adv.ID)
  70. }
  71. if adv.Hostname != "gateway-1" {
  72. t.Errorf("Expected hostname 'gateway-1', got '%s'", adv.Hostname)
  73. }
  74. if adv.MAC != "AA:BB:CC:DD:EE:FF" {
  75. t.Errorf("Expected MAC 'AA:BB:CC:DD:EE:FF', got '%s'", adv.MAC)
  76. }
  77. }
  78. // TestIntegration_MultipleMessages tests handling multiple messages in sequence
  79. func TestIntegration_MultipleMessages(t *testing.T) {
  80. if testing.Short() {
  81. t.Skip("Skipping integration test in short mode")
  82. }
  83. kafkaURL := os.Getenv("KAFKA_URL")
  84. if kafkaURL == "" {
  85. kafkaURL = "localhost:9092"
  86. }
  87. testTopic := "test-rawbeacons-multi-" + time.Now().Format("20060102150405")
  88. // Setup
  89. appState := appcontext.NewAppState()
  90. appState.AddBeaconToLookup("AA:BB:CC:DD:EE:FF", "test-beacon-1")
  91. appState.AddBeaconToLookup("11:22:33:44:55:66", "test-beacon-2")
  92. writer := kafka.NewWriter(kafka.WriterConfig{
  93. Brokers: []string{kafkaURL},
  94. Topic: testTopic,
  95. })
  96. defer writer.Close()
  97. reader := kafka.NewReader(kafka.ReaderConfig{
  98. Brokers: []string{kafkaURL},
  99. Topic: testTopic,
  100. GroupID: "test-group-multi-" + time.Now().Format("20060102150405"),
  101. MinBytes: 10e3, // 10KB
  102. MaxBytes: 10e6, // 10MB
  103. })
  104. defer reader.Close()
  105. // Send multiple messages
  106. for i := 0; i < 5; i++ {
  107. reading := model.RawReading{
  108. Timestamp: time.Now().Format(time.RFC3339),
  109. Type: "BLE",
  110. MAC: "AA:BB:CC:DD:EE:FF",
  111. RSSI: -65 - i,
  112. RawData: "0201060302A0",
  113. }
  114. messageBytes, _ := json.Marshal([]model.RawReading{reading})
  115. mqtthandler(writer, "publish_out/gateway-1", messageBytes, appState)
  116. }
  117. // Give Kafka time to propagate
  118. time.Sleep(2 * time.Second)
  119. // Read and verify messages
  120. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  121. defer cancel()
  122. messageCount := 0
  123. for i := 0; i < 5; i++ {
  124. msg, err := reader.ReadMessage(ctx)
  125. if err != nil {
  126. t.Fatalf("Failed to read message %d from Kafka: %v", i+1, err)
  127. }
  128. var adv model.BeaconAdvertisement
  129. err = json.Unmarshal(msg.Value, &adv)
  130. if err != nil {
  131. t.Fatalf("Failed to unmarshal beacon advertisement %d: %v", i+1, err)
  132. }
  133. if adv.ID != "test-beacon-1" {
  134. t.Errorf("Message %d: Expected ID 'test-beacon-1', got '%s'", i+1, adv.ID)
  135. }
  136. messageCount++
  137. }
  138. if messageCount != 5 {
  139. t.Errorf("Expected 5 messages, got %d", messageCount)
  140. }
  141. }
  142. // TestIntegration_AppStateSequentialOperations tests sequential operations on AppState
  143. func TestIntegration_AppStateSequentialOperations(t *testing.T) {
  144. // Setup
  145. appState := appcontext.NewAppState()
  146. // Test sequential beacon additions
  147. for i := 0; i < 100; i++ {
  148. mac := generateMAC(i)
  149. id := generateID(i)
  150. appState.AddBeaconToLookup(mac, id)
  151. }
  152. // Verify all beacons were added
  153. for i := 0; i < 100; i++ {
  154. mac := generateMAC(i)
  155. expectedID := generateID(i)
  156. id, exists := appState.BeaconExists(mac)
  157. if !exists {
  158. t.Errorf("Beacon with MAC %s should exist", mac)
  159. }
  160. if id != expectedID {
  161. t.Errorf("Expected ID '%s', got '%s'", expectedID, id)
  162. }
  163. }
  164. // Test sequential deletions
  165. for i := 0; i < 50; i++ {
  166. mac := generateMAC(i)
  167. appState.RemoveBeaconFromLookup(mac)
  168. }
  169. // Verify deletions
  170. for i := 0; i < 50; i++ {
  171. mac := generateMAC(i)
  172. _, exists := appState.BeaconExists(mac)
  173. if exists {
  174. t.Errorf("Beacon with MAC %s should have been deleted", mac)
  175. }
  176. }
  177. // Verify remaining beacons still exist
  178. for i := 50; i < 100; i++ {
  179. mac := generateMAC(i)
  180. _, exists := appState.BeaconExists(mac)
  181. if !exists {
  182. t.Errorf("Beacon with MAC %s should still exist", mac)
  183. }
  184. }
  185. }
  186. func generateMAC(index int) string {
  187. return fmt.Sprintf("AA:BB:CC:DD:%02X:%02X", (index>>8)&0xFF, index&0xFF)
  188. }
  189. func generateID(index int) string {
  190. return fmt.Sprintf("beacon-%d", index)
  191. }
  192. // TestIntegration_CleanLookup tests the CleanLookup functionality
  193. func TestIntegration_CleanLookup(t *testing.T) {
  194. // Setup
  195. appState := appcontext.NewAppState()
  196. // Add multiple beacons
  197. for i := 0; i < 10; i++ {
  198. mac := generateMAC(i)
  199. id := generateID(i)
  200. appState.AddBeaconToLookup(mac, id)
  201. }
  202. // Verify they were added
  203. for i := 0; i < 10; i++ {
  204. mac := generateMAC(i)
  205. _, exists := appState.BeaconExists(mac)
  206. if !exists {
  207. t.Errorf("Beacon with MAC %s should exist before cleanup", mac)
  208. }
  209. }
  210. // Clean lookup
  211. appState.CleanLookup()
  212. // Verify all beacons were removed
  213. for i := 0; i < 10; i++ {
  214. mac := generateMAC(i)
  215. _, exists := appState.BeaconExists(mac)
  216. if exists {
  217. t.Errorf("Beacon with MAC %s should have been removed by cleanup", mac)
  218. }
  219. }
  220. }