Add comprehensive unit tests for ETA estimator
- 12 test functions covering all estimator functionality - Tests for progress tracking, time calculations, formatting - Tests for edge cases (zero items, no progress, etc.) - All tests passing (12/12)
This commit is contained in:
259
internal/progress/estimator_test.go
Normal file
259
internal/progress/estimator_test.go
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user