diff options
Diffstat (limited to 'internal/handlers')
| -rw-r--r-- | internal/handlers/handlers.go | 4 | ||||
| -rw-r--r-- | internal/handlers/timeline_logic_test.go | 160 |
2 files changed, 164 insertions, 0 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 5014f39..5c86ce2 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -996,6 +996,10 @@ func (h *Handler) HandleTabTasks(w http.ResponseWriter, r *http.Request) { var currentAtoms, futureAtoms []models.Atom for _, a := range atoms { + // Don't show recurring tasks until the day they're due + if a.IsRecurring && a.IsFuture { + continue + } if a.IsFuture { futureAtoms = append(futureAtoms, a) } else { diff --git a/internal/handlers/timeline_logic_test.go b/internal/handlers/timeline_logic_test.go new file mode 100644 index 0000000..a0576d6 --- /dev/null +++ b/internal/handlers/timeline_logic_test.go @@ -0,0 +1,160 @@ +package handlers + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "task-dashboard/internal/models" + "task-dashboard/internal/store" + + _ "github.com/mattn/go-sqlite3" +) + +// MockCalendarClient implements GoogleCalendarAPI interface for testing +type MockCalendarClient struct { + Events []models.CalendarEvent + Err error +} + +func (m *MockCalendarClient) GetUpcomingEvents(ctx context.Context, maxResults int) ([]models.CalendarEvent, error) { + return m.Events, m.Err +} + +func (m *MockCalendarClient) GetEventsByDateRange(ctx context.Context, start, end time.Time) ([]models.CalendarEvent, error) { + return m.Events, m.Err +} + +func setupTestStore(t *testing.T) *store.Store { + t.Helper() + tempDir := t.TempDir() + dbPath := filepath.Join(tempDir, "test.db") + migrationDir := filepath.Join(tempDir, "migrations") + + if err := os.MkdirAll(migrationDir, 0755); err != nil { + t.Fatalf("Failed to create migration dir: %v", err) + } + + schema := ` + CREATE TABLE IF NOT EXISTS tasks ( + id TEXT PRIMARY KEY, + content TEXT NOT NULL, + description TEXT, + project_id TEXT, + project_name TEXT, + due_date DATETIME, + priority INTEGER DEFAULT 1, + completed BOOLEAN DEFAULT FALSE, + labels TEXT, + url TEXT, + created_at DATETIME, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS meals ( + id TEXT PRIMARY KEY, + recipe_name TEXT NOT NULL, + date DATETIME, + meal_type TEXT, + recipe_url TEXT, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS boards ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + CREATE TABLE IF NOT EXISTS cards ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + board_id TEXT NOT NULL, + list_id TEXT, + list_name TEXT, + due_date DATETIME, + url TEXT, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + ` + if err := os.WriteFile(filepath.Join(migrationDir, "001_init.sql"), []byte(schema), 0644); err != nil { + t.Fatalf("Failed to write migration file: %v", err) + } + + // Initialize store (this creates tables) + s, err := store.New(dbPath, migrationDir) + if err != nil { + t.Fatalf("Failed to create store: %v", err) + } + return s +} + +func TestBuildTimeline(t *testing.T) { + s := setupTestStore(t) + + // Fix a base time: 2023-01-01 08:00:00 + baseTime := time.Date(2023, 1, 1, 8, 0, 0, 0, time.UTC) + + // Task: 10:00 + taskDate := baseTime.Add(2 * time.Hour) + s.SaveTasks([]models.Task{ + {ID: "t1", Content: "Task 1", DueDate: &taskDate}, + }) + + // Meal: Lunch (defaults to 12:00) + mealDate := baseTime // Date part matters + s.SaveMeals([]models.Meal{ + {ID: "m1", RecipeName: "Lunch", Date: mealDate, MealType: "lunch"}, + }) + + // Card: 14:00 + cardDate := baseTime.Add(6 * time.Hour) + s.SaveBoards([]models.Board{ + { + ID: "b1", + Name: "Board 1", + Cards: []models.Card{ + {ID: "c1", Name: "Card 1", DueDate: &cardDate, ListID: "l1"}, + }, + }, + }) + + // Calendar Event: 09:00 + eventDate := baseTime.Add(1 * time.Hour) + mockCal := &MockCalendarClient{ + Events: []models.CalendarEvent{ + {ID: "e1", Summary: "Event 1", Start: eventDate, End: eventDate.Add(1 * time.Hour)}, + }, + } + + // Test Range: Full Day + start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC) + + items, err := BuildTimeline(context.Background(), s, mockCal, start, end) + if err != nil { + t.Fatalf("BuildTimeline failed: %v", err) + } + + if len(items) != 4 { + t.Errorf("Expected 4 items, got %d", len(items)) + } + + // Expected Order: + // 1. Event (09:00) + // 2. Task (10:00) + // 3. Meal (12:00) + // 4. Card (14:00) + + if items[0].Type != models.TimelineItemTypeEvent { + t.Errorf("Expected item 0 to be Event, got %s", items[0].Type) + } + if items[1].Type != models.TimelineItemTypeTask { + t.Errorf("Expected item 1 to be Task, got %s", items[1].Type) + } + if items[2].Type != models.TimelineItemTypeMeal { + t.Errorf("Expected item 2 to be Meal, got %s", items[2].Type) + } + if items[3].Type != models.TimelineItemTypeCard { + t.Errorf("Expected item 3 to be Card, got %s", items[3].Type) + } +} |
