test: add Good/Bad/Ugly for status, paths, auto_pr, prep — agentic 74.0%

New properly named tests:
- TestStatus_Status_Ugly — dead PID detection (blocked/completed/failed)
- TestPaths_DefaultBranch_{Good,Bad,Ugly} — main/master/non-git
- TestAutoPR_AutoCreatePR_{Good,Bad,Ugly} — early returns + no commits
- TestPrep_BuildPrompt_{Good,Bad,Ugly} — basic/empty/persona+issue

558 agentic tests, 74.0% coverage

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-25 08:16:53 +00:00
parent 3c894e8101
commit acae0d804f
4 changed files with 330 additions and 0 deletions

114
pkg/agentic/auto_pr_test.go Normal file
View file

@ -0,0 +1,114 @@
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAutoPR_AutoCreatePR_Good(t *testing.T) {
t.Skip("needs real git + forge integration")
}
func TestAutoPR_AutoCreatePR_Bad(t *testing.T) {
root := t.TempDir()
t.Setenv("CORE_WORKSPACE", root)
s := &PrepSubsystem{
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// No status file → early return (no panic)
wsNoStatus := filepath.Join(root, "ws-no-status")
require.NoError(t, os.MkdirAll(wsNoStatus, 0o755))
assert.NotPanics(t, func() {
s.autoCreatePR(wsNoStatus)
})
// Empty branch → early return
wsNoBranch := filepath.Join(root, "ws-no-branch")
require.NoError(t, os.MkdirAll(wsNoBranch, 0o755))
st := &WorkspaceStatus{
Status: "completed",
Agent: "codex",
Repo: "go-io",
Branch: "",
}
data, err := json.MarshalIndent(st, "", " ")
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(wsNoBranch, "status.json"), data, 0o644))
assert.NotPanics(t, func() {
s.autoCreatePR(wsNoBranch)
})
// Empty repo → early return
wsNoRepo := filepath.Join(root, "ws-no-repo")
require.NoError(t, os.MkdirAll(wsNoRepo, 0o755))
st2 := &WorkspaceStatus{
Status: "completed",
Agent: "codex",
Repo: "",
Branch: "agent/fix-tests",
}
data2, err := json.MarshalIndent(st2, "", " ")
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(wsNoRepo, "status.json"), data2, 0o644))
assert.NotPanics(t, func() {
s.autoCreatePR(wsNoRepo)
})
}
func TestAutoPR_AutoCreatePR_Ugly(t *testing.T) {
root := t.TempDir()
t.Setenv("CORE_WORKSPACE", root)
// Set up a real git repo with no commits ahead of origin/dev
wsDir := filepath.Join(root, "ws-no-ahead")
repoDir := filepath.Join(wsDir, "repo")
require.NoError(t, os.MkdirAll(repoDir, 0o755))
// Init the repo
cmd := exec.Command("git", "init", "-b", "dev", repoDir)
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", repoDir, "config", "user.name", "Test")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", repoDir, "config", "user.email", "test@test.com")
require.NoError(t, cmd.Run())
require.NoError(t, os.WriteFile(filepath.Join(repoDir, "README.md"), []byte("# test"), 0o644))
cmd = exec.Command("git", "-C", repoDir, "add", ".")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", repoDir, "commit", "-m", "init")
require.NoError(t, cmd.Run())
// Write status with valid branch + repo
st := &WorkspaceStatus{
Status: "completed",
Agent: "codex",
Repo: "go-io",
Branch: "agent/fix-tests",
StartedAt: time.Now(),
}
data, err := json.MarshalIndent(st, "", " ")
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(wsDir, "status.json"), data, 0o644))
s := &PrepSubsystem{
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// git log origin/dev..HEAD will fail (no origin remote) → early return
assert.NotPanics(t, func() {
s.autoCreatePR(wsDir)
})
}

View file

@ -4,10 +4,12 @@ package agentic
import (
"os"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCoreRoot_Good_EnvVar(t *testing.T) {
@ -142,3 +144,56 @@ func TestGeneratePlanID_Good(t *testing.T) {
assert.True(t, len(id) > 0)
assert.True(t, strings.Contains(id, "fix-the-login-bug"))
}
// --- DefaultBranch ---
func TestPaths_DefaultBranch_Good(t *testing.T) {
dir := t.TempDir()
// Init git repo with "main" branch
cmd := exec.Command("git", "init", "-b", "main", dir)
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "config", "user.name", "Test")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "config", "user.email", "test@test.com")
require.NoError(t, cmd.Run())
require.NoError(t, os.WriteFile(dir+"/README.md", []byte("# Test"), 0o644))
cmd = exec.Command("git", "-C", dir, "add", ".")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "commit", "-m", "init")
require.NoError(t, cmd.Run())
branch := DefaultBranch(dir)
assert.Equal(t, "main", branch)
}
func TestPaths_DefaultBranch_Bad(t *testing.T) {
// Non-git directory — should return "main" (default)
dir := t.TempDir()
branch := DefaultBranch(dir)
assert.Equal(t, "main", branch)
}
func TestPaths_DefaultBranch_Ugly(t *testing.T) {
dir := t.TempDir()
// Init git repo with "master" branch
cmd := exec.Command("git", "init", "-b", "master", dir)
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "config", "user.name", "Test")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "config", "user.email", "test@test.com")
require.NoError(t, cmd.Run())
require.NoError(t, os.WriteFile(dir+"/README.md", []byte("# Test"), 0o644))
cmd = exec.Command("git", "-C", dir, "add", ".")
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "-C", dir, "commit", "-m", "init")
require.NoError(t, cmd.Run())
branch := DefaultBranch(dir)
assert.Equal(t, "master", branch)
}

View file

