Skip to content

Commit 0a6c809

Browse files
josealekhineclaude
andcommitted
test: expand integration tests for binary commands
Add comprehensive integration tests that invoke the actual ctx binary and verify output. Tests now cover all required cases: - ctx init creates expected files - ctx status returns actual status (not help text) - ctx add learning modifies LEARNINGS.md - ctx session save creates session file - ctx agent returns context packet Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 3d8e572 commit 0a6c809

2 files changed

Lines changed: 121 additions & 30 deletions

File tree

.context/TASKS.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@
4242
- [x] Document session persistence in AGENT_PLAYBOOK.md
4343

4444
### Phase 7: Testing & Verification `#priority:high` `#area:quality`
45-
- [ ] Add headers to all files
46-
- [ ] Add integration tests — invoke actual binary, verify output
47-
- [ ] `ctx init` creates expected files
48-
- [ ] `ctx status` returns valid status (not just help text)
49-
- [ ] `ctx add learning "test"` modifies LEARNINGS.md
50-
- [ ] `ctx session save` creates session file
51-
- [ ] `ctx agent` returns context packet
45+
- [x] Add headers to all files
46+
- [x] Add integration tests — invoke actual binary, verify output
47+
- [x] `ctx init` creates expected files
48+
- [x] `ctx status` returns valid status (not just help text)
49+
- [x] `ctx add learning "test"` modifies LEARNINGS.md
50+
- [x] `ctx session save` creates session file
51+
- [x] `ctx agent` returns context packet
5252
- [ ] Set unit test coverage target (70% for internal/cli, internal/context)
5353
- [ ] Add coverage reporting to `make test`
5454
- [ ] Add smoke test to CI/Makefile: build binary, run basic commands

internal/cli/cli_test.go

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -342,30 +342,121 @@ func TestBinaryIntegration(t *testing.T) {
342342
t.Fatalf("failed to create test dir: %v", err)
343343
}
344344

