diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-23 06:50:43 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-23 06:50:43 +0000 |
| commit | b0688c8819da1b7fcb4a97b6ec1fa58050e4841e (patch) | |
| tree | c62b4ea3e5cdb56225c39ad930dd3e5584053827 /internal/handlers/agent_test.go | |
| parent | ef7a45361996b7a49226a0b088e2599f2801d017 (diff) | |
feat: complete Agent Context API Phase 2 & 3 (Write/Create/Management)
- Implement write operations (complete, uncomplete, update due date, update task)
- Implement create operations (create task, add shopping item)
- Add Trusted Agents management UI in Settings with revocation support
- Fix SQLite timestamp scanning bug for completed tasks
- Add comprehensive unit tests for all new agent endpoints
- Update worklog and feature documentation
Diffstat (limited to 'internal/handlers/agent_test.go')
| -rw-r--r-- | internal/handlers/agent_test.go | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/internal/handlers/agent_test.go b/internal/handlers/agent_test.go index 5775962..eab1609 100644 --- a/internal/handlers/agent_test.go +++ b/internal/handlers/agent_test.go @@ -2,12 +2,15 @@ package handlers import ( "bytes" + "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" + "github.com/go-chi/chi/v5" + "task-dashboard/internal/config" "task-dashboard/internal/models" ) @@ -738,6 +741,155 @@ func TestHandleAgentWebContext(t *testing.T) { } } +func TestHandleAgentTaskWriteOperations(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + // Mock clients + mockTodoist := &mockTodoistClient{} + mockTrello := &mockTrelloClient{} + h := &Handler{ + store: db, + todoistClient: mockTodoist, + trelloClient: mockTrello, + config: &config.Config{}, + } + + // Create an approved session + sessionToken := "write-session-token" + session := &models.AgentSession{ + RequestToken: "write-request-token", + AgentName: "WriteAgent", + AgentID: "write-agent-uuid", + ExpiresAt: time.Now().Add(5 * time.Minute), + } + db.CreateAgentSession(session) + db.ApproveAgentSession("write-request-token", sessionToken, time.Now().Add(1*time.Hour)) + + // Pre-populate store with a task to get details during completion + db.SaveTasks([]models.Task{{ID: "task123", Content: "Test Task"}}) + + t.Run("complete task", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/agent/tasks/task123/complete?source=todoist", nil) + w := httptest.NewRecorder() + + // Manually inject session into context as middleware would + ctx := context.WithValue(req.Context(), agentSessionContextKey, session) + req = req.WithContext(ctx) + + // Set chi URL param + rctx := chi.NewRouteContext() + rctx.URLParams.Add("id", "task123") + req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) + + h.HandleAgentTaskComplete(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) + } + + // Verify task was logged as completed + completed, err := db.GetCompletedTasks(1) + if err != nil { + t.Fatalf("Failed to get completed tasks: %v", err) + } + if len(completed) == 0 { + t.Fatal("Expected task to be logged as completed, but got 0 results") + } + if completed[0].SourceID != "task123" { + t.Errorf("Expected source ID task123, got %s", completed[0].SourceID) + } + }) + + t.Run("update due date", func(t *testing.T) { + due := time.Now().Add(24 * time.Hour) + body, _ := json.Marshal(map[string]*time.Time{"due": &due}) + req := httptest.NewRequest(http.MethodPatch, "/agent/tasks/task123/due?source=todoist", bytes.NewReader(body)) + w := httptest.NewRecorder() + + rctx := chi.NewRouteContext() + rctx.URLParams.Add("id", "task123") + req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) + + h.HandleAgentTaskUpdateDue(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) + } + }) + + t.Run("update task details", func(t *testing.T) { + body, _ := json.Marshal(map[string]string{"title": "Updated Title", "description": "New Desc"}) + req := httptest.NewRequest(http.MethodPatch, "/agent/tasks/task123?source=todoist", bytes.NewReader(body)) + w := httptest.NewRecorder() + + rctx := chi.NewRouteContext() + rctx.URLParams.Add("id", "task123") + req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) + + h.HandleAgentTaskUpdate(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) + } + }) +} + +func TestHandleAgentCreateOperations(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + mockTodoist := &mockTodoistClient{} + h := &Handler{ + store: db, + todoistClient: mockTodoist, + config: &config.Config{}, + } + + t.Run("create task", func(t *testing.T) { + body, _ := json.Marshal(map[string]string{ + "title": "New Agent Task", + "source": "todoist", + }) + req := httptest.NewRequest(http.MethodPost, "/agent/tasks", bytes.NewReader(body)) + w := httptest.NewRecorder() + + h.HandleAgentTaskCreate(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) + } + }) + + t.Run("add shopping item", func(t *testing.T) { + body, _ := json.Marshal(map[string]string{ + "name": "Milk", + "store": "Costco", + }) + req := httptest.NewRequest(http.MethodPost, "/agent/shopping", bytes.NewReader(body)) + w := httptest.NewRecorder() + + h.HandleAgentShoppingAdd(w, req) + + if w.Code != http.StatusOK { + t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String()) + } + + // Verify item was saved + items, _ := db.GetUserShoppingItems() + found := false + for _, item := range items { + if item.Name == "Milk" && item.Store == "Costco" { + found = true + break + } + } + if !found { + t.Error("Expected shopping item to be saved") + } + }) +} + // contains is a helper function to check if a string contains a substring func contains(s, substr string) bool { return bytes.Contains([]byte(s), []byte(substr)) |