@ -270,6 +270,93 @@ func TestBuildPrompt_Good_WithIssue(t *testing.T) {
assert.Contains(t, prompt, "Steps to reproduce")
}
// --- buildPrompt (naming convention tests) ---
func TestPrep_BuildPrompt_Good(t *testing.T) {
dir := t.TempDir()
// Create go.mod to detect language as "go"
os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module test\n\ngo 1.22\n"), 0o644)
s := &PrepSubsystem{
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
prompt, memories, consumers := s.buildPrompt(context.Background(), PrepInput{
Task: "Add unit tests",
Org: "core",
Repo: "go-io",
}, "dev", dir)
assert.Contains(t, prompt, "TASK: Add unit tests")
assert.Contains(t, prompt, "REPO: core/go-io on branch dev")
assert.Contains(t, prompt, "LANGUAGE: go")
assert.Contains(t, prompt, "BUILD: go build ./...")
assert.Contains(t, prompt, "TEST: go test ./...")
assert.Contains(t, prompt, "CONSTRAINTS:")
assert.Equal(t, 0, memories)
assert.Equal(t, 0, consumers)
}
func TestPrep_BuildPrompt_Bad(t *testing.T) {
// Empty repo path — still produces a prompt (no crash)
s := &PrepSubsystem{
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
prompt, memories, consumers := s.buildPrompt(context.Background(), PrepInput{
Task: "Do something",
Org: "core",
Repo: "go-io",
}, "main", "")
assert.Contains(t, prompt, "TASK: Do something")
assert.Contains(t, prompt, "CONSTRAINTS:")
assert.Equal(t, 0, memories)
assert.Equal(t, 0, consumers)
}
func TestPrep_BuildPrompt_Ugly(t *testing.T) {
dir := t.TempDir()
os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module test\n\ngo 1.22\n"), 0o644)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"number": 99,
"title": "Critical bug",
"body": "Server crashes on startup",
})
}))
t.Cleanup(srv.Close)
s := &PrepSubsystem{
forge: forge.NewForge(srv.URL, "test-token"),
codePath: t.TempDir(),
client: srv.Client(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
prompt, _, _ := s.buildPrompt(context.Background(), PrepInput{
Task: "Fix critical bug",
Org: "core",
Repo: "go-io",
Persona: "reviewer",
PlanTemplate: "nonexistent-plan",
Issue: 99,
}, "agent/fix-bug", dir)
// Persona may or may not resolve, but prompt must still contain core fields
assert.Contains(t, prompt, "TASK: Fix critical bug")
assert.Contains(t, prompt, "REPO: core/go-io on branch agent/fix-bug")
assert.Contains(t, prompt, "ISSUE:")
assert.Contains(t, prompt, "Server crashes on startup")
assert.Contains(t, prompt, "CONSTRAINTS:")
}
// --- runQA ---
func TestRunQA_Good_PHPNoComposer(t *testing.T) {

View file

@ -180,3 +180,77 @@ func TestReadStatus_Ugly_EmptyFile(t *testing.T) {
_, err := ReadStatus(dir)
assert.Error(t, err)
}
// --- status() dead PID detection ---
func TestStatus_Status_Ugly(t *testing.T) {
root := t.TempDir()
t.Setenv("CORE_WORKSPACE", root)
wsRoot := filepath.Join(root, "workspace")
// Case 1: running + dead PID + BLOCKED.md → should detect as blocked
ws1 := filepath.Join(wsRoot, "dead-blocked")
require.True(t, fs.EnsureDir(filepath.Join(ws1, "repo")).OK)
require.NoError(t, writeStatus(ws1, &WorkspaceStatus{
Status: "running",
Repo: "go-io",
Agent: "codex",
PID: 999999,
}))
require.True(t, fs.Write(filepath.Join(ws1, "repo", "BLOCKED.md"), "Need API credentials").OK)
// Case 2: running + dead PID + agent log → completed
ws2 := filepath.Join(wsRoot, "dead-completed")
require.True(t, fs.EnsureDir(filepath.Join(ws2, "repo")).OK)
require.NoError(t, writeStatus(ws2, &WorkspaceStatus{
Status: "running",
Repo: "go-log",
Agent: "claude",
PID: 999999,
}))
require.True(t, fs.Write(filepath.Join(ws2, "agent-claude.log"), "agent finished ok").OK)
// Case 3: running + dead PID + no log + no BLOCKED.md → failed
ws3 := filepath.Join(wsRoot, "dead-failed")
require.True(t, fs.EnsureDir(filepath.Join(ws3, "repo")).OK)
require.NoError(t, writeStatus(ws3, &WorkspaceStatus{
Status: "running",
Repo: "agent",
Agent: "gemini",
PID: 999999,
}))
s := &PrepSubsystem{
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
_, out, err := s.status(nil, nil, StatusInput{})
require.NoError(t, err)
assert.Equal(t, 3, out.Total)
// Verify case 1: blocked
assert.Len(t, out.Blocked, 1)
assert.Equal(t, "go-io", out.Blocked[0].Repo)
assert.Equal(t, "Need API credentials", out.Blocked[0].Question)
// Verify case 2: completed
assert.Equal(t, 1, out.Completed)
// Verify case 3: failed
assert.Equal(t, 1, out.Failed)
// Verify statuses were persisted to disk
st1, err := ReadStatus(ws1)
require.NoError(t, err)
assert.Equal(t, "blocked", st1.Status)
st2, err := ReadStatus(ws2)
require.NoError(t, err)
assert.Equal(t, "completed", st2.Status)
st3, err := ReadStatus(ws3)
require.NoError(t, err)
assert.Equal(t, "failed", st3.Status)
assert.Equal(t, "Agent process died (no output log)", st3.Question)
}