persistence_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package indexer
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. )
  7. func TestPersistenceManager_Creation(t *testing.T) {
  8. // Test default config
  9. pm := NewPersistenceManager(nil)
  10. if pm == nil {
  11. t.Fatal("Expected non-nil persistence manager")
  12. }
  13. if pm.maxBatchSize != 1000 {
  14. t.Errorf("Expected default batch size 1000, got %d", pm.maxBatchSize)
  15. }
  16. if pm.flushInterval != 30*time.Second {
  17. t.Errorf("Expected default flush interval 30s, got %v", pm.flushInterval)
  18. }
  19. // Test custom config
  20. config := &IncrementalIndexConfig{
  21. MaxBatchSize: 500,
  22. FlushInterval: 15 * time.Second,
  23. CheckInterval: 2 * time.Minute,
  24. MaxAge: 7 * 24 * time.Hour,
  25. }
  26. pm2 := NewPersistenceManager(config)
  27. if pm2.maxBatchSize != 500 {
  28. t.Errorf("Expected custom batch size 500, got %d", pm2.maxBatchSize)
  29. }
  30. if pm2.flushInterval != 15*time.Second {
  31. t.Errorf("Expected custom flush interval 15s, got %v", pm2.flushInterval)
  32. }
  33. }
  34. func TestIncrementalIndexConfig_Default(t *testing.T) {
  35. config := DefaultIncrementalConfig()
  36. if config.MaxBatchSize != 1000 {
  37. t.Errorf("Expected default MaxBatchSize 1000, got %d", config.MaxBatchSize)
  38. }
  39. if config.FlushInterval != 30*time.Second {
  40. t.Errorf("Expected default FlushInterval 30s, got %v", config.FlushInterval)
  41. }
  42. if config.CheckInterval != 5*time.Minute {
  43. t.Errorf("Expected default CheckInterval 5m, got %v", config.CheckInterval)
  44. }
  45. if config.MaxAge != 30*24*time.Hour {
  46. t.Errorf("Expected default MaxAge 30 days, got %v", config.MaxAge)
  47. }
  48. }
  49. func TestGetMainLogPathFromFile(t *testing.T) {
  50. testCases := []struct {
  51. input string
  52. expected string
  53. }{
  54. {"/var/log/nginx/access.log", "/var/log/nginx/access.log"},
  55. {"/var/log/nginx/access.log.1", "/var/log/nginx/access.log"},
  56. {"/var/log/nginx/access.log.2.gz", "/var/log/nginx/access.log"},
  57. {"/var/log/nginx/access.1.log", "/var/log/nginx/access.log"},
  58. {"/var/log/nginx/access.2.log.gz", "/var/log/nginx/access.log"},
  59. {"/var/log/nginx/access.3.log.bz2", "/var/log/nginx/access.log"},
  60. {"/var/log/nginx/error.log.10", "/var/log/nginx/error.log"},
  61. {"/var/log/nginx/error.1.log.xz", "/var/log/nginx/error.log"},
  62. {"/var/log/nginx/access.log.20231201", "/var/log/nginx/access.log"},
  63. {"/var/log/nginx/access.log.2023-12-01", "/var/log/nginx/access.log"},
  64. {"/var/log/nginx/custom.log.99", "/var/log/nginx/custom.log"},
  65. {"/logs/app.5.log.lz4", "/logs/app.log"},
  66. }
  67. for _, tc := range testCases {
  68. result := getMainLogPathFromFile(tc.input)
  69. if result != tc.expected {
  70. t.Errorf("getMainLogPathFromFile(%s) = %s, expected %s", tc.input, result, tc.expected)
  71. }
  72. }
  73. }
  74. func TestIsDatePattern(t *testing.T) {
  75. testCases := []struct {
  76. input string
  77. expected bool
  78. }{
  79. {"20231201", true}, // YYYYMMDD
  80. {"2023-12-01", true}, // YYYY-MM-DD
  81. {"2023.12.01", true}, // YYYY.MM.DD
  82. {"231201", true}, // YYMMDD
  83. {"access", false}, // Not a date
  84. {"123", false}, // Too short
  85. {"12345678901", false}, // Too long
  86. {"2023-13-01", true}, // Would match pattern (validation not checked)
  87. {"log", false}, // Text
  88. {"1", false}, // Single digit
  89. }
  90. for _, tc := range testCases {
  91. result := isDatePattern(tc.input)
  92. if result != tc.expected {
  93. t.Errorf("isDatePattern(%s) = %v, expected %v", tc.input, result, tc.expected)
  94. }
  95. }
  96. }
  97. func TestLogFileInfo_Structure(t *testing.T) {
  98. // Test LogFileInfo struct initialization
  99. info := &LogFileInfo{
  100. Path: "/var/log/nginx/access.log",
  101. LastModified: time.Now().Unix(),
  102. LastSize: 1024,
  103. LastIndexed: time.Now().Unix(),
  104. LastPosition: 512,
  105. }
  106. if info.Path != "/var/log/nginx/access.log" {
  107. t.Errorf("Expected path to be set correctly")
  108. }
  109. if info.LastSize != 1024 {
  110. t.Errorf("Expected LastSize 1024, got %d", info.LastSize)
  111. }
  112. if info.LastPosition != 512 {
  113. t.Errorf("Expected LastPosition 512, got %d", info.LastPosition)
  114. }
  115. // Checksum field removed from LogFileInfo
  116. }
  117. // Mock tests (without database dependency)
  118. func TestPersistenceManager_CacheOperations(t *testing.T) {
  119. pm := NewPersistenceManager(nil)
  120. // Test initial cache state
  121. if len(pm.enabledPaths) != 0 {
  122. t.Errorf("Expected empty cache initially, got %d entries", len(pm.enabledPaths))
  123. }
  124. // Simulate cache operations
  125. pm.enabledPaths["/test/path1"] = true
  126. pm.enabledPaths["/test/path2"] = false
  127. if len(pm.enabledPaths) != 2 {
  128. t.Errorf("Expected 2 cache entries, got %d", len(pm.enabledPaths))
  129. }
  130. // Test RefreshCache method preparation (would need database in real scenario)
  131. pm.enabledPaths = make(map[string]bool)
  132. if len(pm.enabledPaths) != 0 {
  133. t.Errorf("Expected cache to be cleared")
  134. }
  135. }
  136. func TestPersistenceManager_ConfigValidation(t *testing.T) {
  137. // Test with various configurations
  138. configs := []*IncrementalIndexConfig{
  139. {
  140. MaxBatchSize: 100,
  141. FlushInterval: 5 * time.Second,
  142. CheckInterval: 1 * time.Minute,
  143. MaxAge: 1 * time.Hour,
  144. },
  145. {
  146. MaxBatchSize: 10000,
  147. FlushInterval: 5 * time.Minute,
  148. CheckInterval: 30 * time.Minute,
  149. MaxAge: 90 * 24 * time.Hour,
  150. },
  151. }
  152. for i, config := range configs {
  153. pm := NewPersistenceManager(config)
  154. if pm.maxBatchSize != config.MaxBatchSize {
  155. t.Errorf("Config %d: Expected MaxBatchSize %d, got %d", i, config.MaxBatchSize, pm.maxBatchSize)
  156. }
  157. if pm.flushInterval != config.FlushInterval {
  158. t.Errorf("Config %d: Expected FlushInterval %v, got %v", i, config.FlushInterval, pm.flushInterval)
  159. }
  160. }
  161. }
  162. func TestGetMainLogPathFromFile_EdgeCases(t *testing.T) {
  163. edgeCases := []struct {
  164. input string
  165. expected string
  166. description string
  167. }{
  168. {
  169. "/var/log/nginx/access.999.log.gz",
  170. "/var/log/nginx/access.log",
  171. "High rotation number with compression",
  172. },
  173. {
  174. "/var/log/nginx/access.log.2023.12.01.gz",
  175. "/var/log/nginx/access.log",
  176. "Date-based rotation with compression",
  177. },
  178. {
  179. "/single/file.log",
  180. "/single/file.log",
  181. "No rotation pattern",
  182. },
  183. {
  184. "/path/with.dots.in.name.log.1",
  185. "/path/with.dots.in.name.log",
  186. "Multiple dots in filename",
  187. },
  188. {
  189. "/var/log/nginx/access.log.1000",
  190. "/var/log/nginx/access.log.1000",
  191. "Number too high for rotation (should not match)",
  192. },
  193. }
  194. for _, tc := range edgeCases {
  195. result := getMainLogPathFromFile(tc.input)
  196. if result != tc.expected {
  197. t.Errorf("%s: getMainLogPathFromFile(%s) = %s, expected %s",
  198. tc.description, tc.input, result, tc.expected)
  199. }
  200. }
  201. }
  202. func TestPersistenceManager_Close(t *testing.T) {
  203. pm := NewPersistenceManager(nil)
  204. // Add some cache entries
  205. pm.enabledPaths["/test/path1"] = true
  206. pm.enabledPaths["/test/path2"] = false
  207. // Close should clean up
  208. err := pm.Close()
  209. if err != nil {
  210. t.Errorf("Expected no error on close, got %v", err)
  211. }
  212. // Cache should be cleared
  213. if pm.enabledPaths != nil {
  214. t.Errorf("Expected cache to be nil after close")
  215. }
  216. }
  217. // Benchmark tests for performance validation
  218. func BenchmarkGetMainLogPathFromFile(b *testing.B) {
  219. testPaths := []string{
  220. "/var/log/nginx/access.log",
  221. "/var/log/nginx/access.log.1",
  222. "/var/log/nginx/access.log.2.gz",
  223. "/var/log/nginx/access.1.log",
  224. "/var/log/nginx/access.2.log.gz",
  225. "/var/log/nginx/error.log.20231201",
  226. }
  227. b.ResetTimer()
  228. for i := 0; i < b.N; i++ {
  229. path := testPaths[i%len(testPaths)]
  230. _ = getMainLogPathFromFile(path)
  231. }
  232. }
  233. func BenchmarkIsDatePattern(b *testing.B) {
  234. testStrings := []string{
  235. "20231201",
  236. "2023-12-01",
  237. "access",
  238. "log",
  239. "231201",
  240. "notadate",
  241. }
  242. b.ResetTimer()
  243. for i := 0; i < b.N; i++ {
  244. s := testStrings[i%len(testStrings)]
  245. _ = isDatePattern(s)
  246. }
  247. }
  248. func BenchmarkPersistenceManager_CacheAccess(b *testing.B) {
  249. pm := NewPersistenceManager(nil)
  250. // Populate cache
  251. for i := 0; i < 1000; i++ {
  252. pm.enabledPaths[fmt.Sprintf("/path/file%d.log", i)] = i%2 == 0
  253. }
  254. b.ResetTimer()
  255. for i := 0; i < b.N; i++ {
  256. path := fmt.Sprintf("/path/file%d.log", i%1000)
  257. _ = pm.enabledPaths[path]
  258. }
  259. }