package filesystem_test import ( "os" "path/filepath" "testing" "github.com/jfraeys/fetch_ml/internal/domain" "github.com/jfraeys/fetch_ml/internal/queue/filesystem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestNewQueue tests queue creation func TestNewQueue(t *testing.T) { t.Parallel() tmpDir := t.TempDir() queueRoot := filepath.Join(tmpDir, "queue") q, err := filesystem.NewQueue(queueRoot) require.NoError(t, err) require.NotNil(t, q) defer q.Close() // Verify directories were created for _, dir := range []string{"pending/entries", "running", "finished", "failed"} { path := filepath.Join(queueRoot, dir) info, err := os.Stat(path) require.NoError(t, err, "Directory %s should exist", dir) assert.True(t, info.IsDir()) } } // TestNewQueueEmptyRoot tests queue creation with empty root func TestNewQueueEmptyRoot(t *testing.T) { t.Parallel() _, err := filesystem.NewQueue("") require.Error(t, err) assert.Contains(t, err.Error(), "root is required") } // TestClose tests queue closing func TestClose(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) err = q.Close() require.NoError(t, err) } // TestAddTask tests adding tasks func TestAddTask(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "test-task-1", Status: "pending", Args: "test args", Output: "test output", } err = q.AddTask(task) require.NoError(t, err) // Verify file was created taskFile := filepath.Join(tmpDir, "pending", "entries", "test-task-1.json") _, err = os.Stat(taskFile) require.NoError(t, err) } // TestAddTaskNil tests adding nil task func TestAddTaskNil(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() err = q.AddTask(nil) require.Error(t, err) assert.Contains(t, err.Error(), "task is nil") } // TestAddTaskInvalidID tests adding task with invalid ID func TestAddTaskInvalidID(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "invalid/id/with/slashes", Status: "pending", } err = q.AddTask(task) require.Error(t, err) assert.Contains(t, err.Error(), "invalid task ID") } // TestGetTask tests retrieving tasks func TestGetTask(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "get-task-1", Status: "pending", Args: "test args", } err = q.AddTask(task) require.NoError(t, err) retrieved, err := q.GetTask("get-task-1") require.NoError(t, err) assert.Equal(t, task.ID, retrieved.ID) assert.Equal(t, task.Status, retrieved.Status) assert.Equal(t, task.Args, retrieved.Args) } // TestGetTaskNotFound tests retrieving nonexistent task func TestGetTaskNotFound(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() _, err = q.GetTask("nonexistent-task") require.Error(t, err) assert.Contains(t, err.Error(), "task not found") } // TestGetTaskInvalidID tests retrieving with invalid ID func TestGetTaskInvalidID(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() _, err = q.GetTask("invalid/id") require.Error(t, err) assert.Contains(t, err.Error(), "invalid task ID") } // TestListTasks tests listing all tasks func TestListTasks(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() // Add multiple tasks tasks := []*domain.Task{ {ID: "list-task-1", Status: "pending"}, {ID: "list-task-2", Status: "pending"}, {ID: "list-task-3", Status: "pending"}, } for _, task := range tasks { err := q.AddTask(task) require.NoError(t, err) } listed, err := q.ListTasks() require.NoError(t, err) assert.Len(t, listed, 3) } // TestListTasksEmpty tests listing with no tasks func TestListTasksEmpty(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() listed, err := q.ListTasks() require.NoError(t, err) assert.Empty(t, listed) } // TestCancelTask tests canceling tasks func TestCancelTask(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "cancel-task-1", Status: "pending", } err = q.AddTask(task) require.NoError(t, err) // Verify file exists taskFile := filepath.Join(tmpDir, "pending", "entries", "cancel-task-1.json") _, err = os.Stat(taskFile) require.NoError(t, err) // Cancel task err = q.CancelTask("cancel-task-1") require.NoError(t, err) // Verify file is gone _, err = os.Stat(taskFile) require.True(t, os.IsNotExist(err)) } // TestCancelTaskNotFound tests canceling nonexistent task func TestCancelTaskNotFound(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() // Should not error for nonexistent task err = q.CancelTask("nonexistent-task") require.NoError(t, err) } // TestCancelTaskInvalidID tests canceling with invalid ID func TestCancelTaskInvalidID(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() err = q.CancelTask("invalid/id") require.Error(t, err) assert.Contains(t, err.Error(), "invalid task ID") } // TestUpdateTask tests updating tasks func TestUpdateTask(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "update-task-1", Status: "pending", Args: "original args", } err = q.AddTask(task) require.NoError(t, err) // Update task updated := &domain.Task{ ID: "update-task-1", Status: "running", Args: "updated args", Output: "new output", } err = q.UpdateTask(updated) require.NoError(t, err) // Verify update retrieved, err := q.GetTask("update-task-1") require.NoError(t, err) assert.Equal(t, "running", retrieved.Status) assert.Equal(t, "updated args", retrieved.Args) assert.Equal(t, "new output", retrieved.Output) } // TestUpdateTaskNil tests updating with nil task func TestUpdateTaskNil(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() err = q.UpdateTask(nil) require.Error(t, err) assert.Contains(t, err.Error(), "task is nil") } // TestUpdateTaskNotFound tests updating nonexistent task func TestUpdateTaskNotFound(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "nonexistent-task", Status: "pending", } err = q.UpdateTask(task) require.Error(t, err) assert.Contains(t, err.Error(), "task not found") } // TestUpdateTaskInvalidID tests updating with invalid ID func TestUpdateTaskInvalidID(t *testing.T) { t.Parallel() tmpDir := t.TempDir() q, err := filesystem.NewQueue(tmpDir) require.NoError(t, err) defer q.Close() task := &domain.Task{ ID: "invalid/id", Status: "pending", } err = q.UpdateTask(task) require.Error(t, err) assert.Contains(t, err.Error(), "invalid task ID") }