From 25a5b7ecf9ddd31da54e91f87988b77aea857571 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Tue, 3 Feb 2026 15:16:35 -1000 Subject: Add comprehensive test coverage across packages New test files: - api/http_test.go: HTTP client and error handling tests - config/config_test.go: Configuration loading and validation tests - middleware/security_test.go: Security middleware tests - models/atom_test.go: Atom model and conversion tests Expanded test coverage: - api/todoist_test.go: Todoist API client tests - api/trello_test.go: Trello API client tests - auth/auth_test.go: Authentication and CSRF tests - handlers/timeline_logic_test.go: Timeline building logic tests - store/sqlite_test.go: SQLite store operations tests Co-Authored-By: Claude Opus 4.5 --- internal/api/todoist_test.go | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) (limited to 'internal/api/todoist_test.go') diff --git a/internal/api/todoist_test.go b/internal/api/todoist_test.go index 7bbcc1e..f7ca719 100644 --- a/internal/api/todoist_test.go +++ b/internal/api/todoist_test.go @@ -246,3 +246,186 @@ func TestTodoistClient_GetProjects(t *testing.T) { t.Errorf("Project 2 mismatch: got ID=%s Name=%s", projects[1].ID, projects[1].Name) } } + +func TestTodoistClient_GetTasks(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("Expected GET, got %s", r.Method) + } + + w.Header().Set("Content-Type", "application/json") + + // GetTasks also calls GetProjects internally + if r.URL.Path == "/projects" { + response := []todoistProjectResponse{ + {ID: "proj-1", Name: "Project 1"}, + } + json.NewEncoder(w).Encode(response) + return + } + + if r.URL.Path == "/tasks" { + response := []todoistTaskResponse{ + {ID: "task-1", Content: "Task 1", ProjectID: "proj-1", CreatedAt: time.Now().Format(time.RFC3339)}, + {ID: "task-2", Content: "Task 2", ProjectID: "proj-1", CreatedAt: time.Now().Format(time.RFC3339)}, + } + json.NewEncoder(w).Encode(response) + return + } + + t.Errorf("Unexpected path: %s", r.URL.Path) + })) + defer server.Close() + + client := newTestTodoistClient(server.URL, "test-key") + tasks, err := client.GetTasks(context.Background()) + if err != nil { + t.Fatalf("GetTasks failed: %v", err) + } + + if len(tasks) != 2 { + t.Errorf("Expected 2 tasks, got %d", len(tasks)) + } +} + +func TestTodoistClient_ReopenTask(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Expected POST, got %s", r.Method) + } + expectedPath := "/tasks/task-123/reopen" + if r.URL.Path != expectedPath { + t.Errorf("Expected path %s, got %s", expectedPath, r.URL.Path) + } + + w.WriteHeader(http.StatusNoContent) + })) + defer server.Close() + + client := newTestTodoistClient(server.URL, "test-key") + err := client.ReopenTask(context.Background(), "task-123") + if err != nil { + t.Fatalf("ReopenTask failed: %v", err) + } +} + +func TestTodoistClient_UpdateTask(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Expected POST, got %s", r.Method) + } + expectedPath := "/tasks/task-123" + if r.URL.Path != expectedPath { + t.Errorf("Expected path %s, got %s", expectedPath, r.URL.Path) + } + + var payload map[string]interface{} + json.NewDecoder(r.Body).Decode(&payload) + if payload["content"] != "Updated Content" { + t.Errorf("Expected content 'Updated Content', got %v", payload["content"]) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]string{"id": "task-123"}) + })) + defer server.Close() + + client := newTestTodoistClient(server.URL, "test-key") + err := client.UpdateTask(context.Background(), "task-123", map[string]interface{}{"content": "Updated Content"}) + if err != nil { + t.Fatalf("UpdateTask failed: %v", err) + } +} + +func TestTodoistClient_Sync(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Expected POST, got %s", r.Method) + } + if r.URL.Path != "/sync" { + t.Errorf("Expected path /sync, got %s", r.URL.Path) + } + + response := TodoistSyncResponse{ + SyncToken: "new-sync-token", + FullSync: true, + Items: []SyncItemResponse{ + {ID: "item-1", Content: "Item 1", ProjectID: "proj-1"}, + }, + Projects: []SyncProjectResponse{ + {ID: "proj-1", Name: "Project 1"}, + }, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + })) + defer server.Close() + + client := newTestTodoistClient(server.URL, "test-key") + resp, err := client.Sync(context.Background(), "*") + if err != nil { + t.Fatalf("Sync failed: %v", err) + } + + if resp.SyncToken != "new-sync-token" { + t.Errorf("Expected sync token 'new-sync-token', got '%s'", resp.SyncToken) + } + if len(resp.Items) != 1 { + t.Errorf("Expected 1 item, got %d", len(resp.Items)) + } +} + +func TestConvertSyncItemsToTasks(t *testing.T) { + projects := map[string]string{ + "proj-1": "Project 1", + } + + items := []SyncItemResponse{ + { + ID: "item-1", + Content: "Task 1", + Description: "Description 1", + ProjectID: "proj-1", + Priority: 3, + Labels: []string{"label1"}, + }, + { + ID: "item-2", + Content: "Completed Task", + ProjectID: "proj-1", + IsCompleted: true, + }, + } + + tasks := ConvertSyncItemsToTasks(items, projects) + + // Should skip completed task + if len(tasks) != 1 { + t.Errorf("Expected 1 task (excluding completed), got %d", len(tasks)) + } + + if tasks[0].ID != "item-1" { + t.Errorf("Expected task ID 'item-1', got '%s'", tasks[0].ID) + } + if tasks[0].ProjectName != "Project 1" { + t.Errorf("Expected project name 'Project 1', got '%s'", tasks[0].ProjectName) + } +} + +func TestBuildProjectMapFromSync(t *testing.T) { + projects := []SyncProjectResponse{ + {ID: "proj-1", Name: "Project 1"}, + {ID: "proj-2", Name: "Project 2"}, + } + + projectMap := BuildProjectMapFromSync(projects) + + if len(projectMap) != 2 { + t.Errorf("Expected 2 projects in map, got %d", len(projectMap)) + } + + if projectMap["proj-1"] != "Project 1" { + t.Errorf("Expected 'Project 1', got '%s'", projectMap["proj-1"]) + } +} -- cgit v1.2.3