345-
// Run init
346-
initCmd := exec.Command(binaryPath, "init")
347-
initCmd.Dir = testDir
348-
if output, err := initCmd.CombinedOutput(); err != nil {
349-
t.Fatalf("ctx init failed: %v\n%s", err, output)
350-
}
345+
// Subtest: ctx init creates expected files
346+
t.Run("init creates expected files", func(t *testing.T) {
347+
initCmd := exec.Command(binaryPath, "init")
348+
initCmd.Dir = testDir
349+
if output, err := initCmd.CombinedOutput(); err != nil {
350+
t.Fatalf("ctx init failed: %v\n%s", err, output)
351+
}
351352

352-
// Check .context exists
353-
ctxDir := filepath.Join(testDir, ".context")
354-
if _, err := os.Stat(ctxDir); os.IsNotExist(err) {
355-
t.Fatal(".context directory was not created")
356-
}
353+
// Check .context directory exists
354+
ctxDir := filepath.Join(testDir, ".context")
355+
if _, err := os.Stat(ctxDir); os.IsNotExist(err) {
356+
t.Fatal(".context directory was not created")
357+
}
357358

358-
// Run status
359-
statusCmd := exec.Command(binaryPath, "status")
360-
statusCmd.Dir = testDir
361-
if output, err := statusCmd.CombinedOutput(); err != nil {
362-
t.Fatalf("ctx status failed: %v\n%s", err, output)
363-
}
359+
// Check required files exist
360+
requiredFiles := []string{
361+
"CONSTITUTION.md",
362+
"TASKS.md",
363+
"DECISIONS.md",
364+
"LEARNINGS.md",
365+
"CONVENTIONS.md",
366+
"ARCHITECTURE.md",
367+
}
368+
for _, name := range requiredFiles {
369+
path := filepath.Join(ctxDir, name)
370+
if _, err := os.Stat(path); os.IsNotExist(err) {
371+
t.Errorf("required file %s was not created", name)
372+
}
373+
}
374+
})
375+
376+
// Subtest: ctx status returns valid status (not just help text)
377+
t.Run("status returns valid status", func(t *testing.T) {
378+
statusCmd := exec.Command(binaryPath, "status")
379+
statusCmd.Dir = testDir
380+
output, err := statusCmd.CombinedOutput()
381+
if err != nil {
382+
t.Fatalf("ctx status failed: %v\n%s", err, output)
383+
}
364384

365-
// Run drift
366-
driftCmd := exec.Command(binaryPath, "drift")
367-
driftCmd.Dir = testDir
368-
if output, err := driftCmd.CombinedOutput(); err != nil {
369-
t.Fatalf("ctx drift failed: %v\n%s", err, output)
370-
}
385+
outputStr := string(output)
386+
// Verify it's actual status output, not help text
387+
if strings.Contains(outputStr, "Usage:") || strings.Contains(outputStr, "Available Commands:") {
388+
t.Error("ctx status returned help text instead of status")
389+
}
390+
// Check for expected status output markers
391+
if !strings.Contains(outputStr, "Context Status") && !strings.Contains(outputStr, "Context Directory") {
392+
t.Errorf("ctx status did not return expected status output, got:\n%s", outputStr)
393+
}
394+
})
395+
396+
// Subtest: ctx add learning modifies LEARNINGS.md
397+
t.Run("add learning modifies LEARNINGS.md", func(t *testing.T) {
398+
addCmd := exec.Command(binaryPath, "add", "learning", "Test learning from integration test")
399+
addCmd.Dir = testDir
400+
if output, err := addCmd.CombinedOutput(); err != nil {
401+
t.Fatalf("ctx add learning failed: %v\n%s", err, output)
402+
}
403+
404+
// Verify learning was added
405+
learningsPath := filepath.Join(testDir, ".context", "LEARNINGS.md")
406+
content, err := os.ReadFile(learningsPath)
407+
if err != nil {
408+
t.Fatalf("failed to read LEARNINGS.md: %v", err)
409+
}
410+
if !strings.Contains(string(content), "Test learning from integration test") {
411+
t.Error("learning was not added to LEARNINGS.md")
412+
}
413+
})
414+
415+
// Subtest: ctx session save creates session file
416+
t.Run("session save creates session file", func(t *testing.T) {
417+
saveCmd := exec.Command(binaryPath, "session", "save")
418+
saveCmd.Dir = testDir
419+
if output, err := saveCmd.CombinedOutput(); err != nil {
420+
t.Fatalf("ctx session save failed: %v\n%s", err, output)
421+
}
422+
423+
// Check that sessions directory exists and has at least one file
424+
sessionsDir := filepath.Join(testDir, ".context", "sessions")
425+
entries, err := os.ReadDir(sessionsDir)
426+
if err != nil {
427+
t.Fatalf("failed to read sessions directory: %v", err)
428+
}
429+
if len(entries) == 0 {
430+
t.Error("no session file was created")
431+
}
432+
})
433+
434+
// Subtest: ctx agent returns context packet
435+
t.Run("agent returns context packet", func(t *testing.T) {
436+
agentCmd := exec.Command(binaryPath, "agent")
437+
agentCmd.Dir = testDir
438+
output, err := agentCmd.CombinedOutput()
439+
if err != nil {
440+
t.Fatalf("ctx agent failed: %v\n%s", err, output)
441+
}
442+
443+
outputStr := string(output)
444+
// Verify it's actual agent output, not help text
445+
if strings.Contains(outputStr, "Usage:") || strings.Contains(outputStr, "Available Commands:") {
446+
t.Error("ctx agent returned help text instead of context packet")
447+
}
448+
// Check for expected context packet markers
449+
if !strings.Contains(outputStr, "CONSTITUTION") && !strings.Contains(outputStr, "TASKS") {
450+
t.Errorf("ctx agent did not return expected context packet, got:\n%s", outputStr)
451+
}
452+
})
453+
454+
// Subtest: ctx drift runs without error
455+
t.Run("drift runs without error", func(t *testing.T) {
456+
driftCmd := exec.Command(binaryPath, "drift")
457+
driftCmd.Dir = testDir
458+
if output, err := driftCmd.CombinedOutput(); err != nil {
459+
t.Fatalf("ctx drift failed: %v\n%s", err, output)
460+
}
461+
})
371462
}

0 commit comments

Comments
 (0)