fetch_ml/internal/queue/filesystem/queue_test.go
Jeremie Fraeys f827ee522a
test(tracking/plugins): add PodmanInterface and comprehensive plugin tests for 91% coverage
Refactor plugins to use interface for testability:
- Add PodmanInterface to container package (StartContainer, StopContainer, RemoveContainer)
- Update MLflow plugin to use container.PodmanInterface
- Update TensorBoard plugin to use container.PodmanInterface
- Add comprehensive mocked tests for all three plugins (wandb, mlflow, tensorboard)
- Coverage increased from 18% to 91.4%
2026-03-14 16:59:16 -04:00

353 lines
7.5 KiB
Go

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")
}