unarr/internal/engine/manager_test.go
Deivid Soto d0dbfc3d12 fix(ci): fix lint errors and pin CI to Go 1.25
- Run gofmt on all files
- Export SetupUPnP to fix unused lint error
- Remove Go 1.26 from CI matrix (only test with 1.25)
2026-03-31 22:15:12 +02:00

306 lines
7.9 KiB
Go

package engine
import (
"context"
"os"
"testing"
"time"
"github.com/torrentclaw/unarr/internal/agent"
)
func TestManagerSubmitAndWait(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
dl := &mockDownloader{method: MethodTorrent, available: true}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 2,
OutputDir: t.TempDir(),
}, reporter, dl)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go reporter.Run(ctx)
mgr.Submit(ctx, agent.Task{
ID: "test-task-1",
InfoHash: "abc123def456abc123def456abc123def456abc1",
Title: "Test Movie",
PreferredMethod: "torrent",
})
mgr.Wait()
// Task should have been processed (completed or failed depending on verify)
// Since mock returns a file that doesn't exist, it may fail at verify
// This is expected — we're testing the pipeline works
}
func TestManagerHasCapacity(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 2}, reporter)
if !mgr.HasCapacity() {
t.Error("new manager should have capacity")
}
}
func TestManagerActiveCount(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 3}, reporter)
if mgr.ActiveCount() != 0 {
t.Errorf("ActiveCount = %d, want 0", mgr.ActiveCount())
}
}
func TestManagerShutdown(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
dl := &mockDownloader{method: MethodTorrent, available: true}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 1,
OutputDir: t.TempDir(),
}, reporter, dl)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
mgr.Shutdown(ctx)
// Should not hang
}
func TestManagerDefaultConcurrency(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 0}, reporter)
if cap(mgr.sem) != 3 {
t.Errorf("default MaxConcurrent should be 3, got %d", cap(mgr.sem))
}
}
func TestManagerGetTask(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 2}, reporter)
// No task added
if task := mgr.GetTask("nonexistent"); task != nil {
t.Error("expected nil for nonexistent task")
}
}
func TestManagerActiveTasks(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 2}, reporter)
tasks := mgr.ActiveTasks()
if len(tasks) != 0 {
t.Errorf("expected 0 active tasks, got %d", len(tasks))
}
}
func TestManagerSubmitCompletesWithValidFile(t *testing.T) {
dir := t.TempDir()
// Create a file that verify() will accept
filePath := dir + "/movie.mkv"
os.WriteFile(filePath, make([]byte, 1024), 0o644)
reporter := &mockStatusReporter{}
pr := &ProgressReporter{
reporter: reporter,
interval: 100 * time.Millisecond,
latest: make(map[string]*Task),
lastReported: make(map[string]TaskStatus),
}
dl := &resultMockDownloader{
method: MethodTorrent,
result: &Result{
FilePath: filePath,
FileName: "movie.mkv",
Method: MethodTorrent,
Size: 1024,
},
}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 2,
OutputDir: dir,
}, pr, dl)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go pr.Run(ctx)
mgr.Submit(ctx, agent.Task{
ID: "task-complete-test1",
InfoHash: "abc123def456abc123def456abc123def456abc1",
Title: "Test Movie",
PreferredMethod: "torrent",
})
mgr.Wait()
cancel()
// Task should have completed successfully
// (we can't check directly since it's removed from active map after processing)
}
func TestManagerCancelTask(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
dl := &slowMockDownloader{method: MethodTorrent}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 2,
OutputDir: t.TempDir(),
}, reporter, dl)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go reporter.Run(ctx)
mgr.Submit(ctx, agent.Task{
ID: "task-cancel-test12",
InfoHash: "abc123def456abc123def456abc123def456abc1",
Title: "Cancel Me",
PreferredMethod: "torrent",
})
// Give it time to start
time.Sleep(100 * time.Millisecond)
mgr.CancelTask("task-cancel-test12")
mgr.Wait()
}
func TestManagerPauseTask(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
dl := &slowMockDownloader{method: MethodTorrent}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 2,
OutputDir: t.TempDir(),
}, reporter, dl)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go reporter.Run(ctx)
mgr.Submit(ctx, agent.Task{
ID: "task-pause-test123",
InfoHash: "abc123def456abc123def456abc123def456abc1",
Title: "Pause Me",
PreferredMethod: "torrent",
})
time.Sleep(100 * time.Millisecond)
mgr.PauseTask("task-pause-test123")
mgr.Wait()
}
func TestManagerCancelAndDeleteFiles(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
dl := &slowMockDownloader{method: MethodTorrent}
mgr := NewManager(ManagerConfig{
MaxConcurrent: 2,
OutputDir: t.TempDir(),
}, reporter, dl)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go reporter.Run(ctx)
mgr.Submit(ctx, agent.Task{
ID: "task-delfile-test12",
InfoHash: "abc123def456abc123def456abc123def456abc1",
Title: "Delete Me",
PreferredMethod: "torrent",
})
time.Sleep(100 * time.Millisecond)
mgr.CancelAndDeleteFiles("task-delfile-test12")
mgr.Wait()
}
func TestManagerCancelNonexistent(t *testing.T) {
reporter := NewProgressReporter(
agent.NewClient("http://localhost", "test", "test"),
1*time.Second,
)
mgr := NewManager(ManagerConfig{MaxConcurrent: 2}, reporter)
// Should not panic
mgr.CancelTask("nonexistent")
mgr.PauseTask("nonexistent")
mgr.CancelAndDeleteFiles("nonexistent")
}
// resultMockDownloader returns a configurable result
type resultMockDownloader struct {
method DownloadMethod
result *Result
}
func (m *resultMockDownloader) Method() DownloadMethod { return m.method }
func (m *resultMockDownloader) Available(_ context.Context, _ *Task) (bool, error) {
return true, nil
}
func (m *resultMockDownloader) Download(_ context.Context, _ *Task, _ string, _ chan<- Progress) (*Result, error) {
return m.result, nil
}
func (m *resultMockDownloader) Pause(_ string) error { return nil }
func (m *resultMockDownloader) Cancel(_ string) error { return nil }
func (m *resultMockDownloader) Shutdown(_ context.Context) error { return nil }
// slowMockDownloader blocks until context is cancelled
type slowMockDownloader struct {
method DownloadMethod
}
func (m *slowMockDownloader) Method() DownloadMethod { return m.method }
func (m *slowMockDownloader) Available(_ context.Context, _ *Task) (bool, error) {
return true, nil
}
func (m *slowMockDownloader) Download(ctx context.Context, _ *Task, _ string, _ chan<- Progress) (*Result, error) {
<-ctx.Done()
return nil, ctx.Err()
}
func (m *slowMockDownloader) Pause(_ string) error { return nil }
func (m *slowMockDownloader) Cancel(_ string) error { return nil }
func (m *slowMockDownloader) Shutdown(_ context.Context) error { return nil }