Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

416 rader
11 KiB

  1. package decoder
  2. import (
  3. "context"
  4. "os"
  5. "testing"
  6. "time"
  7. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  8. "github.com/AFASystems/presence/internal/pkg/model"
  9. "github.com/segmentio/kafka-go"
  10. )
  11. // TestIntegration_DecoderEndToEnd tests the complete decoder flow
  12. func TestIntegration_DecoderEndToEnd(t *testing.T) {
  13. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  14. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  15. }
  16. // Check if Kafka is available
  17. kafkaURL := os.Getenv("KAFKA_URL")
  18. if kafkaURL == "" {
  19. kafkaURL = "localhost:9092"
  20. }
  21. // Create test topics
  22. alertTopic := "test-alertbeacons-" + time.Now().Format("20060102150405")
  23. // Setup
  24. appState := appcontext.NewAppState()
  25. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  26. // Register a test parser
  27. config := model.Config{
  28. Name: "integration-test-parser",
  29. Min: 2,
  30. Max: 20,
  31. Pattern: []string{"0x02", "0x01"},
  32. Configs: map[string]model.ParserConfig{},
  33. }
  34. parserRegistry.Register("integration-test-parser", config)
  35. // Create Kafka writer
  36. writer := kafka.NewWriter(kafka.WriterConfig{
  37. Brokers: []string{kafkaURL},
  38. Topic: alertTopic,
  39. })
  40. defer writer.Close()
  41. // Create Kafka reader to verify messages
  42. reader := kafka.NewReader(kafka.ReaderConfig{
  43. Brokers: []string{kafkaURL},
  44. Topic: alertTopic,
  45. GroupID: "test-group-" + time.Now().Format("20060102150405"),
  46. })
  47. defer reader.Close()
  48. // Create a test beacon advertisement
  49. adv := model.BeaconAdvertisement{
  50. ID: "integration-test-beacon",
  51. Data: "020106", // Valid hex data
  52. }
  53. // Process the beacon
  54. err := decodeBeacon(adv, appState, writer, parserRegistry)
  55. if err != nil {
  56. t.Logf("Decode beacon returned error (may be expected if no parser matches): %v", err)
  57. }
  58. // Give Kafka time to propagate
  59. time.Sleep(1 * time.Second)
  60. // Verify event was stored in appState
  61. event, exists := appState.GetBeaconEvent("integration-test-beacon")
  62. if exists {
  63. t.Logf("Event stored in appState: %+v", event)
  64. }
  65. }
  66. // TestIntegration_ParserRegistryOperations tests parser registry with real Kafka
  67. func TestIntegration_ParserRegistryOperations(t *testing.T) {
  68. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  69. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  70. }
  71. kafkaURL := os.Getenv("KAFKA_URL")
  72. if kafkaURL == "" {
  73. kafkaURL = "localhost:9092"
  74. }
  75. alertTopic := "test-alertbeacons-registry-" + time.Now().Format("20060102150405")
  76. // Setup
  77. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  78. writer := kafka.NewWriter(kafka.WriterConfig{
  79. Brokers: []string{kafkaURL},
  80. Topic: alertTopic,
  81. })
  82. defer writer.Close()
  83. // Test parser registration through Kafka message flow
  84. parserMsg := model.KafkaParser{
  85. ID: "add",
  86. Name: "kafka-test-parser",
  87. Config: model.Config{
  88. Name: "kafka-test-parser",
  89. Min: 2,
  90. Max: 20,
  91. Pattern: []string{"0x02", "0x01"},
  92. Configs: map[string]model.ParserConfig{},
  93. },
  94. }
  95. // Simulate parser registry update
  96. switch parserMsg.ID {
  97. case "add":
  98. config := parserMsg.Config
  99. parserRegistry.Register(config.Name, config)
  100. case "delete":
  101. parserRegistry.Unregister(parserMsg.Name)
  102. case "update":
  103. config := parserMsg.Config
  104. parserRegistry.Register(config.Name, config)
  105. }
  106. // Verify parser was registered
  107. if len(parserRegistry.ParserList) != 1 {
  108. t.Errorf("Expected 1 parser in registry, got %d", len(parserRegistry.ParserList))
  109. }
  110. if _, exists := parserRegistry.ParserList["kafka-test-parser"]; !exists {
  111. t.Error("Parser should exist in registry")
  112. }
  113. }
  114. // TestIntegration_MultipleBeaconsSequential tests processing multiple beacons
  115. func TestIntegration_MultipleBeaconsSequential(t *testing.T) {
  116. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  117. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  118. }
  119. kafkaURL := os.Getenv("KAFKA_URL")
  120. if kafkaURL == "" {
  121. kafkaURL = "localhost:9092"
  122. }
  123. alertTopic := "test-alertbeacons-multi-" + time.Now().Format("20060102150405")
  124. // Setup
  125. appState := appcontext.NewAppState()
  126. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  127. // Register parser
  128. config := model.Config{
  129. Name: "multi-test-parser",
  130. Min: 2,
  131. Max: 20,
  132. Pattern: []string{"0x02", "0x01"},
  133. Configs: map[string]model.ParserConfig{},
  134. }
  135. parserRegistry.Register("multi-test-parser", config)
  136. writer := kafka.NewWriter(kafka.WriterConfig{
  137. Brokers: []string{kafkaURL},
  138. Topic: alertTopic,
  139. })
  140. defer writer.Close()
  141. reader := kafka.NewReader(kafka.ReaderConfig{
  142. Brokers: []string{kafkaURL},
  143. Topic: alertTopic,
  144. GroupID: "test-group-multi-" + time.Now().Format("20060102150405"),
  145. MinBytes: 10e3,
  146. MaxBytes: 10e6,
  147. })
  148. defer reader.Close()
  149. // Process multiple beacons
  150. beacons := []model.BeaconAdvertisement{
  151. {ID: "beacon-1", Data: "020106"},
  152. {ID: "beacon-2", Data: "020107"},
  153. {ID: "beacon-3", Data: "020108"},
  154. }
  155. for _, adv := range beacons {
  156. err := decodeBeacon(adv, appState, writer, parserRegistry)
  157. if err != nil {
  158. t.Logf("Processing beacon %s returned error: %v", adv.ID, err)
  159. }
  160. }
  161. // Give Kafka time to propagate
  162. time.Sleep(2 * time.Second)
  163. // Verify events in appState
  164. for _, adv := range beacons {
  165. event, exists := appState.GetBeaconEvent(adv.ID)
  166. if exists {
  167. t.Logf("Event for %s: %+v", adv.ID, event)
  168. }
  169. }
  170. }
  171. // TestIntegration_EventDeduplication tests that duplicate events are not published
  172. func TestIntegration_EventDeduplication(t *testing.T) {
  173. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  174. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  175. }
  176. kafkaURL := os.Getenv("KAFKA_URL")
  177. if kafkaURL == "" {
  178. kafkaURL = "localhost:9092"
  179. }
  180. alertTopic := "test-alertbeacons-dedup-" + time.Now().Format("20060102150405")
  181. // Setup
  182. appState := appcontext.NewAppState()
  183. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  184. // Register parser
  185. config := model.Config{
  186. Name: "dedup-test-parser",
  187. Min: 2,
  188. Max: 20,
  189. Pattern: []string{"0x02", "0x01"},
  190. Configs: map[string]model.ParserConfig{},
  191. }
  192. parserRegistry.Register("dedup-test-parser", config)
  193. writer := kafka.NewWriter(kafka.WriterConfig{
  194. Brokers: []string{kafkaURL},
  195. Topic: alertTopic,
  196. })
  197. defer writer.Close()
  198. reader := kafka.NewReader(kafka.ReaderConfig{
  199. Brokers: []string{kafkaURL},
  200. Topic: alertTopic,
  201. GroupID: "test-group-dedup-" + time.Now().Format("20060102150405"),
  202. })
  203. defer reader.Close()
  204. // Create identical beacon advertisement
  205. adv := model.BeaconAdvertisement{
  206. ID: "dedup-test-beacon",
  207. Data: "020106",
  208. }
  209. // Process first time
  210. err := decodeBeacon(adv, appState, writer, parserRegistry)
  211. if err != nil {
  212. t.Logf("First processing returned error: %v", err)
  213. }
  214. // Process second time with identical data
  215. err = decodeBeacon(adv, appState, writer, parserRegistry)
  216. if err != nil {
  217. t.Logf("Second processing returned error: %v", err)
  218. }
  219. // Give Kafka time to propagate
  220. time.Sleep(1 * time.Second)
  221. // Try to read from Kafka - should have at most 1 message due to deduplication
  222. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  223. defer cancel()
  224. messageCount := 0
  225. for {
  226. msg, err := reader.ReadMessage(ctx)
  227. if err != nil {
  228. break
  229. }
  230. messageCount++
  231. t.Logf("Read message %d: %s", messageCount, string(msg.Value))
  232. if messageCount > 1 {
  233. t.Error("Expected at most 1 message due to deduplication, got more")
  234. break
  235. }
  236. }
  237. t.Logf("Total messages read: %d", messageCount)
  238. }
  239. // TestIntegration_AppStatePersistence tests that events persist in AppState
  240. func TestIntegration_AppStatePersistence(t *testing.T) {
  241. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  242. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  243. }
  244. kafkaURL := os.Getenv("KAFKA_URL")
  245. if kafkaURL == "" {
  246. kafkaURL = "localhost:9092"
  247. }
  248. alertTopic := "test-alertbeacons-persist-" + time.Now().Format("20060102150405")
  249. // Setup
  250. appState := appcontext.NewAppState()
  251. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  252. config := model.Config{
  253. Name: "persist-test-parser",
  254. Min: 2,
  255. Max: 20,
  256. Pattern: []string{"0x02", "0x01"},
  257. Configs: map[string]model.ParserConfig{},
  258. }
  259. parserRegistry.Register("persist-test-parser", config)
  260. writer := kafka.NewWriter(kafka.WriterConfig{
  261. Brokers: []string{kafkaURL},
  262. Topic: alertTopic,
  263. })
  264. defer writer.Close()
  265. // Process beacon
  266. adv := model.BeaconAdvertisement{
  267. ID: "persist-test-beacon",
  268. Data: "020106",
  269. }
  270. err := decodeBeacon(adv, appState, writer, parserRegistry)
  271. if err != nil {
  272. t.Logf("Processing returned error: %v", err)
  273. }
  274. // Verify event persists in AppState
  275. event, exists := appState.GetBeaconEvent("persist-test-beacon")
  276. if !exists {
  277. t.Error("Event should exist in AppState after processing")
  278. } else {
  279. t.Logf("Event persisted: ID=%s, Type=%s, Battery=%d",
  280. event.ID, event.Type, event.Battery)
  281. // Verify event can be serialized to JSON
  282. jsonData, err := event.ToJSON()
  283. if err != nil {
  284. t.Errorf("Failed to serialize event to JSON: %v", err)
  285. } else {
  286. t.Logf("Event JSON: %s", string(jsonData))
  287. }
  288. }
  289. }
  290. // TestIntegration_ParserUpdateFlow tests updating parsers during runtime
  291. func TestIntegration_ParserUpdateFlow(t *testing.T) {
  292. if testing.Short() || os.Getenv("E2E_TEST") != "1" {
  293. t.Skip("Skipping integration test (set E2E_TEST=1 and run without -short)")
  294. }
  295. kafkaURL := os.Getenv("KAFKA_URL")
  296. if kafkaURL == "" {
  297. kafkaURL = "localhost:9092"
  298. }
  299. alertTopic := "test-alertbeacons-update-" + time.Now().Format("20060102150405")
  300. // Setup
  301. appState := appcontext.NewAppState()
  302. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  303. writer := kafka.NewWriter(kafka.WriterConfig{
  304. Brokers: []string{kafkaURL},
  305. Topic: alertTopic,
  306. })
  307. defer writer.Close()
  308. // Initial parser config
  309. config1 := model.Config{
  310. Name: "update-test-parser",
  311. Min: 2,
  312. Max: 20,
  313. Pattern: []string{"0x02", "0x01"},
  314. Configs: map[string]model.ParserConfig{},
  315. }
  316. parserRegistry.Register("update-test-parser", config1)
  317. // Process with initial config
  318. adv := model.BeaconAdvertisement{
  319. ID: "update-test-beacon",
  320. Data: "020106",
  321. }
  322. err := decodeBeacon(adv, appState, writer, parserRegistry)
  323. t.Logf("First processing: %v", err)
  324. // Update parser config
  325. config2 := model.Config{
  326. Name: "update-test-parser",
  327. Min: 3,
  328. Max: 25,
  329. Pattern: []string{"0x03"},
  330. Configs: map[string]model.ParserConfig{},
  331. }
  332. parserRegistry.Register("update-test-parser", config2)
  333. // Process again with updated config
  334. adv2 := model.BeaconAdvertisement{
  335. ID: "update-test-beacon-2",
  336. Data: "030107",
  337. }
  338. err = decodeBeacon(adv2, appState, writer, parserRegistry)
  339. t.Logf("Second processing with updated parser: %v", err)
  340. // Verify parser still exists
  341. if _, exists := parserRegistry.ParserList["update-test-parser"]; !exists {
  342. t.Error("Parser should exist after update")
  343. }
  344. }