1
0

progress_tracker_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. package indexer
  2. import (
  3. "context"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. "testing"
  8. "time"
  9. )
  10. func TestProgressTracker_BasicFunctionality(t *testing.T) {
  11. // Create a new progress tracker
  12. var progressNotifications []ProgressNotification
  13. var completionNotifications []CompletionNotification
  14. var mu sync.Mutex
  15. config := &ProgressConfig{
  16. OnProgress: func(pn ProgressNotification) {
  17. mu.Lock()
  18. progressNotifications = append(progressNotifications, pn)
  19. mu.Unlock()
  20. },
  21. OnCompletion: func(cn CompletionNotification) {
  22. mu.Lock()
  23. completionNotifications = append(completionNotifications, cn)
  24. mu.Unlock()
  25. },
  26. }
  27. tracker := NewProgressTracker("/var/log/nginx/access.log", config)
  28. // Test adding files with various rotation formats
  29. tracker.AddFile("/var/log/nginx/access.log", false)
  30. tracker.AddFile("/var/log/nginx/access.log.1", false)
  31. tracker.AddFile("/var/log/nginx/access.log.2.gz", true)
  32. tracker.AddFile("/var/log/nginx/access.1.log", false)
  33. tracker.AddFile("/var/log/nginx/access.2.log.gz", true)
  34. // Set estimates for files
  35. tracker.SetFileEstimate("/var/log/nginx/access.log", 1000)
  36. tracker.SetFileEstimate("/var/log/nginx/access.log.1", 2000)
  37. tracker.SetFileEstimate("/var/log/nginx/access.log.2.gz", 500)
  38. tracker.SetFileEstimate("/var/log/nginx/access.1.log", 800)
  39. tracker.SetFileEstimate("/var/log/nginx/access.2.log.gz", 300)
  40. // Test initial progress
  41. progress := tracker.GetProgress()
  42. if progress.Percentage != 0 {
  43. t.Errorf("Expected initial progress to be 0, got %.2f", progress.Percentage)
  44. }
  45. if progress.TotalFiles != 5 {
  46. t.Errorf("Expected 5 total files, got %d", progress.TotalFiles)
  47. }
  48. expectedTotal := int64(1000 + 2000 + 500 + 800 + 300) // 4600
  49. if progress.EstimatedLines != expectedTotal {
  50. t.Errorf("Expected %d estimated lines, got %d", expectedTotal, progress.EstimatedLines)
  51. }
  52. // Start processing first file
  53. tracker.StartFile("/var/log/nginx/access.log")
  54. progress = tracker.GetProgress()
  55. if progress.ProcessingFiles != 1 {
  56. t.Errorf("Expected 1 processing file, got %d", progress.ProcessingFiles)
  57. }
  58. // Update progress for first file
  59. tracker.UpdateFileProgress("/var/log/nginx/access.log", 500)
  60. progress = tracker.GetProgress()
  61. // With hybrid calculation: 40% line-based + 60% file-based
  62. // Line progress: 500/4600 = 10.87%, File progress: 0/5 = 0% (file still processing)
  63. // Expected: (10.87 * 0.4) + (0 * 0.6) = 4.35%
  64. expectedPercentage := (float64(500)/float64(4600)*100)*0.4 + (0.0)*0.6
  65. if progress.Percentage < expectedPercentage-1 || progress.Percentage > expectedPercentage+1 {
  66. t.Errorf("Expected progress around %.2f%%, got %.2f%%", expectedPercentage, progress.Percentage)
  67. }
  68. // Complete first file
  69. tracker.CompleteFile("/var/log/nginx/access.log", 1000)
  70. progress = tracker.GetProgress()
  71. if progress.CompletedFiles != 1 {
  72. t.Errorf("Expected 1 completed file, got %d", progress.CompletedFiles)
  73. }
  74. // Complete remaining files
  75. tracker.StartFile("/var/log/nginx/access.log.1")
  76. tracker.CompleteFile("/var/log/nginx/access.log.1", 2000)
  77. tracker.StartFile("/var/log/nginx/access.log.2.gz")
  78. tracker.CompleteFile("/var/log/nginx/access.log.2.gz", 500)
  79. tracker.StartFile("/var/log/nginx/access.1.log")
  80. tracker.CompleteFile("/var/log/nginx/access.1.log", 800)
  81. tracker.StartFile("/var/log/nginx/access.2.log.gz")
  82. tracker.CompleteFile("/var/log/nginx/access.2.log.gz", 300)
  83. // Wait a bit for notifications
  84. time.Sleep(10 * time.Millisecond)
  85. // Check final progress
  86. progress = tracker.GetProgress()
  87. if progress.Percentage != 100 {
  88. t.Errorf("Expected 100%% progress when all files complete, got %.2f%%", progress.Percentage)
  89. }
  90. if progress.CompletedFiles != 5 {
  91. t.Errorf("Expected 5 completed files, got %d", progress.CompletedFiles)
  92. }
  93. if !progress.IsCompleted {
  94. t.Errorf("Expected IsCompleted to be true")
  95. }
  96. // Verify notifications were sent
  97. mu.Lock()
  98. if len(completionNotifications) != 1 {
  99. t.Errorf("Expected 1 completion notification, got %d", len(completionNotifications))
  100. }
  101. mu.Unlock()
  102. }
  103. func TestProgressTracker_FileFailure(t *testing.T) {
  104. var completionNotifications []CompletionNotification
  105. var mu sync.Mutex
  106. config := &ProgressConfig{
  107. OnCompletion: func(cn CompletionNotification) {
  108. mu.Lock()
  109. completionNotifications = append(completionNotifications, cn)
  110. mu.Unlock()
  111. },
  112. }
  113. tracker := NewProgressTracker("/var/log/nginx/test.log", config)
  114. // Add files
  115. tracker.AddFile("/var/log/nginx/access.log", false)
  116. tracker.AddFile("/var/log/nginx/error.log", false)
  117. // Start processing
  118. tracker.StartFile("/var/log/nginx/access.log")
  119. tracker.CompleteFile("/var/log/nginx/access.log", 100)
  120. tracker.StartFile("/var/log/nginx/error.log")
  121. tracker.FailFile("/var/log/nginx/error.log", "permission denied")
  122. // Wait for notifications
  123. time.Sleep(10 * time.Millisecond)
  124. // Check progress
  125. progress := tracker.GetProgress()
  126. if progress.FailedFiles != 1 {
  127. t.Errorf("Expected 1 failed file, got %d", progress.FailedFiles)
  128. }
  129. if progress.CompletedFiles != 1 {
  130. t.Errorf("Expected 1 completed file, got %d", progress.CompletedFiles)
  131. }
  132. if !progress.IsCompleted {
  133. t.Errorf("Expected IsCompleted to be true")
  134. }
  135. // Verify completion notification
  136. mu.Lock()
  137. if len(completionNotifications) != 1 {
  138. t.Errorf("Expected 1 completion notification, got %d", len(completionNotifications))
  139. }
  140. if len(completionNotifications) > 0 && completionNotifications[0].Success {
  141. t.Errorf("Expected completion notification to indicate failure")
  142. }
  143. mu.Unlock()
  144. }
  145. func TestProgressTracker_GetFileProgress(t *testing.T) {
  146. tracker := NewProgressTracker("/var/log/nginx/test.log", nil)
  147. // Add a file
  148. tracker.AddFile("/var/log/nginx/access.log", false)
  149. tracker.SetFileEstimate("/var/log/nginx/access.log", 1000)
  150. tracker.SetFileSize("/var/log/nginx/access.log", 150000)
  151. // Get file progress
  152. fileProgress, exists := tracker.GetFileProgress("/var/log/nginx/access.log")
  153. if !exists {
  154. t.Error("Expected file progress to exist")
  155. }
  156. if fileProgress.EstimatedLines != 1000 {
  157. t.Errorf("Expected 1000 estimated lines, got %d", fileProgress.EstimatedLines)
  158. }
  159. if fileProgress.FileSize != 150000 {
  160. t.Errorf("Expected file size 150000, got %d", fileProgress.FileSize)
  161. }
  162. // Test non-existent file
  163. _, exists = tracker.GetFileProgress("/nonexistent.log")
  164. if exists {
  165. t.Error("Expected file progress to not exist for non-existent file")
  166. }
  167. }
  168. func TestProgressTracker_GetAllFiles(t *testing.T) {
  169. tracker := NewProgressTracker("/var/log/nginx/test.log", nil)
  170. // Add multiple files
  171. files := []string{
  172. "/var/log/nginx/access.log",
  173. "/var/log/nginx/error.log",
  174. "/var/log/nginx/ssl.log",
  175. }
  176. for _, file := range files {
  177. tracker.AddFile(file, false)
  178. }
  179. allFiles := tracker.GetAllFiles()
  180. if len(allFiles) != 3 {
  181. t.Errorf("Expected 3 files, got %d", len(allFiles))
  182. }
  183. for _, file := range files {
  184. if _, exists := allFiles[file]; !exists {
  185. t.Errorf("Expected file %s to exist in all files", file)
  186. }
  187. }
  188. }
  189. func TestProgressTracker_Cancel(t *testing.T) {
  190. var completionNotifications []CompletionNotification
  191. var mu sync.Mutex
  192. config := &ProgressConfig{
  193. OnCompletion: func(cn CompletionNotification) {
  194. mu.Lock()
  195. completionNotifications = append(completionNotifications, cn)
  196. mu.Unlock()
  197. },
  198. }
  199. tracker := NewProgressTracker("/var/log/nginx/test.log", config)
  200. // Add files
  201. tracker.AddFile("/var/log/nginx/access.log", false)
  202. tracker.AddFile("/var/log/nginx/error.log", false)
  203. // Start one file
  204. tracker.StartFile("/var/log/nginx/access.log")
  205. // Cancel the tracker
  206. tracker.Cancel("user requested cancellation")
  207. // Wait for notifications
  208. time.Sleep(10 * time.Millisecond)
  209. // Check that all files are marked as failed
  210. allFiles := tracker.GetAllFiles()
  211. for _, file := range allFiles {
  212. if file.State != FileStateFailed {
  213. t.Errorf("Expected file %s to be in failed state after cancellation", file.FilePath)
  214. }
  215. }
  216. // Check completion
  217. if !tracker.IsCompleted() {
  218. t.Error("Expected tracker to be completed after cancellation")
  219. }
  220. // Verify completion notification
  221. mu.Lock()
  222. if len(completionNotifications) != 1 {
  223. t.Errorf("Expected 1 completion notification after cancellation, got %d", len(completionNotifications))
  224. }
  225. mu.Unlock()
  226. }
  227. func TestProgressTracker_NoNotificationWithoutConfig(t *testing.T) {
  228. // Create tracker without notification config
  229. tracker := NewProgressTracker("/var/log/nginx/test.log", nil)
  230. // Add and complete a file
  231. tracker.AddFile("/var/log/nginx/access.log", false)
  232. tracker.StartFile("/var/log/nginx/access.log")
  233. tracker.CompleteFile("/var/log/nginx/access.log", 100)
  234. // Should not panic or cause issues
  235. progress := tracker.GetProgress()
  236. if !progress.IsCompleted {
  237. t.Error("Expected tracker to be completed")
  238. }
  239. }
  240. func TestProgressTracker_DuplicateCompletion(t *testing.T) {
  241. var completionNotifications []CompletionNotification
  242. var mu sync.Mutex
  243. config := &ProgressConfig{
  244. OnCompletion: func(cn CompletionNotification) {
  245. mu.Lock()
  246. completionNotifications = append(completionNotifications, cn)
  247. mu.Unlock()
  248. },
  249. }
  250. tracker := NewProgressTracker("/var/log/nginx/test.log", config)
  251. // Add a file
  252. tracker.AddFile("/var/log/nginx/access.log", false)
  253. tracker.StartFile("/var/log/nginx/access.log")
  254. // Complete the file multiple times
  255. tracker.CompleteFile("/var/log/nginx/access.log", 100)
  256. tracker.CompleteFile("/var/log/nginx/access.log", 100)
  257. tracker.CompleteFile("/var/log/nginx/access.log", 100)
  258. // Wait for notifications
  259. time.Sleep(10 * time.Millisecond)
  260. // Should only get one completion notification
  261. mu.Lock()
  262. if len(completionNotifications) != 1 {
  263. t.Errorf("Expected 1 completion notification despite multiple complete calls, got %d", len(completionNotifications))
  264. }
  265. mu.Unlock()
  266. }
  267. func TestProgressTracker_NotificationThrottling(t *testing.T) {
  268. var progressNotifications []ProgressNotification
  269. var mu sync.Mutex
  270. config := &ProgressConfig{
  271. NotifyInterval: 100 * time.Millisecond, // Custom throttle interval
  272. OnProgress: func(pn ProgressNotification) {
  273. mu.Lock()
  274. progressNotifications = append(progressNotifications, pn)
  275. mu.Unlock()
  276. },
  277. }
  278. tracker := NewProgressTracker("/var/log/nginx/test.log", config)
  279. // Add a file
  280. tracker.AddFile("/var/log/nginx/access.log", false)
  281. tracker.SetFileEstimate("/var/log/nginx/access.log", 1000)
  282. tracker.StartFile("/var/log/nginx/access.log")
  283. // Send multiple rapid updates
  284. for i := 0; i < 10; i++ {
  285. tracker.UpdateFileProgress("/var/log/nginx/access.log", int64(i*10))
  286. time.Sleep(10 * time.Millisecond) // Faster than throttle interval
  287. }
  288. // Should have throttled notifications
  289. mu.Lock()
  290. notificationCount := len(progressNotifications)
  291. mu.Unlock()
  292. if notificationCount > 5 { // Should be significantly less than 10 due to throttling
  293. t.Errorf("Expected notifications to be throttled, got %d notifications", notificationCount)
  294. }
  295. }
  296. func TestEstimateFileLines(t *testing.T) {
  297. ctx := context.Background()
  298. // Test zero size file
  299. lines, err := EstimateFileLines(ctx, "/nonexistent/file", 0, false)
  300. if err != nil {
  301. t.Errorf("Expected no error for zero size file, got %v", err)
  302. }
  303. if lines != 0 {
  304. t.Errorf("Expected 0 lines for zero size file, got %d", lines)
  305. }
  306. // Test fallback for non-existent file
  307. lines, err = EstimateFileLines(ctx, "/nonexistent/file", 10000, false)
  308. if err != nil {
  309. t.Errorf("Expected no error for fallback estimation, got %v", err)
  310. }
  311. expected := int64(10000 / 150) // fallback estimate
  312. if lines != expected {
  313. t.Errorf("Expected %d lines for fallback estimate, got %d", expected, lines)
  314. }
  315. // Test context cancellation - since EstimateFileLines returns fallback for non-existent files,
  316. // we test cancellation by using a valid path but cancelling the context
  317. // For a non-existent file, it returns immediately with fallback, so no cancellation check
  318. cancelCtx, cancel := context.WithCancel(ctx)
  319. cancel() // Cancel immediately
  320. // For non-existent files, EstimateFileLines returns fallback estimate without error
  321. lines, err = EstimateFileLines(cancelCtx, "/nonexistent/file", 10000, false)
  322. if err != nil {
  323. t.Errorf("Expected no error for non-existent file even with cancelled context, got %v", err)
  324. }
  325. if lines != 10000/150 {
  326. t.Errorf("Expected fallback estimate for cancelled context with non-existent file")
  327. }
  328. }
  329. func TestEstimateFileLines_RealFile(t *testing.T) {
  330. // Create a temporary file with known content
  331. tmpDir := t.TempDir()
  332. testFile := filepath.Join(tmpDir, "test.log")
  333. // Create test content with known line count
  334. content := ""
  335. lineCount := 100
  336. for i := 0; i < lineCount; i++ {
  337. content += "This is a test log line with some content to make it realistic\n"
  338. }
  339. err := os.WriteFile(testFile, []byte(content), 0644)
  340. if err != nil {
  341. t.Fatalf("Failed to create test file: %v", err)
  342. }
  343. fileInfo, err := os.Stat(testFile)
  344. if err != nil {
  345. t.Fatalf("Failed to stat test file: %v", err)
  346. }
  347. ctx := context.Background()
  348. estimatedLines, err := EstimateFileLines(ctx, testFile, fileInfo.Size(), false)
  349. if err != nil {
  350. t.Errorf("Expected no error for real file estimation, got %v", err)
  351. }
  352. // Allow some tolerance in estimation (within 20% of actual)
  353. tolerance := float64(lineCount) * 0.2
  354. if float64(estimatedLines) < float64(lineCount)-tolerance ||
  355. float64(estimatedLines) > float64(lineCount)+tolerance {
  356. t.Errorf("Estimated lines %d not within tolerance of actual lines %d", estimatedLines, lineCount)
  357. }
  358. }
  359. func TestProgressManager(t *testing.T) {
  360. manager := NewProgressManager()
  361. // Test getting new tracker
  362. config := &ProgressConfig{}
  363. tracker1 := manager.GetTracker("/var/log/nginx/access.log", config)
  364. if tracker1 == nil {
  365. t.Error("Expected non-nil tracker")
  366. }
  367. // Test getting same tracker
  368. tracker2 := manager.GetTracker("/var/log/nginx/access.log", config)
  369. if tracker1 != tracker2 {
  370. t.Error("Expected same tracker instance for same path")
  371. }
  372. // Test getting different tracker
  373. tracker3 := manager.GetTracker("/var/log/nginx/error.log", config)
  374. if tracker1 == tracker3 {
  375. t.Error("Expected different tracker instance for different path")
  376. }
  377. // Test getting all trackers
  378. allTrackers := manager.GetAllTrackers()
  379. if len(allTrackers) != 2 {
  380. t.Errorf("Expected 2 trackers, got %d", len(allTrackers))
  381. }
  382. // Test removing tracker
  383. manager.RemoveTracker("/var/log/nginx/access.log")
  384. allTrackers = manager.GetAllTrackers()
  385. if len(allTrackers) != 1 {
  386. t.Errorf("Expected 1 tracker after removal, got %d", len(allTrackers))
  387. }
  388. // Test getting tracker after removal creates new one
  389. tracker4 := manager.GetTracker("/var/log/nginx/access.log", config)
  390. if tracker1 == tracker4 {
  391. t.Error("Expected new tracker instance after removal")
  392. }
  393. }
  394. func TestProgressManager_Cleanup(t *testing.T) {
  395. manager := NewProgressManager()
  396. // Add trackers
  397. config := &ProgressConfig{}
  398. tracker1 := manager.GetTracker("/var/log/nginx/access.log", config)
  399. tracker2 := manager.GetTracker("/var/log/nginx/error.log", config)
  400. // Complete one tracker
  401. tracker1.AddFile("/var/log/nginx/access.log", false)
  402. tracker1.StartFile("/var/log/nginx/access.log")
  403. tracker1.CompleteFile("/var/log/nginx/access.log", 100)
  404. // Leave the other incomplete
  405. tracker2.AddFile("/var/log/nginx/error.log", false)
  406. tracker2.StartFile("/var/log/nginx/error.log")
  407. // Cleanup
  408. manager.Cleanup()
  409. // Should have removed completed tracker
  410. allTrackers := manager.GetAllTrackers()
  411. if len(allTrackers) != 1 {
  412. t.Errorf("Expected 1 tracker after cleanup, got %d", len(allTrackers))
  413. }
  414. // Remaining tracker should be the incomplete one
  415. if _, exists := allTrackers["/var/log/nginx/error.log"]; !exists {
  416. t.Error("Expected incomplete tracker to remain after cleanup")
  417. }
  418. }
  419. func TestFileState_String(t *testing.T) {
  420. tests := []struct {
  421. state FileState
  422. expected string
  423. }{
  424. {FileStatePending, "pending"},
  425. {FileStateProcessing, "processing"},
  426. {FileStateCompleted, "completed"},
  427. {FileStateFailed, "failed"},
  428. {FileState(999), "unknown"},
  429. }
  430. for _, test := range tests {
  431. if test.state.String() != test.expected {
  432. t.Errorf("Expected %s, got %s", test.expected, test.state.String())
  433. }
  434. }
  435. }
  436. func TestProgressTracker_ConcurrentAccess(t *testing.T) {
  437. tracker := NewProgressTracker("/var/log/nginx/test.log", nil)
  438. // Add multiple files
  439. for i := 0; i < 10; i++ {
  440. tracker.AddFile(filepath.Join("/var/log/nginx", "access"+string(rune(i))+".log"), false)
  441. tracker.SetFileEstimate(filepath.Join("/var/log/nginx", "access"+string(rune(i))+".log"), 1000)
  442. }
  443. var wg sync.WaitGroup
  444. // Simulate concurrent progress updates
  445. for i := 0; i < 10; i++ {
  446. wg.Add(1)
  447. go func(fileIndex int) {
  448. defer wg.Done()
  449. fileName := filepath.Join("/var/log/nginx", "access"+string(rune(fileIndex))+".log")
  450. tracker.StartFile(fileName)
  451. for j := 0; j < 100; j++ {
  452. tracker.UpdateFileProgress(fileName, int64(j*10))
  453. time.Sleep(time.Millisecond)
  454. }
  455. tracker.CompleteFile(fileName, 1000)
  456. }(i)
  457. }
  458. // Simulate concurrent progress reads
  459. for i := 0; i < 5; i++ {
  460. wg.Add(1)
  461. go func() {
  462. defer wg.Done()
  463. for j := 0; j < 50; j++ {
  464. _ = tracker.GetProgress()
  465. _ = tracker.GetAllFiles()
  466. time.Sleep(time.Millisecond)
  467. }
  468. }()
  469. }
  470. wg.Wait()
  471. // Verify final state
  472. progress := tracker.GetProgress()
  473. if progress.CompletedFiles != 10 {
  474. t.Errorf("Expected 10 completed files after concurrent access, got %d", progress.CompletedFiles)
  475. }
  476. if !progress.IsCompleted {
  477. t.Error("Expected tracker to be completed after concurrent access")
  478. }
  479. }
  480. func TestRotationLogSupport(t *testing.T) {
  481. // Test compression detection
  482. testCases := []struct {
  483. filePath string
  484. compressed bool
  485. }{
  486. {"/var/log/nginx/access.log", false},
  487. {"/var/log/nginx/access.log.1", false},
  488. {"/var/log/nginx/access.log.2.gz", true},
  489. {"/var/log/nginx/access.1.log", false},
  490. {"/var/log/nginx/access.2.log.gz", true},
  491. {"/var/log/nginx/access.3.log.bz2", true},
  492. {"/var/log/nginx/error.log", false},
  493. {"/var/log/nginx/error.log.1.xz", true},
  494. {"/var/log/nginx/error.1.log.lz4", true},
  495. }
  496. for _, tc := range testCases {
  497. isCompressed := IsCompressedFile(tc.filePath)
  498. if isCompressed != tc.compressed {
  499. t.Errorf("IsCompressedFile(%s) = %v, expected %v", tc.filePath, isCompressed, tc.compressed)
  500. }
  501. }
  502. // Test rotation log detection
  503. rotationTests := []struct {
  504. filePath string
  505. isRotation bool
  506. }{
  507. {"/var/log/nginx/access.log", true},
  508. {"/var/log/nginx/access.log.1", true},
  509. {"/var/log/nginx/access.log.2.gz", true},
  510. {"/var/log/nginx/access.1.log", true},
  511. {"/var/log/nginx/access.2.log.gz", true},
  512. {"/var/log/nginx/error.log", true},
  513. {"/var/log/nginx/error.log.10", true},
  514. {"/var/log/nginx/error.1.log", true},
  515. {"/var/log/nginx/not-a-log.txt", false},
  516. {"/var/log/nginx/access.json", false},
  517. {"/var/log/nginx/config.conf", false},
  518. }
  519. for _, tc := range rotationTests {
  520. isRotation := IsRotationLogFile(tc.filePath)
  521. if isRotation != tc.isRotation {
  522. t.Errorf("IsRotationLogFile(%s) = %v, expected %v", tc.filePath, isRotation, tc.isRotation)
  523. }
  524. }
  525. // Test AddRotationFiles convenience method
  526. tracker := NewProgressTracker("/var/log/nginx/access.log", nil)
  527. // Add multiple rotation files at once
  528. rotationFiles := []string{
  529. "/var/log/nginx/access.log",
  530. "/var/log/nginx/access.log.1",
  531. "/var/log/nginx/access.log.2.gz",
  532. "/var/log/nginx/access.1.log",
  533. "/var/log/nginx/access.2.log.gz",
  534. }
  535. tracker.AddRotationFiles(rotationFiles...)
  536. progress := tracker.GetProgress()
  537. if progress.TotalFiles != 5 {
  538. t.Errorf("Expected 5 files after AddRotationFiles, got %d", progress.TotalFiles)
  539. }
  540. // Verify compression was detected correctly
  541. files := tracker.GetAllFiles()
  542. expectedCompression := map[string]bool{
  543. "/var/log/nginx/access.log": false,
  544. "/var/log/nginx/access.log.1": false,
  545. "/var/log/nginx/access.log.2.gz": true,
  546. "/var/log/nginx/access.1.log": false,
  547. "/var/log/nginx/access.2.log.gz": true,
  548. }
  549. for filePath, expectedComp := range expectedCompression {
  550. found := false
  551. for _, file := range files {
  552. if file.FilePath == filePath {
  553. found = true
  554. if file.IsCompressed != expectedComp {
  555. t.Errorf("File %s: expected compressed=%v, got compressed=%v",
  556. filePath, expectedComp, file.IsCompressed)
  557. }
  558. break
  559. }
  560. }
  561. if !found {
  562. t.Errorf("File %s not found in tracker", filePath)
  563. }
  564. }
  565. }