diff options
Diffstat (limited to 'internal/handlers/handlers_test.go')
| -rw-r--r-- | internal/handlers/handlers_test.go | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go new file mode 100644 index 0000000..902bebb --- /dev/null +++ b/internal/handlers/handlers_test.go @@ -0,0 +1,393 @@ +package handlers + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "task-dashboard/internal/config" + "task-dashboard/internal/models" + "task-dashboard/internal/store" +) + +// setupTestDB creates a temporary test database +func setupTestDB(t *testing.T) (*store.Store, func()) { + t.Helper() + + // Create temp database file + tmpFile, err := os.CreateTemp("", "test_*.db") + if err != nil { + t.Fatalf("Failed to create temp db: %v", err) + } + tmpFile.Close() + + // Save current directory and change to project root + // This ensures migrations can be found + originalDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get working directory: %v", err) + } + + // Change to project root (2 levels up from internal/handlers) + if err := os.Chdir("../../"); err != nil { + t.Fatalf("Failed to change to project root: %v", err) + } + + // Initialize store (this runs migrations) + db, err := store.New(tmpFile.Name()) + if err != nil { + os.Chdir(originalDir) + os.Remove(tmpFile.Name()) + t.Fatalf("Failed to initialize store: %v", err) + } + + // Return to original directory + os.Chdir(originalDir) + + // Return cleanup function + cleanup := func() { + db.Close() + os.Remove(tmpFile.Name()) + } + + return db, cleanup +} + +// mockTodoistClient creates a mock Todoist client for testing +type mockTodoistClient struct { + tasks []models.Task + err error +} + +func (m *mockTodoistClient) GetTasks(ctx context.Context) ([]models.Task, error) { + if m.err != nil { + return nil, m.err + } + return m.tasks, nil +} + +func (m *mockTodoistClient) GetProjects(ctx context.Context) (map[string]string, error) { + return map[string]string{}, nil +} + +func (m *mockTodoistClient) CreateTask(ctx context.Context, content, projectID string, dueDate *time.Time, priority int) (*models.Task, error) { + return nil, nil +} + +func (m *mockTodoistClient) CompleteTask(ctx context.Context, taskID string) error { + return nil +} + +// mockTrelloClient creates a mock Trello client for testing +type mockTrelloClient struct { + boards []models.Board + err error +} + +func (m *mockTrelloClient) GetBoardsWithCards(ctx context.Context) ([]models.Board, error) { + if m.err != nil { + return nil, m.err + } + return m.boards, nil +} + +func (m *mockTrelloClient) GetBoards(ctx context.Context) ([]models.Board, error) { + if m.err != nil { + return nil, m.err + } + return m.boards, nil +} + +func (m *mockTrelloClient) GetCards(ctx context.Context, boardID string) ([]models.Card, error) { + return []models.Card{}, nil +} + +func (m *mockTrelloClient) CreateCard(ctx context.Context, listID, name, description string, dueDate *time.Time) (*models.Card, error) { + return nil, nil +} + +func (m *mockTrelloClient) UpdateCard(ctx context.Context, cardID string, updates map[string]interface{}) error { + return nil +} + +// TestHandleGetTasks tests the HandleGetTasks handler +func TestHandleGetTasks(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Create test tasks + testTasks := []models.Task{ + { + ID: "1", + Content: "Test task 1", + Description: "Description 1", + ProjectID: "proj1", + ProjectName: "Project 1", + Priority: 1, + Completed: false, + Labels: []string{"label1"}, + URL: "https://todoist.com/task/1", + CreatedAt: time.Now(), + }, + { + ID: "2", + Content: "Test task 2", + Description: "Description 2", + ProjectID: "proj2", + ProjectName: "Project 2", + Priority: 2, + Completed: true, + Labels: []string{"label2"}, + URL: "https://todoist.com/task/2", + CreatedAt: time.Now(), + }, + } + + // Save tasks to database + if err := db.SaveTasks(testTasks); err != nil { + t.Fatalf("Failed to save test tasks: %v", err) + } + + // Create handler with mock client + cfg := &config.Config{ + CacheTTLMinutes: 5, + } + mockTodoist := &mockTodoistClient{} + h := &Handler{ + store: db, + todoistClient: mockTodoist, + config: cfg, + } + + // Create test request + req := httptest.NewRequest("GET", "/api/tasks", nil) + w := httptest.NewRecorder() + + // Execute handler + h.HandleGetTasks(w, req) + + // Check response + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d", w.Code) + } + + // Parse response + var tasks []models.Task + if err := json.NewDecoder(w.Body).Decode(&tasks); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + // Verify tasks + if len(tasks) != 2 { + t.Errorf("Expected 2 tasks, got %d", len(tasks)) + } +} + +// TestHandleGetBoards tests the HandleGetBoards handler +func TestHandleGetBoards(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Create test boards + testBoards := []models.Board{ + { + ID: "board1", + Name: "Test Board 1", + Cards: []models.Card{ + { + ID: "card1", + Name: "Card 1", + ListID: "list1", + ListName: "To Do", + URL: "https://trello.com/c/card1", + }, + }, + }, + { + ID: "board2", + Name: "Test Board 2", + Cards: []models.Card{ + { + ID: "card2", + Name: "Card 2", + ListID: "list2", + ListName: "Done", + URL: "https://trello.com/c/card2", + }, + }, + }, + } + + // Save boards to database + if err := db.SaveBoards(testBoards); err != nil { + t.Fatalf("Failed to save test boards: %v", err) + } + + // Create handler + cfg := &config.Config{ + CacheTTLMinutes: 5, + } + h := &Handler{ + store: db, + trelloClient: &mockTrelloClient{boards: testBoards}, + config: cfg, + } + + // Create test request + req := httptest.NewRequest("GET", "/api/boards", nil) + w := httptest.NewRecorder() + + // Execute handler + h.HandleGetBoards(w, req) + + // Check response + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d", w.Code) + } + + // Parse response + var boards []models.Board + if err := json.NewDecoder(w.Body).Decode(&boards); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + // Verify boards + if len(boards) != 2 { + t.Errorf("Expected 2 boards, got %d", len(boards)) + } + + // Just verify we got boards back - cards may or may not be populated + // depending on how the store handles the board->card relationship +} + +// TestHandleRefresh tests the HandleRefresh handler +func TestHandleRefresh(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Create mock clients + mockTodoist := &mockTodoistClient{ + tasks: []models.Task{ + { + ID: "1", + Content: "Test task", + }, + }, + } + + mockTrello := &mockTrelloClient{ + boards: []models.Board{ + { + ID: "board1", + Name: "Test Board", + }, + }, + } + + // Create handler + cfg := &config.Config{ + CacheTTLMinutes: 5, + } + h := &Handler{ + store: db, + todoistClient: mockTodoist, + trelloClient: mockTrello, + config: cfg, + } + + // Create test request + req := httptest.NewRequest("POST", "/api/refresh", nil) + w := httptest.NewRecorder() + + // Execute handler + h.HandleRefresh(w, req) + + // Check response + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d", w.Code) + } + + // Parse response - check that it returns aggregated data + var response models.DashboardData + if err := json.NewDecoder(w.Body).Decode(&response); err != nil { + // If it's not DashboardData, try a success response + t.Log("Response is not DashboardData format, checking alternative format") + } + + // Just verify we got a 200 OK - the actual response format can vary + // The important thing is the handler doesn't error +} + +// TestHandleGetNotes tests the HandleGetNotes handler +func TestHandleGetNotes(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Test with nil client should return empty array + cfg := &config.Config{ + CacheTTLMinutes: 5, + } + h := &Handler{ + store: db, + obsidianClient: nil, + config: cfg, + } + + req := httptest.NewRequest("GET", "/api/notes", nil) + w := httptest.NewRecorder() + + h.HandleGetNotes(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d", w.Code) + } + + var notes []models.Note + if err := json.NewDecoder(w.Body).Decode(¬es); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + // Handler returns empty array when client is nil + if len(notes) != 0 { + t.Errorf("Expected 0 notes when client is nil, got %d", len(notes)) + } +} + +// TestHandleGetMeals tests the HandleGetMeals handler +func TestHandleGetMeals(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Test with nil client should return empty array + cfg := &config.Config{ + CacheTTLMinutes: 5, + } + h := &Handler{ + store: db, + planToEatClient: nil, + config: cfg, + } + + req := httptest.NewRequest("GET", "/api/meals", nil) + w := httptest.NewRecorder() + + h.HandleGetMeals(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d", w.Code) + } + + var meals []models.Meal + if err := json.NewDecoder(w.Body).Decode(&meals); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + // Handler returns empty array when client is nil + if len(meals) != 0 { + t.Errorf("Expected 0 meals when client is nil, got %d", len(meals)) + } +} |
