Add git implementation of task manager

Change-Id: I1e0925e54fa167af9459eceea7a2cae082bc4504
diff --git a/server/tm/git_tm/git_task_manager_test.go b/server/tm/git_tm/git_task_manager_test.go
new file mode 100644
index 0000000..cd37838
--- /dev/null
+++ b/server/tm/git_tm/git_task_manager_test.go
@@ -0,0 +1,1021 @@
+package git_tm
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/iomodo/staff/git"
+	"github.com/iomodo/staff/tm"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+// Test helper functions
+func setupTestDir(t *testing.T) (string, func()) {
+	tempDir, err := os.MkdirTemp("", "git-task-manager-test")
+	require.NoError(t, err)
+
+	cleanup := func() {
+		os.RemoveAll(tempDir)
+	}
+
+	return tempDir, cleanup
+}
+
+func createTestTaskManager(t *testing.T, repoPath string) (*GitTaskManager, git.GitInterface) {
+	// Initialize git repository
+	gitImpl := git.DefaultGit(repoPath)
+	ctx := context.Background()
+
+	err := gitImpl.Init(ctx, repoPath)
+	require.NoError(t, err)
+
+	// Set up git user config for commits
+	userConfig := git.UserConfig{
+		Name:  "Test User",
+		Email: "test@example.com",
+	}
+	err = gitImpl.SetUserConfig(ctx, userConfig)
+	require.NoError(t, err)
+
+	gtm := NewGitTaskManager(gitImpl, repoPath)
+	return gtm, gitImpl
+}
+
+// Test cases
+func TestNewGitTaskManager(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gitImpl := git.DefaultGit(tempDir)
+	gtm := NewGitTaskManager(gitImpl, tempDir)
+
+	assert.NotNil(t, gtm)
+	assert.Equal(t, gitImpl, gtm.git)
+	assert.Equal(t, tempDir, gtm.repoPath)
+	assert.Equal(t, filepath.Join(tempDir, "tasks"), gtm.tasksDir)
+}
+
+func TestEnsureTasksDir(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Test creating tasks directory
+	err := gtm.ensureTasksDir()
+	assert.NoError(t, err)
+
+	// Verify directory exists
+	_, err = os.Stat(gtm.tasksDir)
+	assert.NoError(t, err)
+
+	// Test creating again (should not error)
+	err = gtm.ensureTasksDir()
+	assert.NoError(t, err)
+}
+
+func TestGenerateTaskID(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	id1 := gtm.generateTaskID()
+	id2 := gtm.generateTaskID()
+
+	assert.NotEmpty(t, id1)
+	assert.NotEmpty(t, id2)
+	assert.NotEqual(t, id1, id2)
+	assert.Contains(t, id1, "task-")
+}
+
+func TestTaskToMarkdown(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	now := time.Now()
+	dueDate := now.Add(24 * time.Hour)
+	completedAt := now.Add(12 * time.Hour)
+
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "This is a test task",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:      tm.StatusToDo,
+		Priority:    tm.PriorityHigh,
+		CreatedAt:   now,
+		UpdatedAt:   now,
+		DueDate:     &dueDate,
+		CompletedAt: &completedAt,
+	}
+
+	markdown, err := gtm.taskToMarkdown(task)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, markdown)
+	assert.Contains(t, markdown, "---")
+	assert.Contains(t, markdown, "id: test-task-123")
+	assert.Contains(t, markdown, "title: Test Task")
+	assert.Contains(t, markdown, "description: This is a test task")
+	assert.Contains(t, markdown, "owner_id: user123")
+	assert.Contains(t, markdown, "owner_name: Test User")
+	assert.Contains(t, markdown, "status: todo")
+	assert.Contains(t, markdown, "priority: high")
+	assert.Contains(t, markdown, "# Task Description")
+	assert.Contains(t, markdown, "This is a test task")
+}
+
+func TestParseTaskFromMarkdown(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	markdown := `---
+id: test-task-123
+title: Test Task
+description: This is a test task
+owner_id: user123
+owner_name: Test User
+status: todo
+priority: high
+created_at: 2023-01-01T00:00:00Z
+updated_at: 2023-01-01T00:00:00Z
+due_date: 2023-01-02T00:00:00Z
+completed_at: 2023-01-01T12:00:00Z
+---
+
+# Task Description
+
+This is a test task
+`
+
+	task, err := gtm.parseTaskFromMarkdown(markdown)
+	assert.NoError(t, err)
+	assert.NotNil(t, task)
+	assert.Equal(t, "test-task-123", task.ID)
+	assert.Equal(t, "Test Task", task.Title)
+	assert.Equal(t, "This is a test task", task.Description)
+	assert.Equal(t, "user123", task.Owner.ID)
+	assert.Equal(t, "Test User", task.Owner.Name)
+	assert.Equal(t, tm.StatusToDo, task.Status)
+	assert.Equal(t, tm.PriorityHigh, task.Priority)
+}
+
+func TestParseTaskFromMarkdownInvalid(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Test invalid markdown format
+	invalidMarkdown := "This is not valid markdown"
+
+	task, err := gtm.parseTaskFromMarkdown(invalidMarkdown)
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Contains(t, err.Error(), "invalid markdown format")
+}
+
+func TestWriteAndReadTaskFile(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Ensure tasks directory exists
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	// Create test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "This is a test task",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityHigh,
+		CreatedAt: time.Now(),
+		UpdatedAt: time.Now(),
+	}
+
+	// Write task file
+	err = gtm.writeTaskFile(task)
+	assert.NoError(t, err)
+
+	// Verify file exists
+	filePath := filepath.Join(gtm.tasksDir, task.ID+".md")
+	_, err = os.Stat(filePath)
+	assert.NoError(t, err)
+
+	// Read task file
+	readTask, err := gtm.readTaskFile(task.ID)
+	assert.NoError(t, err)
+	assert.NotNil(t, readTask)
+	assert.Equal(t, task.ID, readTask.ID)
+	assert.Equal(t, task.Title, readTask.Title)
+	assert.Equal(t, task.Description, readTask.Description)
+	assert.Equal(t, task.Owner.ID, readTask.Owner.ID)
+	assert.Equal(t, task.Owner.Name, readTask.Owner.Name)
+	assert.Equal(t, task.Status, readTask.Status)
+	assert.Equal(t, task.Priority, readTask.Priority)
+}
+
+func TestReadTaskFileNotFound(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Try to read non-existent task
+	task, err := gtm.readTaskFile("non-existent-task")
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Equal(t, tm.ErrTaskNotFound, err)
+}
+
+func TestListTaskFiles(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Ensure tasks directory exists
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	// Create some test task files
+	taskIDs := []string{"task-1", "task-2", "task-3"}
+	for _, id := range taskIDs {
+		task := &tm.Task{
+			ID:          id,
+			Title:       "Test Task " + id,
+			Description: "Test task description",
+			Owner: tm.Owner{
+				ID:   "user123",
+				Name: "Test User",
+			},
+			Status:    tm.StatusToDo,
+			Priority:  tm.PriorityMedium,
+			CreatedAt: time.Now(),
+			UpdatedAt: time.Now(),
+		}
+		err = gtm.writeTaskFile(task)
+		require.NoError(t, err)
+	}
+
+	// Create a non-task file
+	nonTaskFile := filepath.Join(gtm.tasksDir, "readme.txt")
+	err = os.WriteFile(nonTaskFile, []byte("This is not a task"), 0644)
+	require.NoError(t, err)
+
+	// List task files
+	taskFiles, err := gtm.listTaskFiles()
+	assert.NoError(t, err)
+	assert.Len(t, taskFiles, 3)
+
+	// Verify all task IDs are present
+	for _, id := range taskIDs {
+		assert.Contains(t, taskFiles, id)
+	}
+}
+
+func TestListTaskFilesEmpty(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// List task files in non-existent directory
+	taskFiles, err := gtm.listTaskFiles()
+	assert.NoError(t, err)
+	assert.Empty(t, taskFiles)
+}
+
+func TestCommitTaskChange(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, gitImpl := createTestTaskManager(t, tempDir)
+
+	// Create a test task file first
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now(),
+		UpdatedAt: time.Now(),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	// Test successful commit
+	err = gtm.commitTaskChange("test-task-123", "created")
+	assert.NoError(t, err)
+
+	// Verify commit was created
+	ctx := context.Background()
+	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
+	assert.NoError(t, err)
+	if len(commits) > 0 {
+		assert.Contains(t, commits[0].Message, "test-task-123")
+		assert.Contains(t, commits[0].Message, "created")
+	}
+}
+
+func TestCreateTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, gitImpl := createTestTaskManager(t, tempDir)
+
+	ctx := context.Background()
+	req := &tm.TaskCreateRequest{
+		Title:       "New Test Task",
+		Description: "This is a new test task",
+		OwnerID:     "user123",
+		Priority:    tm.PriorityHigh,
+	}
+
+	task, err := gtm.CreateTask(ctx, req)
+	assert.NoError(t, err)
+	assert.NotNil(t, task)
+
+	// Verify task properties
+	assert.NotEmpty(t, task.ID)
+	assert.Contains(t, task.ID, "task-")
+	assert.Equal(t, req.Title, task.Title)
+	assert.Equal(t, req.Description, task.Description)
+	assert.Equal(t, req.OwnerID, task.Owner.ID)
+	assert.Equal(t, req.OwnerID, task.Owner.Name) // TODO: Should look up actual name
+	assert.Equal(t, tm.StatusToDo, task.Status)
+	assert.Equal(t, req.Priority, task.Priority)
+	assert.False(t, task.CreatedAt.IsZero())
+	assert.False(t, task.UpdatedAt.IsZero())
+
+	// Verify git commit was created
+	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
+	assert.NoError(t, err)
+	if len(commits) > 0 {
+		assert.Contains(t, commits[0].Message, task.ID)
+		assert.Contains(t, commits[0].Message, "created")
+	}
+}
+
+func TestCreateTaskInvalidData(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	ctx := context.Background()
+
+	// Test empty title
+	req := &tm.TaskCreateRequest{
+		Title:   "",
+		OwnerID: "user123",
+	}
+
+	task, err := gtm.CreateTask(ctx, req)
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Equal(t, tm.ErrInvalidTaskData, err)
+
+	// Test empty owner ID
+	req = &tm.TaskCreateRequest{
+		Title:   "Valid Title",
+		OwnerID: "",
+	}
+
+	task, err = gtm.CreateTask(ctx, req)
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Equal(t, tm.ErrInvalidOwner, err)
+}
+
+func TestGetTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test task description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now(),
+		UpdatedAt: time.Now(),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	// Get the task
+	ctx := context.Background()
+	retrievedTask, err := gtm.GetTask(ctx, task.ID)
+	assert.NoError(t, err)
+	assert.NotNil(t, retrievedTask)
+	assert.Equal(t, task.ID, retrievedTask.ID)
+	assert.Equal(t, task.Title, retrievedTask.Title)
+}
+
+func TestGetTaskNotFound(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	ctx := context.Background()
+	task, err := gtm.GetTask(ctx, "non-existent-task")
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Equal(t, tm.ErrTaskNotFound, err)
+}
+
+func TestUpdateTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, gitImpl := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	originalTask := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Original Title",
+		Description: "Original description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Original User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityLow,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(originalTask)
+	require.NoError(t, err)
+
+	// Commit the initial task
+	err = gtm.commitTaskChange(originalTask.ID, "created")
+	require.NoError(t, err)
+
+	// Update the task
+	ctx := context.Background()
+	newTitle := "Updated Title"
+	newDescription := "Updated description"
+	newStatus := tm.StatusInProgress
+	newPriority := tm.PriorityHigh
+	newOwnerID := "user456"
+
+	req := &tm.TaskUpdateRequest{
+		Title:       &newTitle,
+		Description: &newDescription,
+		Status:      &newStatus,
+		Priority:    &newPriority,
+		OwnerID:     &newOwnerID,
+	}
+
+	updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
+	assert.NoError(t, err)
+	assert.NotNil(t, updatedTask)
+
+	// Verify updated properties
+	assert.Equal(t, newTitle, updatedTask.Title)
+	assert.Equal(t, newDescription, updatedTask.Description)
+	assert.Equal(t, newStatus, updatedTask.Status)
+	assert.Equal(t, newPriority, updatedTask.Priority)
+	assert.Equal(t, newOwnerID, updatedTask.Owner.ID)
+	assert.Equal(t, newOwnerID, updatedTask.Owner.Name)
+
+	// Verify timestamps were updated
+	assert.True(t, updatedTask.UpdatedAt.After(originalTask.UpdatedAt))
+
+	// Verify git commit was created
+	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 2})
+	assert.NoError(t, err)
+	if len(commits) > 0 {
+		assert.Contains(t, commits[0].Message, "updated")
+	}
+}
+
+func TestUpdateTaskNotFound(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	ctx := context.Background()
+	newTitle := "Updated Title"
+	req := &tm.TaskUpdateRequest{
+		Title: &newTitle,
+	}
+
+	task, err := gtm.UpdateTask(ctx, "non-existent-task", req)
+	assert.Error(t, err)
+	assert.Nil(t, task)
+	assert.Equal(t, tm.ErrTaskNotFound, err)
+}
+
+func TestUpdateTaskNoChanges(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	originalTask := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(originalTask)
+	require.NoError(t, err)
+
+	// Update with no changes
+	ctx := context.Background()
+	req := &tm.TaskUpdateRequest{}
+
+	updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
+	assert.NoError(t, err)
+	assert.NotNil(t, updatedTask)
+
+	// Verify no changes were made
+	assert.Equal(t, originalTask.Title, updatedTask.Title)
+	assert.Equal(t, originalTask.Description, updatedTask.Description)
+	assert.Equal(t, originalTask.Status, updatedTask.Status)
+	assert.Equal(t, originalTask.Priority, updatedTask.Priority)
+	assert.Equal(t, originalTask.Owner.ID, updatedTask.Owner.ID)
+}
+
+func TestUpdateTaskStatusTimestamps(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	ctx := context.Background()
+
+	// Test completing a task
+	completedStatus := tm.StatusCompleted
+	req := &tm.TaskUpdateRequest{
+		Status: &completedStatus,
+	}
+
+	updatedTask, err := gtm.UpdateTask(ctx, task.ID, req)
+	assert.NoError(t, err)
+	assert.NotNil(t, updatedTask)
+	assert.Equal(t, tm.StatusCompleted, updatedTask.Status)
+	assert.NotNil(t, updatedTask.CompletedAt)
+
+	// Test archiving a task
+	archivedStatus := tm.StatusArchived
+	req = &tm.TaskUpdateRequest{
+		Status: &archivedStatus,
+	}
+
+	updatedTask, err = gtm.UpdateTask(ctx, task.ID, req)
+	assert.NoError(t, err)
+	assert.NotNil(t, updatedTask)
+	assert.Equal(t, tm.StatusArchived, updatedTask.Status)
+	assert.NotNil(t, updatedTask.ArchivedAt)
+}
+
+func TestArchiveTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	// Archive the task
+	ctx := context.Background()
+	err = gtm.ArchiveTask(ctx, task.ID)
+	assert.NoError(t, err)
+
+	// Verify task was archived
+	archivedTask, err := gtm.GetTask(ctx, task.ID)
+	assert.NoError(t, err)
+	assert.Equal(t, tm.StatusArchived, archivedTask.Status)
+	assert.NotNil(t, archivedTask.ArchivedAt)
+}
+
+func TestStartTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusToDo,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	// Start the task
+	ctx := context.Background()
+	startedTask, err := gtm.StartTask(ctx, task.ID)
+	assert.NoError(t, err)
+	assert.NotNil(t, startedTask)
+	assert.Equal(t, tm.StatusInProgress, startedTask.Status)
+}
+
+func TestCompleteTask(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create a test task
+	task := &tm.Task{
+		ID:          "test-task-123",
+		Title:       "Test Task",
+		Description: "Test description",
+		Owner: tm.Owner{
+			ID:   "user123",
+			Name: "Test User",
+		},
+		Status:    tm.StatusInProgress,
+		Priority:  tm.PriorityMedium,
+		CreatedAt: time.Now().Add(-time.Hour),
+		UpdatedAt: time.Now().Add(-time.Hour),
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+	err = gtm.writeTaskFile(task)
+	require.NoError(t, err)
+
+	// Complete the task
+	ctx := context.Background()
+	completedTask, err := gtm.CompleteTask(ctx, task.ID)
+	assert.NoError(t, err)
+	assert.NotNil(t, completedTask)
+	assert.Equal(t, tm.StatusCompleted, completedTask.Status)
+	assert.NotNil(t, completedTask.CompletedAt)
+}
+
+func TestListTasks(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create test tasks
+	tasks := []*tm.Task{
+		{
+			ID:          "task-1",
+			Title:       "Task 1",
+			Description: "First task",
+			Owner:       tm.Owner{ID: "user1", Name: "User 1"},
+			Status:      tm.StatusToDo,
+			Priority:    tm.PriorityHigh,
+			CreatedAt:   time.Now().Add(-2 * time.Hour),
+			UpdatedAt:   time.Now().Add(-2 * time.Hour),
+		},
+		{
+			ID:          "task-2",
+			Title:       "Task 2",
+			Description: "Second task",
+			Owner:       tm.Owner{ID: "user2", Name: "User 2"},
+			Status:      tm.StatusInProgress,
+			Priority:    tm.PriorityMedium,
+			CreatedAt:   time.Now().Add(-1 * time.Hour),
+			UpdatedAt:   time.Now().Add(-1 * time.Hour),
+		},
+		{
+			ID:          "task-3",
+			Title:       "Task 3",
+			Description: "Third task",
+			Owner:       tm.Owner{ID: "user1", Name: "User 1"},
+			Status:      tm.StatusCompleted,
+			Priority:    tm.PriorityLow,
+			CreatedAt:   time.Now(),
+			UpdatedAt:   time.Now(),
+		},
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	for _, task := range tasks {
+		err = gtm.writeTaskFile(task)
+		require.NoError(t, err)
+	}
+
+	ctx := context.Background()
+
+	// Test listing all tasks
+	taskList, err := gtm.ListTasks(ctx, nil, 0, 10)
+	assert.NoError(t, err)
+	assert.NotNil(t, taskList)
+	assert.Len(t, taskList.Tasks, 3)
+	assert.Equal(t, 3, taskList.TotalCount)
+	assert.Equal(t, 0, taskList.Page)
+	assert.Equal(t, 10, taskList.PageSize)
+	assert.False(t, taskList.HasMore)
+
+	// Test pagination
+	taskList, err = gtm.ListTasks(ctx, nil, 0, 2)
+	assert.NoError(t, err)
+	assert.Len(t, taskList.Tasks, 2)
+	assert.Equal(t, 3, taskList.TotalCount)
+	assert.True(t, taskList.HasMore)
+
+	// Test filtering by owner
+	ownerFilter := &tm.TaskFilter{OwnerID: stringPtr("user1")}
+	taskList, err = gtm.ListTasks(ctx, ownerFilter, 0, 10)
+	assert.NoError(t, err)
+	assert.Len(t, taskList.Tasks, 2)
+
+	// Test filtering by status
+	statusFilter := &tm.TaskFilter{Status: taskStatusPtr(tm.StatusToDo)}
+	taskList, err = gtm.ListTasks(ctx, statusFilter, 0, 10)
+	assert.NoError(t, err)
+	assert.Len(t, taskList.Tasks, 1)
+	assert.Equal(t, "task-1", taskList.Tasks[0].ID)
+
+	// Test filtering by priority
+	priorityFilter := &tm.TaskFilter{Priority: taskPriorityPtr(tm.PriorityHigh)}
+	taskList, err = gtm.ListTasks(ctx, priorityFilter, 0, 10)
+	assert.NoError(t, err)
+	assert.Len(t, taskList.Tasks, 1)
+	assert.Equal(t, "task-1", taskList.Tasks[0].ID)
+}
+
+func TestGetTasksByOwner(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create test tasks
+	tasks := []*tm.Task{
+		{
+			ID:        "task-1",
+			Title:     "Task 1",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusToDo,
+			Priority:  tm.PriorityHigh,
+			CreatedAt: time.Now().Add(-2 * time.Hour),
+			UpdatedAt: time.Now().Add(-2 * time.Hour),
+		},
+		{
+			ID:        "task-2",
+			Title:     "Task 2",
+			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
+			Status:    tm.StatusInProgress,
+			Priority:  tm.PriorityMedium,
+			CreatedAt: time.Now().Add(-1 * time.Hour),
+			UpdatedAt: time.Now().Add(-1 * time.Hour),
+		},
+		{
+			ID:        "task-3",
+			Title:     "Task 3",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusCompleted,
+			Priority:  tm.PriorityLow,
+			CreatedAt: time.Now(),
+			UpdatedAt: time.Now(),
+		},
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	for _, task := range tasks {
+		err = gtm.writeTaskFile(task)
+		require.NoError(t, err)
+	}
+
+	ctx := context.Background()
+
+	// Get tasks by owner
+	taskList, err := gtm.GetTasksByOwner(ctx, "user1", 0, 10)
+	assert.NoError(t, err)
+	assert.NotNil(t, taskList)
+	assert.Len(t, taskList.Tasks, 2)
+
+	for _, task := range taskList.Tasks {
+		assert.Equal(t, "user1", task.Owner.ID)
+	}
+}
+
+func TestGetTasksByStatus(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create test tasks
+	tasks := []*tm.Task{
+		{
+			ID:        "task-1",
+			Title:     "Task 1",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusToDo,
+			Priority:  tm.PriorityHigh,
+			CreatedAt: time.Now().Add(-2 * time.Hour),
+			UpdatedAt: time.Now().Add(-2 * time.Hour),
+		},
+		{
+			ID:        "task-2",
+			Title:     "Task 2",
+			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
+			Status:    tm.StatusInProgress,
+			Priority:  tm.PriorityMedium,
+			CreatedAt: time.Now().Add(-1 * time.Hour),
+			UpdatedAt: time.Now().Add(-1 * time.Hour),
+		},
+		{
+			ID:        "task-3",
+			Title:     "Task 3",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusCompleted,
+			Priority:  tm.PriorityLow,
+			CreatedAt: time.Now(),
+			UpdatedAt: time.Now(),
+		},
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	for _, task := range tasks {
+		err = gtm.writeTaskFile(task)
+		require.NoError(t, err)
+	}
+
+	ctx := context.Background()
+
+	// Get tasks by status
+	taskList, err := gtm.GetTasksByStatus(ctx, tm.StatusToDo, 0, 10)
+	assert.NoError(t, err)
+	assert.NotNil(t, taskList)
+	assert.Len(t, taskList.Tasks, 1)
+	assert.Equal(t, tm.StatusToDo, taskList.Tasks[0].Status)
+}
+
+func TestGetTasksByPriority(t *testing.T) {
+	tempDir, cleanup := setupTestDir(t)
+	defer cleanup()
+
+	gtm, _ := createTestTaskManager(t, tempDir)
+
+	// Create test tasks
+	tasks := []*tm.Task{
+		{
+			ID:        "task-1",
+			Title:     "Task 1",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusToDo,
+			Priority:  tm.PriorityHigh,
+			CreatedAt: time.Now().Add(-2 * time.Hour),
+			UpdatedAt: time.Now().Add(-2 * time.Hour),
+		},
+		{
+			ID:        "task-2",
+			Title:     "Task 2",
+			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
+			Status:    tm.StatusInProgress,
+			Priority:  tm.PriorityMedium,
+			CreatedAt: time.Now().Add(-1 * time.Hour),
+			UpdatedAt: time.Now().Add(-1 * time.Hour),
+		},
+		{
+			ID:        "task-3",
+			Title:     "Task 3",
+			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
+			Status:    tm.StatusCompleted,
+			Priority:  tm.PriorityLow,
+			CreatedAt: time.Now(),
+			UpdatedAt: time.Now(),
+		},
+	}
+
+	err := gtm.ensureTasksDir()
+	require.NoError(t, err)
+
+	for _, task := range tasks {
+		err = gtm.writeTaskFile(task)
+		require.NoError(t, err)
+	}
+
+	ctx := context.Background()
+
+	// Get tasks by priority
+	taskList, err := gtm.GetTasksByPriority(ctx, tm.PriorityHigh, 0, 10)
+	assert.NoError(t, err)
+	assert.NotNil(t, taskList)
+	assert.Len(t, taskList.Tasks, 1)
+	assert.Equal(t, tm.PriorityHigh, taskList.Tasks[0].Priority)
+}
+
+// Helper functions for creating pointers to string, TaskStatus, and TaskPriority
+func stringPtr(s string) *string {
+	return &s
+}
+
+func taskStatusPtr(status tm.TaskStatus) *tm.TaskStatus {
+	return &status
+}
+
+func taskPriorityPtr(priority tm.TaskPriority) *tm.TaskPriority {
+	return &priority
+}