25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

390 lines
11 KiB

  1. package decoder
  2. import (
  3. "bytes"
  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 TestDecodeBeacon_EmptyData(t *testing.T) {
  10. // Setup
  11. appState := appcontext.NewAppState()
  12. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  13. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  14. adv := model.BeaconAdvertisement{
  15. ID: "test-beacon",
  16. Data: "", // Empty data
  17. }
  18. // Execute
  19. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  20. // Assert
  21. if err != nil {
  22. t.Errorf("Expected no error for empty data, got %v", err)
  23. }
  24. if len(mockWriter.Messages) != 0 {
  25. t.Errorf("Expected no messages for empty data, got %d", len(mockWriter.Messages))
  26. }
  27. }
  28. func TestDecodeBeacon_WhitespaceOnly(t *testing.T) {
  29. // Setup
  30. appState := appcontext.NewAppState()
  31. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  32. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  33. adv := model.BeaconAdvertisement{
  34. ID: "test-beacon",
  35. Data: " ", // Whitespace only
  36. }
  37. // Execute
  38. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  39. // Assert
  40. if err != nil {
  41. t.Errorf("Expected no error for whitespace-only data, got %v", err)
  42. }
  43. if len(mockWriter.Messages) != 0 {
  44. t.Errorf("Expected no messages for whitespace-only data, got %d", len(mockWriter.Messages))
  45. }
  46. }
  47. func TestDecodeBeacon_InvalidHex(t *testing.T) {
  48. // Setup
  49. appState := appcontext.NewAppState()
  50. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  51. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  52. adv := model.BeaconAdvertisement{
  53. ID: "test-beacon",
  54. Data: "INVALID_HEX_DATA!!!",
  55. }
  56. // Execute
  57. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  58. // Assert
  59. if err == nil {
  60. t.Error("Expected error for invalid hex data, got nil")
  61. }
  62. if len(mockWriter.Messages) != 0 {
  63. t.Errorf("Expected no messages for invalid hex, got %d", len(mockWriter.Messages))
  64. }
  65. }
  66. func TestDecodeBeacon_ValidHexNoParser(t *testing.T) {
  67. // Setup
  68. appState := appcontext.NewAppState()
  69. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  70. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)} // No parsers registered
  71. // Valid hex but no matching parser (03 02 FF 06 - type 0x02, no parser registered for it)
  72. adv := model.BeaconAdvertisement{
  73. ID: "test-beacon",
  74. Data: "0302FF06", // Valid AD structure
  75. }
  76. // Execute
  77. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  78. // Assert
  79. if err != nil {
  80. t.Errorf("Expected no error when no parser matches, got %v", err)
  81. }
  82. if len(mockWriter.Messages) != 0 {
  83. t.Errorf("Expected no messages when no parser matches, got %d", len(mockWriter.Messages))
  84. }
  85. }
  86. func TestDecodeBeacon_Deduplication(t *testing.T) {
  87. // Setup
  88. appState := appcontext.NewAppState()
  89. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  90. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  91. // Register a test parser
  92. // Use pattern 0x02 - AD structure 03 02 FF 06 (len 3, type 0x02) - not removed by RemoveFlagBytes
  93. config := model.Config{
  94. Name: "test-parser",
  95. Min: 3,
  96. Max: 10,
  97. Pattern: []string{"0x02"},
  98. Configs: map[string]model.ParserConfig{
  99. "battery": {Length: 1, Offset: 2, Order: "littleendian"},
  100. },
  101. }
  102. parserRegistry.Register("test-parser", config)
  103. // Create an event that will be parsed (03 02 FF 06 - not removed by RemoveFlagBytes)
  104. adv := model.BeaconAdvertisement{
  105. ID: "test-beacon",
  106. Data: "0302FF06",
  107. }
  108. // First processing - should publish
  109. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  110. if err != nil {
  111. t.Fatalf("First processing failed: %v", err)
  112. }
  113. firstMessageCount := len(mockWriter.Messages)
  114. // Second processing with identical data - should deduplicate
  115. err = decodeBeacon(adv, appState, mockWriter, parserRegistry)
  116. if err != nil {
  117. t.Fatalf("Second processing failed: %v", err)
  118. }
  119. // Assert - message count should not have changed
  120. if len(mockWriter.Messages) != firstMessageCount {
  121. t.Errorf("Expected deduplication, got %d messages (should be %d)", len(mockWriter.Messages), firstMessageCount)
  122. }
  123. }
  124. func TestDecodeBeacon_DifferentDataPublishes(t *testing.T) {
  125. // Setup
  126. appState := appcontext.NewAppState()
  127. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  128. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  129. // Register a test parser
  130. // Use pattern 0x02 - AD structure 03 02 FF 06 (len 3, type 0x02) - not removed by RemoveFlagBytes
  131. config := model.Config{
  132. Name: "test-parser",
  133. Min: 3,
  134. Max: 10,
  135. Pattern: []string{"0x02"},
  136. Configs: map[string]model.ParserConfig{
  137. "battery": {Length: 1, Offset: 2, Order: "littleendian"},
  138. },
  139. }
  140. parserRegistry.Register("test-parser", config)
  141. // First processing
  142. adv1 := model.BeaconAdvertisement{
  143. ID: "test-beacon",
  144. Data: "020106",
  145. }
  146. err := decodeBeacon(adv1, appState, mockWriter, parserRegistry)
  147. if err != nil {
  148. t.Fatalf("First processing failed: %v", err)
  149. }
  150. firstMessageCount := len(mockWriter.Messages)
  151. // Second processing with different data - should publish again
  152. adv2 := model.BeaconAdvertisement{
  153. ID: "test-beacon",
  154. Data: "0302FF07", // Different data
  155. }
  156. err = decodeBeacon(adv2, appState, mockWriter, parserRegistry)
  157. if err != nil {
  158. t.Fatalf("Second processing failed: %v", err)
  159. }
  160. // Assert - message count should have increased
  161. if len(mockWriter.Messages) != firstMessageCount+1 {
  162. t.Errorf("Expected new message for different data, got %d messages (expected %d)", len(mockWriter.Messages), firstMessageCount+1)
  163. }
  164. }
  165. func TestDecodeBeacon_WithFlagBytes(t *testing.T) {
  166. // Setup
  167. appState := appcontext.NewAppState()
  168. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  169. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  170. // Register a test parser
  171. // Use pattern 0x02 - AD structure 03 02 FF 06 (len 3, type 0x02) - not removed by RemoveFlagBytes
  172. config := model.Config{
  173. Name: "test-parser",
  174. Min: 3,
  175. Max: 10,
  176. Pattern: []string{"0x02"},
  177. Configs: map[string]model.ParserConfig{
  178. "battery": {Length: 1, Offset: 2, Order: "littleendian"},
  179. },
  180. }
  181. parserRegistry.Register("test-parser", config)
  182. // Data with flag bytes first (02 01 06), then our structure (03 02 FF 08)
  183. adv := model.BeaconAdvertisement{
  184. ID: "test-beacon",
  185. Data: "0201060302FF08", // Flags removed, then 03 02 FF 08 remains
  186. }
  187. // Execute
  188. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  189. // Assert - should process successfully after flag removal
  190. if err != nil {
  191. t.Errorf("Expected no error with flag bytes, got %v", err)
  192. }
  193. }
  194. func TestDecodeBeacon_MultipleBeacons(t *testing.T) {
  195. // Setup
  196. appState := appcontext.NewAppState()
  197. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  198. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  199. // Register a test parser
  200. // Use pattern 0x02 - AD structure 03 02 FF 06 (len 3, type 0x02) - not removed by RemoveFlagBytes
  201. config := model.Config{
  202. Name: "test-parser",
  203. Min: 3,
  204. Max: 10,
  205. Pattern: []string{"0x02"},
  206. Configs: map[string]model.ParserConfig{
  207. "battery": {Length: 1, Offset: 2, Order: "littleendian"},
  208. },
  209. }
  210. parserRegistry.Register("test-parser", config)
  211. // Process multiple different beacons (03 02 FF xx - not removed by RemoveFlagBytes)
  212. beacons := []model.BeaconAdvertisement{
  213. {ID: "beacon-1", Data: "0302FF06"},
  214. {ID: "beacon-2", Data: "0302FF07"},
  215. {ID: "beacon-3", Data: "0302FF08"},
  216. }
  217. for _, adv := range beacons {
  218. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  219. if err != nil {
  220. t.Errorf("Failed to process beacon %s: %v", adv.ID, err)
  221. }
  222. }
  223. // Each unique beacon should produce a message
  224. if len(mockWriter.Messages) != len(beacons) {
  225. t.Errorf("Expected %d messages, got %d", len(beacons), len(mockWriter.Messages))
  226. }
  227. }
  228. func TestProcessIncoming_ErrorHandling(t *testing.T) {
  229. // Setup
  230. appState := appcontext.NewAppState()
  231. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  232. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  233. // Invalid data that will cause an error
  234. adv := model.BeaconAdvertisement{
  235. ID: "test-beacon",
  236. Data: "INVALID_HEX",
  237. }
  238. // Execute - should not panic, just handle error
  239. processIncoming(adv, appState, mockWriter, parserRegistry)
  240. // Assert - no messages should be written
  241. if len(mockWriter.Messages) != 0 {
  242. t.Errorf("Expected no messages on error, got %d", len(mockWriter.Messages))
  243. }
  244. }
  245. func TestDecodeBeacon_EventHashing(t *testing.T) {
  246. // Setup
  247. appState := appcontext.NewAppState()
  248. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  249. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  250. // Register a test parser that creates consistent events
  251. // Use pattern 0x02 - AD structure 03 02 FF 06 (len 3, type 0x02) - not removed by RemoveFlagBytes
  252. config := model.Config{
  253. Name: "test-parser",
  254. Min: 3,
  255. Max: 10,
  256. Pattern: []string{"0x02"},
  257. Configs: map[string]model.ParserConfig{
  258. "battery": {Length: 1, Offset: 2, Order: "littleendian"},
  259. },
  260. }
  261. parserRegistry.Register("test-parser", config)
  262. adv := model.BeaconAdvertisement{
  263. ID: "test-beacon",
  264. Data: "0302FF06",
  265. }
  266. // First processing
  267. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  268. if err != nil {
  269. t.Fatalf("First processing failed: %v", err)
  270. }
  271. // Get the event from appState
  272. event, exists := appState.GetBeaconEvent("test-beacon")
  273. if !exists {
  274. t.Fatal("Event should exist in appState")
  275. }
  276. // Verify hash is created
  277. hash := event.Hash()
  278. if hash == nil || len(hash) == 0 {
  279. t.Error("Expected non-empty hash")
  280. }
  281. // Second processing should be deduplicated based on hash
  282. err = decodeBeacon(adv, appState, mockWriter, parserRegistry)
  283. if err != nil {
  284. t.Fatalf("Second processing failed: %v", err)
  285. }
  286. // Should still have only one message
  287. if len(mockWriter.Messages) != 1 {
  288. t.Errorf("Expected 1 message after deduplication, got %d", len(mockWriter.Messages))
  289. }
  290. }
  291. func TestDecodeBeacon_VariousHexFormats(t *testing.T) {
  292. // Setup
  293. appState := appcontext.NewAppState()
  294. mockWriter := &MockKafkaWriter{Messages: []kafka.Message{}}
  295. parserRegistry := &model.ParserRegistry{ParserList: make(map[string]model.BeaconParser)}
  296. testCases := []struct {
  297. name string
  298. hexData string
  299. shouldError bool
  300. }{
  301. {"lowercase hex", "020106aa", false},
  302. {"uppercase hex", "020106AA", false},
  303. {"mixed case", "020106AaFf", false},
  304. {"with spaces", " 020106 ", false},
  305. {"odd length", "02016", true},
  306. {"invalid chars", "020106ZZ", true},
  307. }
  308. for _, tc := range testCases {
  309. t.Run(tc.name, func(t *testing.T) {
  310. adv := model.BeaconAdvertisement{
  311. ID: "test-beacon",
  312. Data: tc.hexData,
  313. }
  314. err := decodeBeacon(adv, appState, mockWriter, parserRegistry)
  315. if tc.shouldError && err == nil {
  316. t.Errorf("Expected error for %s, got nil", tc.name)
  317. }
  318. if !tc.shouldError && err != nil && !bytes.Contains([]byte(err.Error()), []byte("no parser")) {
  319. // Error is OK if it's "no parser", but not for hex decoding
  320. t.Logf("Got expected error for %s: %v", tc.name, err)
  321. }
  322. })
  323. }
  324. }