diff options
| author | Claudomator Agent <agent@claudomator.local> | 2026-03-22 08:56:21 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator.local> | 2026-03-22 08:56:21 +0000 |
| commit | 15a46b0e8d6fc9b986bce6b17b471c4a29cc950c (patch) | |
| tree | 7e8bb8006131de8edacb3247e1411716ecd1f9d0 /internal/api/stories_test.go | |
| parent | 2e0f3aaf2566db9979ca827b9d29884be8fbeee0 (diff) | |
feat: Phase 5 — story elaboration endpoint, approve flow, branch creation
- POST /api/stories/elaborate: runs Claude/Gemini against project LocalPath
to produce a structured story plan (name, branch_name, tasks, validation)
- POST /api/stories/approve: creates story + sequentially-wired tasks/subtasks
from the elaborate output and pushes the story branch to origin
- createStoryBranch helper: git checkout -b + push -u origin
- Tests: TestBuildStoryElaboratePrompt, TestHandleStoryApprove_WiresDepends
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/api/stories_test.go')
| -rw-r--r-- | internal/api/stories_test.go | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/internal/api/stories_test.go b/internal/api/stories_test.go index 8516ade..cf522e1 100644 --- a/internal/api/stories_test.go +++ b/internal/api/stories_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "strings" "testing" "github.com/thepeterstone/claudomator/internal/task" @@ -118,3 +119,86 @@ func TestAddTaskToStory_AutoWiresDependsOn(t *testing.T) { t.Errorf("task3.DependsOn: want [%s], got %v", task2.ID, task3.DependsOn) } } + +func TestBuildStoryElaboratePrompt(t *testing.T) { + prompt := buildStoryElaboratePrompt() + checks := []struct { + label string + want string + }{ + {"schema: name field", `"name"`}, + {"schema: branch_name field", `"branch_name"`}, + {"schema: tasks field", `"tasks"`}, + {"schema: validation field", `"validation"`}, + {"rule: git push", "git push origin"}, + {"rule: sequential subtasks", "sequentially"}, + {"rule: specific file paths", "file paths"}, + } + for _, c := range checks { + if !strings.Contains(prompt, c.want) { + t.Errorf("%s: prompt should contain %q", c.label, c.want) + } + } +} + +func TestHandleStoryApprove_WiresDepends(t *testing.T) { + srv, _ := testServer(t) + + body := `{ + "name": "My Story", + "branch_name": "story/my-story", + "tasks": [ + {"name": "Task 1", "instructions": "do task 1", "subtasks": []}, + {"name": "Task 2", "instructions": "do task 2", "subtasks": []}, + {"name": "Task 3", "instructions": "do task 3", "subtasks": []} + ], + "validation": {"type": "build", "steps": ["go build ./..."], "success_criteria": "compiles"} + }` + req := httptest.NewRequest("POST", "/api/stories/approve", bytes.NewBufferString(body)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + srv.mux.ServeHTTP(w, req) + + if w.Code != http.StatusCreated { + t.Fatalf("expected 201, got %d: %s", w.Code, w.Body.String()) + } + + var resp struct { + Story task.Story `json:"story"` + TaskIDs []string `json:"task_ids"` + } + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if len(resp.TaskIDs) != 3 { + t.Fatalf("expected 3 task IDs, got %d", len(resp.TaskIDs)) + } + if resp.Story.Name != "My Story" { + t.Errorf("story name: want 'My Story', got %q", resp.Story.Name) + } + + // Verify depends_on chain via the store. + store := srv.store + task1, err := store.GetTask(resp.TaskIDs[0]) + if err != nil { + t.Fatalf("GetTask[0]: %v", err) + } + task2, err := store.GetTask(resp.TaskIDs[1]) + if err != nil { + t.Fatalf("GetTask[1]: %v", err) + } + task3, err := store.GetTask(resp.TaskIDs[2]) + if err != nil { + t.Fatalf("GetTask[2]: %v", err) + } + + if len(task1.DependsOn) != 0 { + t.Errorf("task1.DependsOn: want [], got %v", task1.DependsOn) + } + if len(task2.DependsOn) != 1 || task2.DependsOn[0] != task1.ID { + t.Errorf("task2.DependsOn: want [%s], got %v", task1.ID, task2.DependsOn) + } + if len(task3.DependsOn) != 1 || task3.DependsOn[0] != task2.ID { + t.Errorf("task3.DependsOn: want [%s], got %v", task2.ID, task3.DependsOn) + } +} |
