blob: cd378384a1f88dc22ef312db98029751312374ec [file] [log] [blame]
iomodo3b89bdf2025-07-25 15:19:22 +04001package git_tm
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "testing"
8 "time"
9
10 "github.com/iomodo/staff/git"
11 "github.com/iomodo/staff/tm"
12 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/require"
14)
15
16// Test helper functions
17func setupTestDir(t *testing.T) (string, func()) {
18 tempDir, err := os.MkdirTemp("", "git-task-manager-test")
19 require.NoError(t, err)
20
21 cleanup := func() {
22 os.RemoveAll(tempDir)
23 }
24
25 return tempDir, cleanup
26}
27
28func createTestTaskManager(t *testing.T, repoPath string) (*GitTaskManager, git.GitInterface) {
29 // Initialize git repository
30 gitImpl := git.DefaultGit(repoPath)
31 ctx := context.Background()
32
33 err := gitImpl.Init(ctx, repoPath)
34 require.NoError(t, err)
35
36 // Set up git user config for commits
37 userConfig := git.UserConfig{
38 Name: "Test User",
39 Email: "test@example.com",
40 }
41 err = gitImpl.SetUserConfig(ctx, userConfig)
42 require.NoError(t, err)
43
44 gtm := NewGitTaskManager(gitImpl, repoPath)
45 return gtm, gitImpl
46}
47
48// Test cases
49func TestNewGitTaskManager(t *testing.T) {
50 tempDir, cleanup := setupTestDir(t)
51 defer cleanup()
52
53 gitImpl := git.DefaultGit(tempDir)
54 gtm := NewGitTaskManager(gitImpl, tempDir)
55
56 assert.NotNil(t, gtm)
57 assert.Equal(t, gitImpl, gtm.git)
58 assert.Equal(t, tempDir, gtm.repoPath)
59 assert.Equal(t, filepath.Join(tempDir, "tasks"), gtm.tasksDir)
60}
61
62func TestEnsureTasksDir(t *testing.T) {
63 tempDir, cleanup := setupTestDir(t)
64 defer cleanup()
65
66 gtm, _ := createTestTaskManager(t, tempDir)
67
68 // Test creating tasks directory
69 err := gtm.ensureTasksDir()
70 assert.NoError(t, err)
71
72 // Verify directory exists
73 _, err = os.Stat(gtm.tasksDir)
74 assert.NoError(t, err)
75
76 // Test creating again (should not error)
77 err = gtm.ensureTasksDir()
78 assert.NoError(t, err)
79}
80
81func TestGenerateTaskID(t *testing.T) {
82 tempDir, cleanup := setupTestDir(t)
83 defer cleanup()
84
85 gtm, _ := createTestTaskManager(t, tempDir)
86
87 id1 := gtm.generateTaskID()
88 id2 := gtm.generateTaskID()
89
90 assert.NotEmpty(t, id1)
91 assert.NotEmpty(t, id2)
92 assert.NotEqual(t, id1, id2)
93 assert.Contains(t, id1, "task-")
94}
95
96func TestTaskToMarkdown(t *testing.T) {
97 tempDir, cleanup := setupTestDir(t)
98 defer cleanup()
99
100 gtm, _ := createTestTaskManager(t, tempDir)
101
102 now := time.Now()
103 dueDate := now.Add(24 * time.Hour)
104 completedAt := now.Add(12 * time.Hour)
105
106 task := &tm.Task{
107 ID: "test-task-123",
108 Title: "Test Task",
109 Description: "This is a test task",
110 Owner: tm.Owner{
111 ID: "user123",
112 Name: "Test User",
113 },
114 Status: tm.StatusToDo,
115 Priority: tm.PriorityHigh,
116 CreatedAt: now,
117 UpdatedAt: now,
118 DueDate: &dueDate,
119 CompletedAt: &completedAt,
120 }
121
122 markdown, err := gtm.taskToMarkdown(task)
123 assert.NoError(t, err)
124 assert.NotEmpty(t, markdown)
125 assert.Contains(t, markdown, "---")
126 assert.Contains(t, markdown, "id: test-task-123")
127 assert.Contains(t, markdown, "title: Test Task")
128 assert.Contains(t, markdown, "description: This is a test task")
129 assert.Contains(t, markdown, "owner_id: user123")
130 assert.Contains(t, markdown, "owner_name: Test User")
131 assert.Contains(t, markdown, "status: todo")
132 assert.Contains(t, markdown, "priority: high")
133 assert.Contains(t, markdown, "# Task Description")
134 assert.Contains(t, markdown, "This is a test task")
135}
136
137func TestParseTaskFromMarkdown(t *testing.T) {
138 tempDir, cleanup := setupTestDir(t)
139 defer cleanup()
140
141 gtm, _ := createTestTaskManager(t, tempDir)
142
143 markdown := `---
144id: test-task-123
145title: Test Task
146description: This is a test task
147owner_id: user123
148owner_name: Test User
149status: todo
150priority: high
151created_at: 2023-01-01T00:00:00Z
152updated_at: 2023-01-01T00:00:00Z
153due_date: 2023-01-02T00:00:00Z
154completed_at: 2023-01-01T12:00:00Z
155---
156
157# Task Description
158
159This is a test task
160`
161
162 task, err := gtm.parseTaskFromMarkdown(markdown)
163 assert.NoError(t, err)
164 assert.NotNil(t, task)
165 assert.Equal(t, "test-task-123", task.ID)
166 assert.Equal(t, "Test Task", task.Title)
167 assert.Equal(t, "This is a test task", task.Description)
168 assert.Equal(t, "user123", task.Owner.ID)
169 assert.Equal(t, "Test User", task.Owner.Name)
170 assert.Equal(t, tm.StatusToDo, task.Status)
171 assert.Equal(t, tm.PriorityHigh, task.Priority)
172}
173
174func TestParseTaskFromMarkdownInvalid(t *testing.T) {
175 tempDir, cleanup := setupTestDir(t)
176 defer cleanup()
177
178 gtm, _ := createTestTaskManager(t, tempDir)
179
180 // Test invalid markdown format
181 invalidMarkdown := "This is not valid markdown"
182
183 task, err := gtm.parseTaskFromMarkdown(invalidMarkdown)
184 assert.Error(t, err)
185 assert.Nil(t, task)
186 assert.Contains(t, err.Error(), "invalid markdown format")
187}
188
189func TestWriteAndReadTaskFile(t *testing.T) {
190 tempDir, cleanup := setupTestDir(t)
191 defer cleanup()
192
193 gtm, _ := createTestTaskManager(t, tempDir)
194
195 // Ensure tasks directory exists
196 err := gtm.ensureTasksDir()
197 require.NoError(t, err)
198
199 // Create test task
200 task := &tm.Task{
201 ID: "test-task-123",
202 Title: "Test Task",
203 Description: "This is a test task",
204 Owner: tm.Owner{
205 ID: "user123",
206 Name: "Test User",
207 },
208 Status: tm.StatusToDo,
209 Priority: tm.PriorityHigh,
210 CreatedAt: time.Now(),
211 UpdatedAt: time.Now(),
212 }
213
214 // Write task file
215 err = gtm.writeTaskFile(task)
216 assert.NoError(t, err)
217
218 // Verify file exists
219 filePath := filepath.Join(gtm.tasksDir, task.ID+".md")
220 _, err = os.Stat(filePath)
221 assert.NoError(t, err)
222
223 // Read task file
224 readTask, err := gtm.readTaskFile(task.ID)
225 assert.NoError(t, err)
226 assert.NotNil(t, readTask)
227 assert.Equal(t, task.ID, readTask.ID)
228 assert.Equal(t, task.Title, readTask.Title)
229 assert.Equal(t, task.Description, readTask.Description)
230 assert.Equal(t, task.Owner.ID, readTask.Owner.ID)
231 assert.Equal(t, task.Owner.Name, readTask.Owner.Name)
232 assert.Equal(t, task.Status, readTask.Status)
233 assert.Equal(t, task.Priority, readTask.Priority)
234}
235
236func TestReadTaskFileNotFound(t *testing.T) {
237 tempDir, cleanup := setupTestDir(t)
238 defer cleanup()
239
240 gtm, _ := createTestTaskManager(t, tempDir)
241
242 // Try to read non-existent task
243 task, err := gtm.readTaskFile("non-existent-task")
244 assert.Error(t, err)
245 assert.Nil(t, task)
246 assert.Equal(t, tm.ErrTaskNotFound, err)
247}
248
249func TestListTaskFiles(t *testing.T) {
250 tempDir, cleanup := setupTestDir(t)
251 defer cleanup()
252
253 gtm, _ := createTestTaskManager(t, tempDir)
254
255 // Ensure tasks directory exists
256 err := gtm.ensureTasksDir()
257 require.NoError(t, err)
258
259 // Create some test task files
260 taskIDs := []string{"task-1", "task-2", "task-3"}
261 for _, id := range taskIDs {
262 task := &tm.Task{
263 ID: id,
264 Title: "Test Task " + id,
265 Description: "Test task description",
266 Owner: tm.Owner{
267 ID: "user123",
268 Name: "Test User",
269 },
270 Status: tm.StatusToDo,
271 Priority: tm.PriorityMedium,
272 CreatedAt: time.Now(),
273 UpdatedAt: time.Now(),
274 }
275 err = gtm.writeTaskFile(task)
276 require.NoError(t, err)
277 }
278
279 // Create a non-task file
280 nonTaskFile := filepath.Join(gtm.tasksDir, "readme.txt")
281 err = os.WriteFile(nonTaskFile, []byte("This is not a task"), 0644)
282 require.NoError(t, err)
283
284 // List task files
285 taskFiles, err := gtm.listTaskFiles()
286 assert.NoError(t, err)
287 assert.Len(t, taskFiles, 3)
288
289 // Verify all task IDs are present
290 for _, id := range taskIDs {
291 assert.Contains(t, taskFiles, id)
292 }
293}
294
295func TestListTaskFilesEmpty(t *testing.T) {
296 tempDir, cleanup := setupTestDir(t)
297 defer cleanup()
298
299 gtm, _ := createTestTaskManager(t, tempDir)
300
301 // List task files in non-existent directory
302 taskFiles, err := gtm.listTaskFiles()
303 assert.NoError(t, err)
304 assert.Empty(t, taskFiles)
305}
306
307func TestCommitTaskChange(t *testing.T) {
308 tempDir, cleanup := setupTestDir(t)
309 defer cleanup()
310
311 gtm, gitImpl := createTestTaskManager(t, tempDir)
312
313 // Create a test task file first
314 task := &tm.Task{
315 ID: "test-task-123",
316 Title: "Test Task",
317 Description: "Test description",
318 Owner: tm.Owner{
319 ID: "user123",
320 Name: "Test User",
321 },
322 Status: tm.StatusToDo,
323 Priority: tm.PriorityMedium,
324 CreatedAt: time.Now(),
325 UpdatedAt: time.Now(),
326 }
327
328 err := gtm.ensureTasksDir()
329 require.NoError(t, err)
330 err = gtm.writeTaskFile(task)
331 require.NoError(t, err)
332
333 // Test successful commit
334 err = gtm.commitTaskChange("test-task-123", "created")
335 assert.NoError(t, err)
336
337 // Verify commit was created
338 ctx := context.Background()
339 commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
340 assert.NoError(t, err)
341 if len(commits) > 0 {
342 assert.Contains(t, commits[0].Message, "test-task-123")
343 assert.Contains(t, commits[0].Message, "created")
344 }
345}
346
347func TestCreateTask(t *testing.T) {
348 tempDir, cleanup := setupTestDir(t)
349 defer cleanup()
350
351 gtm, gitImpl := createTestTaskManager(t, tempDir)
352
353 ctx := context.Background()
354 req := &tm.TaskCreateRequest{
355 Title: "New Test Task",
356 Description: "This is a new test task",
357 OwnerID: "user123",
358 Priority: tm.PriorityHigh,
359 }
360
361 task, err := gtm.CreateTask(ctx, req)
362 assert.NoError(t, err)
363 assert.NotNil(t, task)
364
365 // Verify task properties
366 assert.NotEmpty(t, task.ID)
367 assert.Contains(t, task.ID, "task-")
368 assert.Equal(t, req.Title, task.Title)
369 assert.Equal(t, req.Description, task.Description)
370 assert.Equal(t, req.OwnerID, task.Owner.ID)
371 assert.Equal(t, req.OwnerID, task.Owner.Name) // TODO: Should look up actual name
372 assert.Equal(t, tm.StatusToDo, task.Status)
373 assert.Equal(t, req.Priority, task.Priority)
374 assert.False(t, task.CreatedAt.IsZero())
375 assert.False(t, task.UpdatedAt.IsZero())
376
377 // Verify git commit was created
378 commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
379 assert.NoError(t, err)
380 if len(commits) > 0 {
381 assert.Contains(t, commits[0].Message, task.ID)
382 assert.Contains(t, commits[0].Message, "created")
383 }
384}
385
386func TestCreateTaskInvalidData(t *testing.T) {
387 tempDir, cleanup := setupTestDir(t)
388 defer cleanup()
389
390 gtm, _ := createTestTaskManager(t, tempDir)
391
392 ctx := context.Background()
393
394 // Test empty title
395 req := &tm.TaskCreateRequest{
396 Title: "",
397 OwnerID: "user123",
398 }
399
400 task, err := gtm.CreateTask(ctx, req)
401 assert.Error(t, err)
402 assert.Nil(t, task)
403 assert.Equal(t, tm.ErrInvalidTaskData, err)
404
405 // Test empty owner ID
406 req = &tm.TaskCreateRequest{
407 Title: "Valid Title",
408 OwnerID: "",
409 }
410
411 task, err = gtm.CreateTask(ctx, req)
412 assert.Error(t, err)
413 assert.Nil(t, task)
414 assert.Equal(t, tm.ErrInvalidOwner, err)
415}
416
417func TestGetTask(t *testing.T) {
418 tempDir, cleanup := setupTestDir(t)
419 defer cleanup()
420
421 gtm, _ := createTestTaskManager(t, tempDir)
422
423 // Create a test task
424 task := &tm.Task{
425 ID: "test-task-123",
426 Title: "Test Task",
427 Description: "Test task description",
428 Owner: tm.Owner{
429 ID: "user123",
430 Name: "Test User",
431 },
432 Status: tm.StatusToDo,
433 Priority: tm.PriorityMedium,
434 CreatedAt: time.Now(),
435 UpdatedAt: time.Now(),
436 }
437
438 err := gtm.ensureTasksDir()
439 require.NoError(t, err)
440 err = gtm.writeTaskFile(task)
441 require.NoError(t, err)
442
443 // Get the task
444 ctx := context.Background()
445 retrievedTask, err := gtm.GetTask(ctx, task.ID)
446 assert.NoError(t, err)
447 assert.NotNil(t, retrievedTask)
448 assert.Equal(t, task.ID, retrievedTask.ID)
449 assert.Equal(t, task.Title, retrievedTask.Title)
450}
451
452func TestGetTaskNotFound(t *testing.T) {
453 tempDir, cleanup := setupTestDir(t)
454 defer cleanup()
455
456 gtm, _ := createTestTaskManager(t, tempDir)
457
458 ctx := context.Background()
459 task, err := gtm.GetTask(ctx, "non-existent-task")
460 assert.Error(t, err)
461 assert.Nil(t, task)
462 assert.Equal(t, tm.ErrTaskNotFound, err)
463}
464
465func TestUpdateTask(t *testing.T) {
466 tempDir, cleanup := setupTestDir(t)
467 defer cleanup()
468
469 gtm, gitImpl := createTestTaskManager(t, tempDir)
470
471 // Create a test task
472 originalTask := &tm.Task{
473 ID: "test-task-123",
474 Title: "Original Title",
475 Description: "Original description",
476 Owner: tm.Owner{
477 ID: "user123",
478 Name: "Original User",
479 },
480 Status: tm.StatusToDo,
481 Priority: tm.PriorityLow,
482 CreatedAt: time.Now().Add(-time.Hour),
483 UpdatedAt: time.Now().Add(-time.Hour),
484 }
485
486 err := gtm.ensureTasksDir()
487 require.NoError(t, err)
488 err = gtm.writeTaskFile(originalTask)
489 require.NoError(t, err)
490
491 // Commit the initial task
492 err = gtm.commitTaskChange(originalTask.ID, "created")
493 require.NoError(t, err)
494
495 // Update the task
496 ctx := context.Background()
497 newTitle := "Updated Title"
498 newDescription := "Updated description"
499 newStatus := tm.StatusInProgress
500 newPriority := tm.PriorityHigh
501 newOwnerID := "user456"
502
503 req := &tm.TaskUpdateRequest{
504 Title: &newTitle,
505 Description: &newDescription,
506 Status: &newStatus,
507 Priority: &newPriority,
508 OwnerID: &newOwnerID,
509 }
510
511 updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
512 assert.NoError(t, err)
513 assert.NotNil(t, updatedTask)
514
515 // Verify updated properties
516 assert.Equal(t, newTitle, updatedTask.Title)
517 assert.Equal(t, newDescription, updatedTask.Description)
518 assert.Equal(t, newStatus, updatedTask.Status)
519 assert.Equal(t, newPriority, updatedTask.Priority)
520 assert.Equal(t, newOwnerID, updatedTask.Owner.ID)
521 assert.Equal(t, newOwnerID, updatedTask.Owner.Name)
522
523 // Verify timestamps were updated
524 assert.True(t, updatedTask.UpdatedAt.After(originalTask.UpdatedAt))
525
526 // Verify git commit was created
527 commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 2})
528 assert.NoError(t, err)
529 if len(commits) > 0 {
530 assert.Contains(t, commits[0].Message, "updated")
531 }
532}
533
534func TestUpdateTaskNotFound(t *testing.T) {
535 tempDir, cleanup := setupTestDir(t)
536 defer cleanup()
537
538 gtm, _ := createTestTaskManager(t, tempDir)
539
540 ctx := context.Background()
541 newTitle := "Updated Title"
542 req := &tm.TaskUpdateRequest{
543 Title: &newTitle,
544 }
545
546 task, err := gtm.UpdateTask(ctx, "non-existent-task", req)
547 assert.Error(t, err)
548 assert.Nil(t, task)
549 assert.Equal(t, tm.ErrTaskNotFound, err)
550}
551
552func TestUpdateTaskNoChanges(t *testing.T) {
553 tempDir, cleanup := setupTestDir(t)
554 defer cleanup()
555
556 gtm, _ := createTestTaskManager(t, tempDir)
557
558 // Create a test task
559 originalTask := &tm.Task{
560 ID: "test-task-123",
561 Title: "Test Task",
562 Description: "Test description",
563 Owner: tm.Owner{
564 ID: "user123",
565 Name: "Test User",
566 },
567 Status: tm.StatusToDo,
568 Priority: tm.PriorityMedium,
569 CreatedAt: time.Now().Add(-time.Hour),
570 UpdatedAt: time.Now().Add(-time.Hour),
571 }
572
573 err := gtm.ensureTasksDir()
574 require.NoError(t, err)
575 err = gtm.writeTaskFile(originalTask)
576 require.NoError(t, err)
577
578 // Update with no changes
579 ctx := context.Background()
580 req := &tm.TaskUpdateRequest{}
581
582 updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
583 assert.NoError(t, err)
584 assert.NotNil(t, updatedTask)
585
586 // Verify no changes were made
587 assert.Equal(t, originalTask.Title, updatedTask.Title)
588 assert.Equal(t, originalTask.Description, updatedTask.Description)
589 assert.Equal(t, originalTask.Status, updatedTask.Status)
590 assert.Equal(t, originalTask.Priority, updatedTask.Priority)
591 assert.Equal(t, originalTask.Owner.ID, updatedTask.Owner.ID)
592}
593
594func TestUpdateTaskStatusTimestamps(t *testing.T) {
595 tempDir, cleanup := setupTestDir(t)
596 defer cleanup()
597
598 gtm, _ := createTestTaskManager(t, tempDir)
599
600 // Create a test task
601 task := &tm.Task{
602 ID: "test-task-123",
603 Title: "Test Task",
604 Description: "Test description",
605 Owner: tm.Owner{
606 ID: "user123",
607 Name: "Test User",
608 },
609 Status: tm.StatusToDo,
610 Priority: tm.PriorityMedium,
611 CreatedAt: time.Now().Add(-time.Hour),
612 UpdatedAt: time.Now().Add(-time.Hour),
613 }
614
615 err := gtm.ensureTasksDir()
616 require.NoError(t, err)
617 err = gtm.writeTaskFile(task)
618 require.NoError(t, err)
619
620 ctx := context.Background()
621
622 // Test completing a task
623 completedStatus := tm.StatusCompleted
624 req := &tm.TaskUpdateRequest{
625 Status: &completedStatus,
626 }
627
628 updatedTask, err := gtm.UpdateTask(ctx, task.ID, req)
629 assert.NoError(t, err)
630 assert.NotNil(t, updatedTask)
631 assert.Equal(t, tm.StatusCompleted, updatedTask.Status)
632 assert.NotNil(t, updatedTask.CompletedAt)
633
634 // Test archiving a task
635 archivedStatus := tm.StatusArchived
636 req = &tm.TaskUpdateRequest{
637 Status: &archivedStatus,
638 }
639
640 updatedTask, err = gtm.UpdateTask(ctx, task.ID, req)
641 assert.NoError(t, err)
642 assert.NotNil(t, updatedTask)
643 assert.Equal(t, tm.StatusArchived, updatedTask.Status)
644 assert.NotNil(t, updatedTask.ArchivedAt)
645}
646
647func TestArchiveTask(t *testing.T) {
648 tempDir, cleanup := setupTestDir(t)
649 defer cleanup()
650
651 gtm, _ := createTestTaskManager(t, tempDir)
652
653 // Create a test task
654 task := &tm.Task{
655 ID: "test-task-123",
656 Title: "Test Task",
657 Description: "Test description",
658 Owner: tm.Owner{
659 ID: "user123",
660 Name: "Test User",
661 },
662 Status: tm.StatusToDo,
663 Priority: tm.PriorityMedium,
664 CreatedAt: time.Now().Add(-time.Hour),
665 UpdatedAt: time.Now().Add(-time.Hour),
666 }
667
668 err := gtm.ensureTasksDir()
669 require.NoError(t, err)
670 err = gtm.writeTaskFile(task)
671 require.NoError(t, err)
672
673 // Archive the task
674 ctx := context.Background()
675 err = gtm.ArchiveTask(ctx, task.ID)
676 assert.NoError(t, err)
677
678 // Verify task was archived
679 archivedTask, err := gtm.GetTask(ctx, task.ID)
680 assert.NoError(t, err)
681 assert.Equal(t, tm.StatusArchived, archivedTask.Status)
682 assert.NotNil(t, archivedTask.ArchivedAt)
683}
684
685func TestStartTask(t *testing.T) {
686 tempDir, cleanup := setupTestDir(t)
687 defer cleanup()
688
689 gtm, _ := createTestTaskManager(t, tempDir)
690
691 // Create a test task
692 task := &tm.Task{
693 ID: "test-task-123",
694 Title: "Test Task",
695 Description: "Test description",
696 Owner: tm.Owner{
697 ID: "user123",
698 Name: "Test User",
699 },
700 Status: tm.StatusToDo,
701 Priority: tm.PriorityMedium,
702 CreatedAt: time.Now().Add(-time.Hour),
703 UpdatedAt: time.Now().Add(-time.Hour),
704 }
705
706 err := gtm.ensureTasksDir()
707 require.NoError(t, err)
708 err = gtm.writeTaskFile(task)
709 require.NoError(t, err)
710
711 // Start the task
712 ctx := context.Background()
713 startedTask, err := gtm.StartTask(ctx, task.ID)
714 assert.NoError(t, err)
715 assert.NotNil(t, startedTask)
716 assert.Equal(t, tm.StatusInProgress, startedTask.Status)
717}
718
719func TestCompleteTask(t *testing.T) {
720 tempDir, cleanup := setupTestDir(t)
721 defer cleanup()
722
723 gtm, _ := createTestTaskManager(t, tempDir)
724
725 // Create a test task
726 task := &tm.Task{
727 ID: "test-task-123",
728 Title: "Test Task",
729 Description: "Test description",
730 Owner: tm.Owner{
731 ID: "user123",
732 Name: "Test User",
733 },
734 Status: tm.StatusInProgress,
735 Priority: tm.PriorityMedium,
736 CreatedAt: time.Now().Add(-time.Hour),
737 UpdatedAt: time.Now().Add(-time.Hour),
738 }
739
740 err := gtm.ensureTasksDir()
741 require.NoError(t, err)
742 err = gtm.writeTaskFile(task)
743 require.NoError(t, err)
744
745 // Complete the task
746 ctx := context.Background()
747 completedTask, err := gtm.CompleteTask(ctx, task.ID)
748 assert.NoError(t, err)
749 assert.NotNil(t, completedTask)
750 assert.Equal(t, tm.StatusCompleted, completedTask.Status)
751 assert.NotNil(t, completedTask.CompletedAt)
752}
753
754func TestListTasks(t *testing.T) {
755 tempDir, cleanup := setupTestDir(t)
756 defer cleanup()
757
758 gtm, _ := createTestTaskManager(t, tempDir)
759
760 // Create test tasks
761 tasks := []*tm.Task{
762 {
763 ID: "task-1",
764 Title: "Task 1",
765 Description: "First task",
766 Owner: tm.Owner{ID: "user1", Name: "User 1"},
767 Status: tm.StatusToDo,
768 Priority: tm.PriorityHigh,
769 CreatedAt: time.Now().Add(-2 * time.Hour),
770 UpdatedAt: time.Now().Add(-2 * time.Hour),
771 },
772 {
773 ID: "task-2",
774 Title: "Task 2",
775 Description: "Second task",
776 Owner: tm.Owner{ID: "user2", Name: "User 2"},
777 Status: tm.StatusInProgress,
778 Priority: tm.PriorityMedium,
779 CreatedAt: time.Now().Add(-1 * time.Hour),
780 UpdatedAt: time.Now().Add(-1 * time.Hour),
781 },
782 {
783 ID: "task-3",
784 Title: "Task 3",
785 Description: "Third task",
786 Owner: tm.Owner{ID: "user1", Name: "User 1"},
787 Status: tm.StatusCompleted,
788 Priority: tm.PriorityLow,
789 CreatedAt: time.Now(),
790 UpdatedAt: time.Now(),
791 },
792 }
793
794 err := gtm.ensureTasksDir()
795 require.NoError(t, err)
796
797 for _, task := range tasks {
798 err = gtm.writeTaskFile(task)
799 require.NoError(t, err)
800 }
801
802 ctx := context.Background()
803
804 // Test listing all tasks
805 taskList, err := gtm.ListTasks(ctx, nil, 0, 10)
806 assert.NoError(t, err)
807 assert.NotNil(t, taskList)
808 assert.Len(t, taskList.Tasks, 3)
809 assert.Equal(t, 3, taskList.TotalCount)
810 assert.Equal(t, 0, taskList.Page)
811 assert.Equal(t, 10, taskList.PageSize)
812 assert.False(t, taskList.HasMore)
813
814 // Test pagination
815 taskList, err = gtm.ListTasks(ctx, nil, 0, 2)
816 assert.NoError(t, err)
817 assert.Len(t, taskList.Tasks, 2)
818 assert.Equal(t, 3, taskList.TotalCount)
819 assert.True(t, taskList.HasMore)
820
821 // Test filtering by owner
822 ownerFilter := &tm.TaskFilter{OwnerID: stringPtr("user1")}
823 taskList, err = gtm.ListTasks(ctx, ownerFilter, 0, 10)
824 assert.NoError(t, err)
825 assert.Len(t, taskList.Tasks, 2)
826
827 // Test filtering by status
828 statusFilter := &tm.TaskFilter{Status: taskStatusPtr(tm.StatusToDo)}
829 taskList, err = gtm.ListTasks(ctx, statusFilter, 0, 10)
830 assert.NoError(t, err)
831 assert.Len(t, taskList.Tasks, 1)
832 assert.Equal(t, "task-1", taskList.Tasks[0].ID)
833
834 // Test filtering by priority
835 priorityFilter := &tm.TaskFilter{Priority: taskPriorityPtr(tm.PriorityHigh)}
836 taskList, err = gtm.ListTasks(ctx, priorityFilter, 0, 10)
837 assert.NoError(t, err)
838 assert.Len(t, taskList.Tasks, 1)
839 assert.Equal(t, "task-1", taskList.Tasks[0].ID)
840}
841
842func TestGetTasksByOwner(t *testing.T) {
843 tempDir, cleanup := setupTestDir(t)
844 defer cleanup()
845
846 gtm, _ := createTestTaskManager(t, tempDir)
847
848 // Create test tasks
849 tasks := []*tm.Task{
850 {
851 ID: "task-1",
852 Title: "Task 1",
853 Owner: tm.Owner{ID: "user1", Name: "User 1"},
854 Status: tm.StatusToDo,
855 Priority: tm.PriorityHigh,
856 CreatedAt: time.Now().Add(-2 * time.Hour),
857 UpdatedAt: time.Now().Add(-2 * time.Hour),
858 },
859 {
860 ID: "task-2",
861 Title: "Task 2",
862 Owner: tm.Owner{ID: "user2", Name: "User 2"},
863 Status: tm.StatusInProgress,
864 Priority: tm.PriorityMedium,
865 CreatedAt: time.Now().Add(-1 * time.Hour),
866 UpdatedAt: time.Now().Add(-1 * time.Hour),
867 },
868 {
869 ID: "task-3",
870 Title: "Task 3",
871 Owner: tm.Owner{ID: "user1", Name: "User 1"},
872 Status: tm.StatusCompleted,
873 Priority: tm.PriorityLow,
874 CreatedAt: time.Now(),
875 UpdatedAt: time.Now(),
876 },
877 }
878
879 err := gtm.ensureTasksDir()
880 require.NoError(t, err)
881
882 for _, task := range tasks {
883 err = gtm.writeTaskFile(task)
884 require.NoError(t, err)
885 }
886
887 ctx := context.Background()
888
889 // Get tasks by owner
890 taskList, err := gtm.GetTasksByOwner(ctx, "user1", 0, 10)
891 assert.NoError(t, err)
892 assert.NotNil(t, taskList)
893 assert.Len(t, taskList.Tasks, 2)
894
895 for _, task := range taskList.Tasks {
896 assert.Equal(t, "user1", task.Owner.ID)
897 }
898}
899
900func TestGetTasksByStatus(t *testing.T) {
901 tempDir, cleanup := setupTestDir(t)
902 defer cleanup()
903
904 gtm, _ := createTestTaskManager(t, tempDir)
905
906 // Create test tasks
907 tasks := []*tm.Task{
908 {
909 ID: "task-1",
910 Title: "Task 1",
911 Owner: tm.Owner{ID: "user1", Name: "User 1"},
912 Status: tm.StatusToDo,
913 Priority: tm.PriorityHigh,
914 CreatedAt: time.Now().Add(-2 * time.Hour),
915 UpdatedAt: time.Now().Add(-2 * time.Hour),
916 },
917 {
918 ID: "task-2",
919 Title: "Task 2",
920 Owner: tm.Owner{ID: "user2", Name: "User 2"},
921 Status: tm.StatusInProgress,
922 Priority: tm.PriorityMedium,
923 CreatedAt: time.Now().Add(-1 * time.Hour),
924 UpdatedAt: time.Now().Add(-1 * time.Hour),
925 },
926 {
927 ID: "task-3",
928 Title: "Task 3",
929 Owner: tm.Owner{ID: "user1", Name: "User 1"},
930 Status: tm.StatusCompleted,
931 Priority: tm.PriorityLow,
932 CreatedAt: time.Now(),
933 UpdatedAt: time.Now(),
934 },
935 }
936
937 err := gtm.ensureTasksDir()
938 require.NoError(t, err)
939
940 for _, task := range tasks {
941 err = gtm.writeTaskFile(task)
942 require.NoError(t, err)
943 }
944
945 ctx := context.Background()
946
947 // Get tasks by status
948 taskList, err := gtm.GetTasksByStatus(ctx, tm.StatusToDo, 0, 10)
949 assert.NoError(t, err)
950 assert.NotNil(t, taskList)
951 assert.Len(t, taskList.Tasks, 1)
952 assert.Equal(t, tm.StatusToDo, taskList.Tasks[0].Status)
953}
954
955func TestGetTasksByPriority(t *testing.T) {
956 tempDir, cleanup := setupTestDir(t)
957 defer cleanup()
958
959 gtm, _ := createTestTaskManager(t, tempDir)
960
961 // Create test tasks
962 tasks := []*tm.Task{
963 {
964 ID: "task-1",
965 Title: "Task 1",
966 Owner: tm.Owner{ID: "user1", Name: "User 1"},
967 Status: tm.StatusToDo,
968 Priority: tm.PriorityHigh,
969 CreatedAt: time.Now().Add(-2 * time.Hour),
970 UpdatedAt: time.Now().Add(-2 * time.Hour),
971 },
972 {
973 ID: "task-2",
974 Title: "Task 2",
975 Owner: tm.Owner{ID: "user2", Name: "User 2"},
976 Status: tm.StatusInProgress,
977 Priority: tm.PriorityMedium,
978 CreatedAt: time.Now().Add(-1 * time.Hour),
979 UpdatedAt: time.Now().Add(-1 * time.Hour),
980 },
981 {
982 ID: "task-3",
983 Title: "Task 3",
984 Owner: tm.Owner{ID: "user1", Name: "User 1"},
985 Status: tm.StatusCompleted,
986 Priority: tm.PriorityLow,
987 CreatedAt: time.Now(),
988 UpdatedAt: time.Now(),
989 },
990 }
991
992 err := gtm.ensureTasksDir()
993 require.NoError(t, err)
994
995 for _, task := range tasks {
996 err = gtm.writeTaskFile(task)
997 require.NoError(t, err)
998 }
999
1000 ctx := context.Background()
1001
1002 // Get tasks by priority
1003 taskList, err := gtm.GetTasksByPriority(ctx, tm.PriorityHigh, 0, 10)
1004 assert.NoError(t, err)
1005 assert.NotNil(t, taskList)
1006 assert.Len(t, taskList.Tasks, 1)
1007 assert.Equal(t, tm.PriorityHigh, taskList.Tasks[0].Priority)
1008}
1009
1010// Helper functions for creating pointers to string, TaskStatus, and TaskPriority
1011func stringPtr(s string) *string {
1012 return &s
1013}
1014
1015func taskStatusPtr(status tm.TaskStatus) *tm.TaskStatus {
1016 return &status
1017}
1018
1019func taskPriorityPtr(priority tm.TaskPriority) *tm.TaskPriority {
1020 return &priority
1021}