package progress import ( "testing" "time" ) func TestNewETAEstimator(t *testing.T) { estimator := NewETAEstimator("Test Operation", 10) if estimator.operation != "Test Operation" { t.Errorf("Expected operation 'Test Operation', got '%s'", estimator.operation) } if estimator.totalItems != 10 { t.Errorf("Expected totalItems 10, got %d", estimator.totalItems) } if estimator.itemsComplete != 0 { t.Errorf("Expected itemsComplete 0, got %d", estimator.itemsComplete) } if estimator.startTime.IsZero() { t.Error("Expected startTime to be set") } } func TestUpdateProgress(t *testing.T) { estimator := NewETAEstimator("Test", 10) estimator.UpdateProgress(5) if estimator.itemsComplete != 5 { t.Errorf("Expected itemsComplete 5, got %d", estimator.itemsComplete) } estimator.UpdateProgress(8) if estimator.itemsComplete != 8 { t.Errorf("Expected itemsComplete 8, got %d", estimator.itemsComplete) } } func TestGetProgress(t *testing.T) { estimator := NewETAEstimator("Test", 10) // Test 0% progress if progress := estimator.GetProgress(); progress != 0 { t.Errorf("Expected 0%%, got %.2f%%", progress) } // Test 50% progress estimator.UpdateProgress(5) if progress := estimator.GetProgress(); progress != 50.0 { t.Errorf("Expected 50%%, got %.2f%%", progress) } // Test 100% progress estimator.UpdateProgress(10) if progress := estimator.GetProgress(); progress != 100.0 { t.Errorf("Expected 100%%, got %.2f%%", progress) } // Test zero division zeroEstimator := NewETAEstimator("Test", 0) if progress := zeroEstimator.GetProgress(); progress != 0 { t.Errorf("Expected 0%% for zero totalItems, got %.2f%%", progress) } } func TestGetElapsed(t *testing.T) { estimator := NewETAEstimator("Test", 10) // Wait a bit time.Sleep(100 * time.Millisecond) elapsed := estimator.GetElapsed() if elapsed < 100*time.Millisecond { t.Errorf("Expected elapsed time >= 100ms, got %v", elapsed) } } func TestGetETA(t *testing.T) { estimator := NewETAEstimator("Test", 10) // No progress yet, ETA should be 0 if eta := estimator.GetETA(); eta != 0 { t.Errorf("Expected ETA 0 for no progress, got %v", eta) } // Simulate 5 items completed in 5 seconds estimator.startTime = time.Now().Add(-5 * time.Second) estimator.UpdateProgress(5) eta := estimator.GetETA() // Should be approximately 5 seconds (5 items remaining at 1 sec/item) if eta < 4*time.Second || eta > 6*time.Second { t.Errorf("Expected ETA around 5s, got %v", eta) } } func TestFormatProgress(t *testing.T) { estimator := NewETAEstimator("Test", 13) // Test at 0% if result := estimator.FormatProgress(); result != "0/13 (0%)" { t.Errorf("Expected '0/13 (0%%)', got '%s'", result) } // Test at 38% estimator.UpdateProgress(5) if result := estimator.FormatProgress(); result != "5/13 (38%)" { t.Errorf("Expected '5/13 (38%%)', got '%s'", result) } // Test at 100% estimator.UpdateProgress(13) if result := estimator.FormatProgress(); result != "13/13 (100%)" { t.Errorf("Expected '13/13 (100%%)', got '%s'", result) } } func TestFormatDuration(t *testing.T) { tests := []struct { duration time.Duration expected string }{ {500 * time.Millisecond, "< 1s"}, {5 * time.Second, "5s"}, {65 * time.Second, "1m"}, // 5 seconds not shown (<=5) {125 * time.Second, "2m"}, // 5 seconds not shown (<=5) {3 * time.Minute, "3m"}, {3*time.Minute + 3*time.Second, "3m"}, // < 5 seconds not shown {3*time.Minute + 10*time.Second, "3m 10s"}, // > 5 seconds shown {90 * time.Minute, "1h 30m"}, {120 * time.Minute, "2h"}, {150 * time.Minute, "2h 30m"}, } for _, tt := range tests { result := FormatDuration(tt.duration) if result != tt.expected { t.Errorf("FormatDuration(%v) = '%s', expected '%s'", tt.duration, result, tt.expected) } } } func TestFormatETA(t *testing.T) { estimator := NewETAEstimator("Test", 10) // No progress - should show "calculating..." if result := estimator.FormatETA(); result != "calculating..." { t.Errorf("Expected 'calculating...', got '%s'", result) } // With progress estimator.startTime = time.Now().Add(-10 * time.Second) estimator.UpdateProgress(5) result := estimator.FormatETA() if result != "~10s remaining" { t.Errorf("Expected '~10s remaining', got '%s'", result) } } func TestFormatElapsed(t *testing.T) { estimator := NewETAEstimator("Test", 10) estimator.startTime = time.Now().Add(-45 * time.Second) result := estimator.FormatElapsed() if result != "45s" { t.Errorf("Expected '45s', got '%s'", result) } } func TestGetFullStatus(t *testing.T) { estimator := NewETAEstimator("Backing up cluster", 13) // Just started (0 items) result := estimator.GetFullStatus("Backing up cluster") if result != "Backing up cluster | 0/13 | Starting..." { t.Errorf("Unexpected result for 0 items: '%s'", result) } // With progress estimator.startTime = time.Now().Add(-30 * time.Second) estimator.UpdateProgress(5) result = estimator.GetFullStatus("Backing up cluster") // Should contain all components if len(result) < 50 { // Reasonable minimum length t.Errorf("Result too short: '%s'", result) } // Check it contains key elements (format may vary slightly) if !contains(result, "5/13") { t.Errorf("Result missing progress '5/13': '%s'", result) } if !contains(result, "38%") { t.Errorf("Result missing percentage '38%%': '%s'", result) } if !contains(result, "Elapsed:") { t.Errorf("Result missing 'Elapsed:': '%s'", result) } if !contains(result, "ETA:") { t.Errorf("Result missing 'ETA:': '%s'", result) } } func TestGetFullStatusWithZeroItems(t *testing.T) { estimator := NewETAEstimator("Test Operation", 0) estimator.startTime = time.Now().Add(-5 * time.Second) result := estimator.GetFullStatus("Test Operation") // Should only show elapsed time when no items to track if !contains(result, "Test Operation") || !contains(result, "Elapsed:") { t.Errorf("Unexpected result for 0 total items: '%s'", result) } if contains(result, "0/0") { t.Errorf("Should not show 0/0 progress: '%s'", result) } } func TestEstimateSizeBasedDuration(t *testing.T) { // Test 100MB with 1 core duration := EstimateSizeBasedDuration(100*1024*1024, 1) // Should be around 1.2 minutes (1 minute base + 20% buffer) if duration < 60*time.Second || duration > 90*time.Second { t.Errorf("Expected ~1.2 minutes for 100MB/1core, got %v", duration) } // Test 100MB with 8 cores (should be faster) duration8cores := EstimateSizeBasedDuration(100*1024*1024, 8) if duration8cores >= duration { t.Errorf("Expected faster with more cores: %v vs %v", duration8cores, duration) } // Test larger file duration1GB := EstimateSizeBasedDuration(1024*1024*1024, 1) if duration1GB <= duration { t.Errorf("Expected longer duration for larger file: %v vs %v", duration1GB, duration) } } // Helper function func contains(s, substr string) bool { return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && ( s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || indexHelper(s, substr) >= 0)) } func indexHelper(s, substr string) int { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return i } } return -1 }