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