//go:build linux || freebsd // +build linux freebsd package queue import ( "os" "path/filepath" "testing" ) func TestNewQueue(t *testing.T) { q, err := NewQueue(nil) if err != nil { t.Fatalf("Failed to create queue: %v", err) } if q == nil { t.Fatal("Queue is nil") } if q.config.MaxParallel != 3 { t.Errorf("Expected MaxParallel=3, got %d", q.config.MaxParallel) } } func TestQueueAdd(t *testing.T) { tmpDir := t.TempDir() queueFile := filepath.Join(tmpDir, "queue.json") q, err := NewQueue(&QueueConfig{QueueFile: queueFile, AutoSave: true}) if err != nil { t.Fatalf("Failed to create queue: %v", err) } item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) if item == nil { t.Fatal("Added item is nil") } if item.URL != "https://example.com/file.zip" { t.Errorf("Expected URL https://example.com/file.zip, got %s", item.URL) } if item.Priority != 5 { t.Errorf("Expected priority 5, got %d", item.Priority) } if item.Status != "pending" { t.Errorf("Expected status pending, got %s", item.Status) } } func TestQueueAddPriority(t *testing.T) { q, err := NewQueue(nil) if err != nil { t.Fatalf("Failed to create queue: %v", err) } // Test priority clamping item1 := q.Add("https://example.com/1.zip", "/tmp/1.zip", 0) if item1.Priority != 5 { t.Errorf("Expected priority 5 (default), got %d", item1.Priority) } item2 := q.Add("https://example.com/2.zip", "/tmp/2.zip", 15) if item2.Priority != 10 { t.Errorf("Expected priority 10 (max), got %d", item2.Priority) } } func TestQueueRemove(t *testing.T) { q, _ := NewQueue(nil) item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) initialLen := q.Len() if !q.Remove(item.ID) { t.Error("Failed to remove item") } if q.Len() != initialLen-1 { t.Errorf("Expected queue length %d, got %d", initialLen-1, q.Len()) } } func TestQueueGetNext(t *testing.T) { q, _ := NewQueue(nil) // Add items with different priorities q.Add("https://example.com/low.zip", "/tmp/low.zip", 3) q.Add("https://example.com/high.zip", "/tmp/high.zip", 10) q.Add("https://example.com/med.zip", "/tmp/med.zip", 5) next := q.GetNext() if next == nil { t.Fatal("GetNext returned nil") } // Should return highest priority (10) if next.Priority < 8 { t.Errorf("Expected high priority item (>=8), got %d", next.Priority) } } func TestQueueUpdateStatus(t *testing.T) { q, err := NewQueue(nil) if err != nil { t.Fatalf("Failed to create queue: %v", err) } item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) if !q.UpdateStatus(item.ID, "downloading") { t.Error("Failed to update status") } // Refresh item from queue items := q.GetAll() var updated *QueueItem for _, it := range items { if it.ID == item.ID { updated = it break } } if updated == nil || updated.Status != "downloading" { t.Errorf("Expected status downloading, got %s", updated.Status) } } func TestQueueGetPending(t *testing.T) { q, _ := NewQueue(nil) q.Add("https://example.com/1.zip", "/tmp/1.zip", 5) q.Add("https://example.com/2.zip", "/tmp/2.zip", 5) items := q.GetAll() if len(items) >= 2 { q.UpdateStatus(items[0].ID, "completed") } pending := q.GetPending() if len(pending) < 1 { t.Errorf("Expected at least 1 pending item, got %d", len(pending)) } } func TestQueueGetStats(t *testing.T) { q, _ := NewQueue(nil) initialLen := q.Len() q.Add("https://example.com/1.zip", "/tmp/1.zip", 5) q.Add("https://example.com/2.zip", "/tmp/2.zip", 5) q.Add("https://example.com/3.zip", "/tmp/3.zip", 5) items := q.GetAll() completedCount := 0 for _, item := range items { if item.Status == "pending" && completedCount == 0 { q.UpdateStatus(item.ID, "completed") completedCount++ } } stats := q.GetStats() if stats.Total < initialLen+3 { t.Errorf("Expected at least %d total, got %d", initialLen+3, stats.Total) } if stats.Completed < 1 { t.Errorf("Expected at least 1 completed, got %d", stats.Completed) } } func TestQueueSaveLoad(t *testing.T) { tmpDir := t.TempDir() queueFile := filepath.Join(tmpDir, "queue.json") // Create and save queue q1, err := NewQueue(&QueueConfig{QueueFile: queueFile, AutoSave: false}) if err != nil { t.Fatalf("Failed to create queue: %v", err) } q1.Add("https://example.com/file.zip", "/tmp/file.zip", 7) q1.Add("https://example.com/other.zip", "/tmp/other.zip", 3) if err := q1.Save(); err != nil { t.Fatalf("Failed to save queue: %v", err) } // Load queue q2, err := NewQueue(&QueueConfig{QueueFile: queueFile}) if err != nil { t.Fatalf("Failed to load queue: %v", err) } if q2.Len() != 2 { t.Errorf("Expected 2 items after load, got %d", q2.Len()) } } func TestQueueClearCompleted(t *testing.T) { q, _ := NewQueue(nil) initialLen := q.Len() q.Add("https://example.com/1.zip", "/tmp/1.zip", 5) q.Add("https://example.com/2.zip", "/tmp/2.zip", 5) q.Add("https://example.com/3.zip", "/tmp/3.zip", 5) items := q.GetAll() completedCount := 0 for _, item := range items { if item.Status == "pending" && completedCount == 0 { q.UpdateStatus(item.ID, "completed") completedCount++ } } count := q.ClearCompleted() if count < 1 { t.Errorf("Expected to clear at least 1 item, cleared %d", count) } if q.Len() >= initialLen+3 { t.Errorf("Expected less than %d items remaining, got %d", initialLen+3, q.Len()) } } func TestQueueRetry(t *testing.T) { q, err := NewQueue(nil) if err != nil { t.Fatalf("Failed to create queue: %v", err) } item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) q.UpdateStatus(item.ID, "failed") // Should be able to retry if !q.Retry(item.ID) { t.Error("Failed to retry item") } // Check status is back to pending items := q.GetAll() for _, it := range items { if it.ID == item.ID { if it.Status != "pending" { t.Errorf("Expected status pending after retry, got %s", it.Status) } break } } } func TestQueueRetryMaxRetries(t *testing.T) { q, err := NewQueue(&QueueConfig{DefaultMaxRetries: 2}) if err != nil { t.Fatalf("Failed to create queue: %v", err) } item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) q.UpdateStatus(item.ID, "failed") // First retry should work if !q.Retry(item.ID) { t.Error("First retry should succeed") } q.UpdateStatus(item.ID, "failed") // Second retry should work if !q.Retry(item.ID) { t.Error("Second retry should succeed") } q.UpdateStatus(item.ID, "failed") // Third retry should fail (max retries reached) if q.Retry(item.ID) { t.Error("Third retry should fail (max retries exceeded)") } } func TestGenerateID(t *testing.T) { id1 := generateID() id2 := generateID() if id1 == id2 { t.Error("Generated IDs should be unique") } // Check ID format if len(id1) < 3 { t.Errorf("ID too short: %s", id1) } } func TestDefaultQueueFile(t *testing.T) { path := getDefaultQueueFile() if path == "" { t.Error("Default queue file path should not be empty") } } func TestQueueConcurrentAccess(t *testing.T) { q, _ := NewQueue(nil) initialLen := q.Len() done := make(chan bool, 10) // Concurrent adds for i := 0; i < 10; i++ { go func(n int) { q.Add("https://example.com/"+string(rune('a'+n))+".zip", "/tmp/"+string(rune('a'+n))+".zip", 5) done <- true }(i) } for i := 0; i < 10; i++ { <-done } if q.Len() < initialLen+10 { t.Errorf("Expected at least %d items after concurrent adds, got %d", initialLen+10, q.Len()) } } func TestQueueAutoSave(t *testing.T) { tmpDir := t.TempDir() queueFile := filepath.Join(tmpDir, "queue.json") q, err := NewQueue(&QueueConfig{QueueFile: queueFile, AutoSave: true}) if err != nil { t.Fatalf("Failed to create queue: %v", err) } q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) // Check file exists if _, err := os.Stat(queueFile); os.IsNotExist(err) { t.Error("Queue file should exist with AutoSave enabled") } } func TestQueueProgressUpdate(t *testing.T) { q, err := NewQueue(nil) if err != nil { t.Fatalf("Failed to create queue: %v", err) } item := q.Add("https://example.com/file.zip", "/tmp/file.zip", 5) q.UpdateProgress(item.ID, 500, 1000) items := q.GetAll() for _, it := range items { if it.ID == item.ID { if it.Downloaded != 500 { t.Errorf("Expected downloaded 500, got %d", it.Downloaded) } if it.Size != 1000 { t.Errorf("Expected size 1000, got %d", it.Size) } break } } }