diff options
| -rw-r--r-- | cmd/dashboard/main.go | 2 | ||||
| -rw-r--r-- | internal/api/google_calendar.go | 3 | ||||
| -rw-r--r-- | internal/api/http.go | 4 | ||||
| -rw-r--r-- | internal/api/plantoeat.go | 25 | ||||
| -rw-r--r-- | internal/api/research_test.go | 18 | ||||
| -rw-r--r-- | internal/api/todoist_test.go | 12 | ||||
| -rw-r--r-- | internal/api/trello_test.go | 10 | ||||
| -rw-r--r-- | internal/auth/auth_test.go | 8 | ||||
| -rw-r--r-- | internal/auth/handlers_test.go | 4 | ||||
| -rw-r--r-- | internal/handlers/handlers.go | 29 | ||||
| -rw-r--r-- | internal/handlers/handlers_test.go | 12 | ||||
| -rw-r--r-- | internal/handlers/response.go | 4 | ||||
| -rw-r--r-- | internal/handlers/tab_state_test.go | 4 | ||||
| -rw-r--r-- | internal/handlers/timeline_logic_test.go | 8 | ||||
| -rw-r--r-- | internal/store/sqlite.go | 36 | ||||
| -rw-r--r-- | internal/store/sqlite_test.go | 22 | ||||
| -rw-r--r-- | test/acceptance_test.go | 50 |
17 files changed, 116 insertions, 135 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 2d2f63b..40a5002 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -39,7 +39,7 @@ func main() { if err != nil { log.Fatalf("Failed to initialize database: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Initialize session manager sessionManager := scs.New() diff --git a/internal/api/google_calendar.go b/internal/api/google_calendar.go index dc61f3d..e4f9c2f 100644 --- a/internal/api/google_calendar.go +++ b/internal/api/google_calendar.go @@ -55,7 +55,8 @@ func deduplicateEvents(events []models.CalendarEvent) []models.CalendarEvent { // NewGoogleCalendarClient creates a client that fetches from multiple calendars. // calendarIDs can be comma-separated (e.g., "cal1@group.calendar.google.com,cal2@group.calendar.google.com") func NewGoogleCalendarClient(ctx context.Context, credentialsFile, calendarIDs string) (*GoogleCalendarClient, error) { - srv, err := calendar.NewService(ctx, option.WithCredentialsFile(credentialsFile)) + // Use type-safe credential loading (replaces deprecated WithCredentialsFile) + srv, err := calendar.NewService(ctx, option.WithAuthCredentialsFile(option.ServiceAccount, credentialsFile)) if err != nil { return nil, fmt.Errorf("unable to retrieve Calendar client: %v", err) } diff --git a/internal/api/http.go b/internal/api/http.go index 8854625..df28c65 100644 --- a/internal/api/http.go +++ b/internal/api/http.go @@ -95,7 +95,7 @@ func (c *BaseClient) PostEmpty(ctx context.Context, path string, headers map[str if err != nil { return fmt.Errorf("request failed: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode < 200 || resp.StatusCode >= 300 { body, _ := io.ReadAll(resp.Body) @@ -126,7 +126,7 @@ func (c *BaseClient) doJSON(req *http.Request, result interface{}) error { if err != nil { return fmt.Errorf("request failed: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode < 200 || resp.StatusCode >= 300 { body, _ := io.ReadAll(resp.Body) diff --git a/internal/api/plantoeat.go b/internal/api/plantoeat.go index a7fdf58..5c24cc1 100644 --- a/internal/api/plantoeat.go +++ b/internal/api/plantoeat.go @@ -39,27 +39,6 @@ func (c *PlanToEatClient) SetSessionCookie(cookie string) { c.sessionCookie = cookie } -func (c *PlanToEatClient) authHeaders() map[string]string { - return map[string]string{"Authorization": "Bearer " + c.apiKey} -} - -// planToEatPlannerItem represents a planner item from the API -type planToEatPlannerItem struct { - ID int `json:"id"` - Date string `json:"date"` - MealType string `json:"meal_type"` - Recipe struct { - ID int `json:"id"` - Title string `json:"title"` - URL string `json:"url"` - } `json:"recipe"` -} - -// planToEatResponse wraps the API response -type planToEatResponse struct { - Items []planToEatPlannerItem `json:"items"` -} - // GetUpcomingMeals fetches meals by scraping the planner web interface // Requires a valid session cookie set via SetSessionCookie func (c *PlanToEatClient) GetUpcomingMeals(ctx context.Context, days int) ([]models.Meal, error) { @@ -84,7 +63,7 @@ func (c *PlanToEatClient) GetUpcomingMeals(ctx context.Context, days int) ([]mod if err != nil { return nil, fmt.Errorf("request failed: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() log.Printf("DEBUG [PlanToEat/Meals]: Response status %d", resp.StatusCode) @@ -238,7 +217,7 @@ func (c *PlanToEatClient) GetShoppingList(ctx context.Context) ([]models.Shoppin if err != nil { return nil, fmt.Errorf("request failed: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() log.Printf("DEBUG [PlanToEat/Shopping]: Response status %d", resp.StatusCode) diff --git a/internal/api/research_test.go b/internal/api/research_test.go index 83a52b4..f2519e2 100644 --- a/internal/api/research_test.go +++ b/internal/api/research_test.go @@ -45,7 +45,7 @@ func TestTodoistSyncResearch(t *testing.T) { if err != nil { t.Fatalf("Failed to call Sync API: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { @@ -102,13 +102,13 @@ func TestTodoistSyncResearch(t *testing.T) { if err != nil { t.Fatalf("Incremental sync failed: %v", err) } - defer resp2.Body.Close() + defer func() { _ = resp2.Body.Close() }() body2, _ := io.ReadAll(resp2.Body) t.Logf("Incremental response size: %d bytes (vs full: %d bytes)", len(body2), len(body)) var syncResp2 map[string]interface{} - json.Unmarshal(body2, &syncResp2) + _ = json.Unmarshal(body2, &syncResp2) if items2, ok := syncResp2["items"].([]interface{}); ok { t.Logf("Incremental items count: %d", len(items2)) } @@ -142,7 +142,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Fatalf("Failed to fetch boards: %v", err) } body, _ := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() fullDuration := time.Since(start) t.Logf("Test 1 - Full boards response:") @@ -150,7 +150,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Logf(" Duration: %v", fullDuration) var boards []map[string]interface{} - json.Unmarshal(body, &boards) + _ = json.Unmarshal(body, &boards) t.Logf(" Board count: %d", len(boards)) if len(boards) > 0 { t.Logf(" Fields in first board: %v", getKeys(boards[0])) @@ -170,7 +170,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Fatalf("Failed to fetch limited boards: %v", err) } body2, _ := io.ReadAll(resp2.Body) - resp2.Body.Close() + _ = resp2.Body.Close() limitedDuration := time.Since(start) t.Logf("\nTest 2 - Limited fields (id,name):") @@ -196,7 +196,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Fatalf("Failed to fetch board with cards: %v", err) } body3, _ := io.ReadAll(resp3.Body) - resp3.Body.Close() + _ = resp3.Body.Close() batchDuration := time.Since(start) t.Logf("\nTest 3 - Single board with cards/lists embedded:") @@ -204,7 +204,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Logf(" Duration: %v", batchDuration) var boardWithCards map[string]interface{} - json.Unmarshal(body3, &boardWithCards) + _ = json.Unmarshal(body3, &boardWithCards) if cards, ok := boardWithCards["cards"].([]interface{}); ok { t.Logf(" Cards count: %d", len(cards)) } @@ -227,7 +227,7 @@ func TestTrelloOptimizationResearch(t *testing.T) { t.Logf(" 'since' parameter: Error - %v", err) } else { body4, _ := io.ReadAll(resp4.Body) - resp4.Body.Close() + _ = resp4.Body.Close() t.Logf(" 'since' parameter: Status %d, Size %d bytes", resp4.StatusCode, len(body4)) } } diff --git a/internal/api/todoist_test.go b/internal/api/todoist_test.go index 2fa6e28..7bbcc1e 100644 --- a/internal/api/todoist_test.go +++ b/internal/api/todoist_test.go @@ -13,7 +13,7 @@ import ( // newTestTodoistClient creates a TodoistClient for testing with custom base URL func newTestTodoistClient(baseURL, apiKey string) *TodoistClient { client := NewTodoistClient(apiKey) - client.BaseClient.BaseURL = baseURL + client.BaseURL = baseURL client.syncClient.BaseURL = baseURL return client } @@ -62,7 +62,7 @@ func TestTodoistClient_CreateTask(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer server.Close() @@ -96,7 +96,7 @@ func TestTodoistClient_CreateTask_WithDueDate(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Parse JSON body var payload map[string]interface{} - json.NewDecoder(r.Body).Decode(&payload) + _ = json.NewDecoder(r.Body).Decode(&payload) // Verify due_date if payload["due_date"] != "2026-01-15" { @@ -117,7 +117,7 @@ func TestTodoistClient_CreateTask_WithDueDate(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer server.Close() @@ -181,7 +181,7 @@ func TestTodoistClient_CompleteTask_Error(t *testing.T) { // Mock server that returns error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) - w.Write([]byte(`{"error":"Task not found"}`)) + _, _ = w.Write([]byte(`{"error":"Task not found"}`)) })) defer server.Close() @@ -218,7 +218,7 @@ func TestTodoistClient_GetProjects(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer server.Close() diff --git a/internal/api/trello_test.go b/internal/api/trello_test.go index 7433ff0..d677363 100644 --- a/internal/api/trello_test.go +++ b/internal/api/trello_test.go @@ -15,7 +15,7 @@ import ( // newTestTrelloClient creates a TrelloClient for testing with custom base URL func newTestTrelloClient(baseURL, apiKey, token string) *TrelloClient { client := NewTrelloClient(apiKey, token) - client.BaseClient.BaseURL = baseURL + client.BaseURL = baseURL return client } @@ -67,7 +67,7 @@ func TestTrelloClient_CreateCard(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer server.Close() @@ -122,7 +122,7 @@ func TestTrelloClient_CreateCard_WithDueDate(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) })) defer server.Close() @@ -176,7 +176,7 @@ func TestTrelloClient_UpdateCard(t *testing.T) { // Return 200 OK w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"id":"card-123","name":"Updated Name"}`)) + _, _ = w.Write([]byte(`{"id":"card-123","name":"Updated Name"}`)) })) defer server.Close() @@ -201,7 +201,7 @@ func TestTrelloClient_UpdateCard_Error(t *testing.T) { // Mock server that returns error server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(`{"error":"Invalid card ID"}`)) + _, _ = w.Write([]byte(`{"error":"Invalid card ID"}`)) })) defer server.Close() diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index 505efe3..013a4aa 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -15,7 +15,7 @@ func TestAuthenticate(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - defer db.Close() + defer func() { _ = db.Close() }() service := NewService(db) @@ -34,7 +34,7 @@ func TestAuthenticate(t *testing.T) { t.Errorf("expected no error, got %v", err) } if user == nil { - t.Errorf("expected user, got nil") + t.Fatal("expected user, got nil") } if user.Username != "testuser" { t.Errorf("expected username testuser, got %s", user.Username) @@ -50,7 +50,7 @@ func TestAuthenticate_InvalidCredentials(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - defer db.Close() + defer func() { _ = db.Close() }() service := NewService(db) @@ -73,7 +73,7 @@ func TestCreateUser(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - defer db.Close() + defer func() { _ = db.Close() }() service := NewService(db) diff --git a/internal/auth/handlers_test.go b/internal/auth/handlers_test.go index 3e154ce..128ae80 100644 --- a/internal/auth/handlers_test.go +++ b/internal/auth/handlers_test.go @@ -20,7 +20,7 @@ func TestHandleLogin(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - defer db.Close() + defer func() { _ = db.Close() }() service := NewService(db) sessionManager := scs.New() @@ -68,7 +68,7 @@ func TestHandleLogin_InvalidCredentials(t *testing.T) { if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } - defer db.Close() + defer func() { _ = db.Close() }() service := NewService(db) sessionManager := scs.New() diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index e0e185d..a169478 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -659,9 +659,9 @@ func (h *Handler) handleAtomToggle(w http.ResponseWriter, r *http.Request, compl // Remove from local cache switch source { case "todoist": - h.store.DeleteTask(id) + _ = h.store.DeleteTask(id) case "trello": - h.store.DeleteCard(id) + _ = h.store.DeleteCard(id) } // Return completed task HTML with uncomplete option @@ -675,9 +675,9 @@ func (h *Handler) handleAtomToggle(w http.ResponseWriter, r *http.Request, compl // Invalidate cache to force refresh switch source { case "todoist": - h.store.InvalidateCache(store.CacheKeyTodoistTasks) + _ = h.store.InvalidateCache(store.CacheKeyTodoistTasks) case "trello": - h.store.InvalidateCache(store.CacheKeyTrelloBoards) + _ = h.store.InvalidateCache(store.CacheKeyTrelloBoards) } // Don't swap empty response - just trigger refresh w.Header().Set("HX-Reswap", "none") @@ -710,10 +710,11 @@ func (h *Handler) getAtomTitle(id, source string) string { case "bug": if bugs, err := h.store.GetBugs(); err == nil { var bugID int64 - fmt.Sscanf(id, "bug-%d", &bugID) - for _, b := range bugs { - if b.ID == bugID { - return b.Description + if _, err := fmt.Sscanf(id, "bug-%d", &bugID); err == nil { + for _, b := range bugs { + if b.ID == bugID { + return b.Description + } } } } @@ -752,7 +753,7 @@ func (h *Handler) HandleUnifiedAdd(w http.ResponseWriter, r *http.Request) { JSONError(w, http.StatusInternalServerError, "Failed to create Todoist task", err) return } - h.store.InvalidateCache(store.CacheKeyTodoistTasks) + _ = h.store.InvalidateCache(store.CacheKeyTodoistTasks) case "trello": listID := r.FormValue("list_id") @@ -764,7 +765,7 @@ func (h *Handler) HandleUnifiedAdd(w http.ResponseWriter, r *http.Request) { JSONError(w, http.StatusInternalServerError, "Failed to create Trello card", err) return } - h.store.InvalidateCache(store.CacheKeyTrelloBoards) + _ = h.store.InvalidateCache(store.CacheKeyTrelloBoards) default: JSONError(w, http.StatusBadRequest, "Invalid source", nil) @@ -791,7 +792,7 @@ func (h *Handler) HandleGetListsOptions(w http.ResponseWriter, r *http.Request) w.Header().Set("Content-Type", "text/html") for _, list := range lists { - fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name) + _, _ = fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name) } } @@ -805,12 +806,12 @@ func (h *Handler) HandleGetBugs(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") if len(bugs) == 0 { - fmt.Fprint(w, `<p class="text-gray-500 text-sm">No bugs reported yet.</p>`) + _, _ = fmt.Fprint(w, `<p class="text-gray-500 text-sm">No bugs reported yet.</p>`) return } for _, bug := range bugs { - fmt.Fprintf(w, `<div class="text-sm border-b border-gray-100 py-2"> + _, _ = fmt.Fprintf(w, `<div class="text-sm border-b border-gray-100 py-2"> <p class="text-gray-900">%s</p> <p class="text-gray-400 text-xs">%s</p> </div>`, template.HTMLEscapeString(bug.Description), bug.CreatedAt.Format("Jan 2, 3:04 PM")) @@ -916,7 +917,7 @@ func (h *Handler) HandleGetShoppingLists(w http.ResponseWriter, r *http.Request) w.Header().Set("Content-Type", "text/html") for _, list := range lists { - fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name) + _, _ = fmt.Fprintf(w, `<option value="%s">%s</option>`, list.ID, list.Name) } } diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go index 3658e0e..d863546 100644 --- a/internal/handlers/handlers_test.go +++ b/internal/handlers/handlers_test.go @@ -26,7 +26,7 @@ func setupTestDB(t *testing.T) (*store.Store, func()) { if err != nil { t.Fatalf("Failed to create temp db: %v", err) } - tmpFile.Close() + _ = tmpFile.Close() // Save current directory and change to project root // This ensures migrations can be found @@ -43,18 +43,18 @@ func setupTestDB(t *testing.T) (*store.Store, func()) { // Initialize store (this runs migrations) db, err := store.New(tmpFile.Name(), "migrations") if err != nil { - os.Chdir(originalDir) - os.Remove(tmpFile.Name()) + _ = os.Chdir(originalDir) + _ = os.Remove(tmpFile.Name()) t.Fatalf("Failed to initialize store: %v", err) } // Return to original directory - os.Chdir(originalDir) + _ = os.Chdir(originalDir) // Return cleanup function cleanup := func() { - db.Close() - os.Remove(tmpFile.Name()) + _ = db.Close() + _ = os.Remove(tmpFile.Name()) } return db, cleanup diff --git a/internal/handlers/response.go b/internal/handlers/response.go index 3976f02..9a7ab45 100644 --- a/internal/handlers/response.go +++ b/internal/handlers/response.go @@ -10,7 +10,7 @@ import ( // JSONResponse writes data as JSON with appropriate headers func JSONResponse(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(data) + _ = json.NewEncoder(w).Encode(data) } // JSONError writes an error response as JSON @@ -32,5 +32,5 @@ func HTMLResponse(w http.ResponseWriter, tmpl *template.Template, name string, d // HTMLString writes an HTML string directly func HTMLString(w http.ResponseWriter, html string) { w.Header().Set("Content-Type", "text/html") - w.Write([]byte(html)) + _, _ = w.Write([]byte(html)) } diff --git a/internal/handlers/tab_state_test.go b/internal/handlers/tab_state_test.go index 60f1340..71c6ed8 100644 --- a/internal/handlers/tab_state_test.go +++ b/internal/handlers/tab_state_test.go @@ -17,7 +17,7 @@ func TestHandleDashboard_TabState(t *testing.T) { if err != nil { t.Fatalf("Failed to create test database: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Create mock API clients todoistClient := api.NewTodoistClient("test-key") @@ -75,7 +75,7 @@ func TestHandleDashboard_TabState(t *testing.T) { h.HandleDashboard(w, req) resp := w.Result() - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) diff --git a/internal/handlers/timeline_logic_test.go b/internal/handlers/timeline_logic_test.go index a0576d6..038f836 100644 --- a/internal/handlers/timeline_logic_test.go +++ b/internal/handlers/timeline_logic_test.go @@ -96,21 +96,21 @@ func TestBuildTimeline(t *testing.T) { // Task: 10:00 taskDate := baseTime.Add(2 * time.Hour) - s.SaveTasks([]models.Task{ + _ = 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{ + _ = 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{ + _ = s.SaveBoards([]models.Board{ { - ID: "b1", + ID: "b1", Name: "Board 1", Cards: []models.Card{ {ID: "c1", Name: "Card 1", DueDate: &cardDate, ListID: "l1"}, diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index c5c52a4..a9a0597 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -151,7 +151,7 @@ func (s *Store) SaveTasks(tasks []models.Task) error { if err != nil { return err } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() // Clear existing tasks first to remove stale data if _, err := tx.Exec(`DELETE FROM tasks`); err != nil { @@ -166,7 +166,7 @@ func (s *Store) SaveTasks(tasks []models.Task) error { if err != nil { return err } - defer stmt.Close() + defer func() { _ = stmt.Close() }() for _, task := range tasks { labelsJSON, _ := json.Marshal(task.Labels) @@ -201,7 +201,7 @@ func (s *Store) GetTasks() ([]models.Task, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() return scanTasks(rows) } @@ -243,13 +243,13 @@ func (s *Store) DeleteTasksByIDs(ids []string) error { if err != nil { return err } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() stmt, err := tx.Prepare(`DELETE FROM tasks WHERE id = ?`) if err != nil { return err } - defer stmt.Close() + defer func() { _ = stmt.Close() }() for _, id := range ids { if _, err := stmt.Exec(id); err != nil { @@ -267,7 +267,7 @@ func (s *Store) SaveMeals(meals []models.Meal) error { if err != nil { return err } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() stmt, err := tx.Prepare(` INSERT OR REPLACE INTO meals @@ -277,7 +277,7 @@ func (s *Store) SaveMeals(meals []models.Meal) error { if err != nil { return err } - defer stmt.Close() + defer func() { _ = stmt.Close() }() for _, meal := range meals { _, err := stmt.Exec( @@ -312,7 +312,7 @@ func (s *Store) GetMeals(startDate, endDate time.Time) ([]models.Meal, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var meals []models.Meal for rows.Next() { @@ -391,7 +391,7 @@ func (s *Store) SaveBoards(boards []models.Board) error { if err != nil { return err } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() // Clear existing data first (cards must be deleted before boards due to foreign key) if _, err := tx.Exec(`DELETE FROM cards`); err != nil { @@ -409,7 +409,7 @@ func (s *Store) SaveBoards(boards []models.Board) error { if err != nil { return err } - defer boardStmt.Close() + defer func() { _ = boardStmt.Close() }() // Save cards cardStmt, err := tx.Prepare(` @@ -420,7 +420,7 @@ func (s *Store) SaveBoards(boards []models.Board) error { if err != nil { return err } - defer cardStmt.Close() + defer func() { _ = cardStmt.Close() }() savedBoards := 0 savedCards := 0 @@ -472,7 +472,7 @@ func (s *Store) GetBoards() ([]models.Board, error) { if err != nil { return nil, err } - defer boardRows.Close() + defer func() { _ = boardRows.Close() }() var boards []models.Board boardMap := make(map[string]int) // Store index, not pointer @@ -501,7 +501,7 @@ func (s *Store) GetBoards() ([]models.Board, error) { if err != nil { return nil, err } - defer cardRows.Close() + defer func() { _ = cardRows.Close() }() for cardRows.Next() { var card models.Card @@ -596,7 +596,7 @@ func (s *Store) GetBugs() ([]Bug, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var bugs []Bug for rows.Next() { @@ -619,7 +619,7 @@ func (s *Store) GetUnresolvedBugs() ([]Bug, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var bugs []Bug for rows.Next() { @@ -665,7 +665,7 @@ func (s *Store) GetUserShoppingItems() ([]UserShoppingItem, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var items []UserShoppingItem for rows.Next() { @@ -701,7 +701,7 @@ func (s *Store) GetTasksByDateRange(start, end time.Time) ([]models.Task, error) if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() return scanTasks(rows) } @@ -722,7 +722,7 @@ func (s *Store) GetCardsByDateRange(start, end time.Time) ([]models.Card, error) if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var cards []models.Card for rows.Next() { diff --git a/internal/store/sqlite_test.go b/internal/store/sqlite_test.go index fc8a3b7..9aef09d 100644 --- a/internal/store/sqlite_test.go +++ b/internal/store/sqlite_test.go @@ -125,7 +125,7 @@ func setupTestStoreWithMeals(t *testing.T) *Store { // TestDeleteTask verifies that DeleteTask removes a task from the cache func TestDeleteTask(t *testing.T) { store := setupTestStoreWithTasks(t) - defer store.Close() + defer func() { _ = store.Close() }() // Save some tasks tasks := []models.Task{ @@ -213,7 +213,7 @@ func TestDeleteTask(t *testing.T) { // TestDeleteTask_NonExistent verifies that deleting a non-existent task doesn't error func TestDeleteTask_NonExistent(t *testing.T) { store := setupTestStoreWithTasks(t) - defer store.Close() + defer func() { _ = store.Close() }() // Delete a task that doesn't exist - should not error err := store.DeleteTask("nonexistent") @@ -225,7 +225,7 @@ func TestDeleteTask_NonExistent(t *testing.T) { // TestDeleteCard verifies that DeleteCard removes a card from the cache func TestDeleteCard(t *testing.T) { store := setupTestStoreWithCards(t) - defer store.Close() + defer func() { _ = store.Close() }() // First create a board _, err := store.db.Exec(`INSERT INTO boards (id, name) VALUES (?, ?)`, "board1", "Test Board") @@ -292,7 +292,7 @@ func TestDeleteCard(t *testing.T) { // TestDeleteCard_NonExistent verifies that deleting a non-existent card doesn't error func TestDeleteCard_NonExistent(t *testing.T) { store := setupTestStoreWithCards(t) - defer store.Close() + defer func() { _ = store.Close() }() // Delete a card that doesn't exist - should not error err := store.DeleteCard("nonexistent") @@ -306,7 +306,7 @@ func TestDeleteCard_NonExistent(t *testing.T) { // where pointers in boardMap became stale when the boards slice grew. func TestSaveAndGetBoards_MultipleBoards(t *testing.T) { store := setupTestStoreWithCards(t) - defer store.Close() + defer func() { _ = store.Close() }() // Create multiple boards with varying numbers of cards // Use enough boards to trigger slice reallocation @@ -411,7 +411,7 @@ func TestSaveAndGetBoards_MultipleBoards(t *testing.T) { // to ensure slice reallocation is thoroughly tested func TestSaveAndGetBoards_ManyBoards(t *testing.T) { store := setupTestStoreWithCards(t) - defer store.Close() + defer func() { _ = store.Close() }() // Create 20 boards with 5 cards each = 100 cards total numBoards := 20 @@ -466,7 +466,7 @@ func TestSaveAndGetBoards_ManyBoards(t *testing.T) { func TestGetTasksByDateRange(t *testing.T) { store := setupTestStoreWithTasks(t) - defer store.Close() + defer func() { _ = store.Close() }() now := time.Now() tomorrow := now.Add(24 * time.Hour) @@ -498,7 +498,7 @@ func TestGetTasksByDateRange(t *testing.T) { func TestGetMealsByDateRange(t *testing.T) { store := setupTestStoreWithMeals(t) - defer store.Close() + defer func() { _ = store.Close() }() now := time.Now() tomorrow := now.Add(24 * time.Hour) @@ -530,7 +530,7 @@ func TestGetMealsByDateRange(t *testing.T) { func TestGetCardsByDateRange(t *testing.T) { store := setupTestStoreWithCards(t) - defer store.Close() + defer func() { _ = store.Close() }() now := time.Now() tomorrow := now.Add(24 * time.Hour) @@ -603,7 +603,7 @@ func setupTestStoreWithBugs(t *testing.T) *Store { func TestBugResolution(t *testing.T) { store := setupTestStoreWithBugs(t) - defer store.Close() + defer func() { _ = store.Close() }() // Save some bugs if err := store.SaveBug("Bug 1"); err != nil { @@ -681,7 +681,7 @@ func TestBugResolution(t *testing.T) { func TestResolveBug_NonExistent(t *testing.T) { store := setupTestStoreWithBugs(t) - defer store.Close() + defer func() { _ = store.Close() }() // Resolving a non-existent bug should not error (no rows affected is fine) err := store.ResolveBug(999) diff --git a/test/acceptance_test.go b/test/acceptance_test.go index 25ddcc9..5d5b09f 100644 --- a/test/acceptance_test.go +++ b/test/acceptance_test.go @@ -34,7 +34,7 @@ func setupTestServer(t *testing.T) (*httptest.Server, *store.Store, *http.Client if err != nil { t.Fatalf("Failed to create temp db: %v", err) } - tmpFile.Close() + _ = tmpFile.Close() // Save current directory and change to project root // This ensures migrations can be found @@ -51,13 +51,13 @@ func setupTestServer(t *testing.T) (*httptest.Server, *store.Store, *http.Client // Initialize store (this runs migrations) db, err := store.New(tmpFile.Name(), "migrations") if err != nil { - os.Chdir(originalDir) - os.Remove(tmpFile.Name()) + _ = os.Chdir(originalDir) + _ = os.Remove(tmpFile.Name()) t.Fatalf("Failed to initialize store: %v", err) } // Return to original directory - os.Chdir(originalDir) + _ = os.Chdir(originalDir) // Auth setup sessionManager := scs.New() @@ -70,7 +70,7 @@ func setupTestServer(t *testing.T) (*httptest.Server, *store.Store, *http.Client authHandlers := auth.NewHandlers(authService, sessionManager, authTemplates) // Ensure default user - authService.EnsureDefaultUser("admin", "password") + _ = authService.EnsureDefaultUser("admin", "password") // Create mock API clients todoistClient := api.NewTodoistClient("test_key") @@ -119,8 +119,8 @@ func setupTestServer(t *testing.T) (*httptest.Server, *store.Store, *http.Client cleanup := func() { server.Close() - db.Close() - os.Remove(tmpFile.Name()) + _ = db.Close() + _ = os.Remove(tmpFile.Name()) } return server, db, client, cleanup @@ -136,7 +136,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to login: %v", err) } - resp.Body.Close() + _ = resp.Body.Close() // Seed database with test data testTasks := []models.Task{ @@ -184,7 +184,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to get tasks: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) @@ -210,7 +210,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to get boards: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) @@ -240,7 +240,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to refresh: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) @@ -253,7 +253,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to get meals: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) @@ -275,7 +275,7 @@ func TestFullWorkflow(t *testing.T) { if err != nil { t.Fatalf("Failed to get dashboard: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusInternalServerError { t.Errorf("Expected status 200 or 500, got %d", resp.StatusCode) @@ -289,7 +289,7 @@ func TestCaching(t *testing.T) { defer cleanup() // Login - client.Get(server.URL + "/test/login") + _, _ = client.Get(server.URL + "/test/login") // Seed initial data testTasks := []models.Task{ @@ -298,8 +298,8 @@ func TestCaching(t *testing.T) { Content: "Initial task", }, } - db.SaveTasks(testTasks) - db.UpdateCacheMetadata("todoist_tasks", 5) + _ = db.SaveTasks(testTasks) + _ = db.UpdateCacheMetadata("todoist_tasks", 5) // Test 1: First request should use cache t.Run("UsesCache", func(t *testing.T) { @@ -307,10 +307,10 @@ func TestCaching(t *testing.T) { if err != nil { t.Fatalf("Failed to get tasks: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() var tasks []models.Task - json.NewDecoder(resp.Body).Decode(&tasks) + _ = json.NewDecoder(resp.Body).Decode(&tasks) if len(tasks) != 1 { t.Errorf("Expected 1 task from cache, got %d", len(tasks)) @@ -324,7 +324,7 @@ func TestCaching(t *testing.T) { if err != nil { t.Fatalf("Failed to refresh: %v", err) } - resp.Body.Close() + _ = resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected refresh to succeed, got status %d", resp.StatusCode) @@ -338,7 +338,7 @@ func TestErrorHandling(t *testing.T) { defer cleanup() // Login - client.Get(server.URL + "/test/login") + _, _ = client.Get(server.URL + "/test/login") // Test 1: Invalid routes should 404 t.Run("InvalidRoute", func(t *testing.T) { @@ -346,7 +346,7 @@ func TestErrorHandling(t *testing.T) { if err != nil { t.Fatalf("Failed to make request: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusNotFound { t.Errorf("Expected status 404, got %d", resp.StatusCode) @@ -362,7 +362,7 @@ func TestErrorHandling(t *testing.T) { if err != nil { t.Fatalf("Failed to make request: %v", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusMethodNotAllowed { t.Errorf("Expected status 405, got %d", resp.StatusCode) @@ -376,14 +376,14 @@ func TestConcurrentRequests(t *testing.T) { defer cleanup() // Login - client.Get(server.URL + "/test/login") + _, _ = client.Get(server.URL + "/test/login") // Seed data testBoards := []models.Board{ {ID: "board1", Name: "Board 1", Cards: []models.Card{}}, {ID: "board2", Name: "Board 2", Cards: []models.Card{}}, } - db.SaveBoards(testBoards) + _ = db.SaveBoards(testBoards) // Make 10 concurrent requests const numRequests = 10 @@ -401,7 +401,7 @@ func TestConcurrentRequests(t *testing.T) { done <- false return } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { errors <- fmt.Errorf("request %d got status %d", id, resp.StatusCode) |
