agent/pkg/agentic/status_test.go
Snider acae0d804f 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>
2026-03-25 08:16:53 +00:00

256 lines
6.8 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"encoding/json"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"path/filepath"
"testing"
"time"
)
func TestWriteStatus_Good(t *testing.T) {
dir := t.TempDir()
status := &WorkspaceStatus{
Status: "running",
Agent: "gemini",
Repo: "go-io",
Task: "fix tests",
PID: 12345,
StartedAt: time.Now(),
Runs: 1,
}
err := writeStatus(dir, status)
require.NoError(t, err)
// Verify file was written via core.Fs
r := fs.Read(filepath.Join(dir, "status.json"))
require.True(t, r.OK)
var read WorkspaceStatus
err = json.Unmarshal([]byte(r.Value.(string)), &read)
require.NoError(t, err)
assert.Equal(t, "running", read.Status)
assert.Equal(t, "gemini", read.Agent)
assert.Equal(t, "go-io", read.Repo)
assert.Equal(t, "fix tests", read.Task)
assert.Equal(t, 12345, read.PID)
assert.Equal(t, 1, read.Runs)
assert.False(t, read.UpdatedAt.IsZero(), "UpdatedAt should be set by writeStatus")
}
func TestWriteStatus_Good_UpdatesTimestamp(t *testing.T) {
dir := t.TempDir()
before := time.Now().Add(-time.Second)
status := &WorkspaceStatus{
Status: "running",
Agent: "claude",
}
err := writeStatus(dir, status)
require.NoError(t, err)
assert.True(t, status.UpdatedAt.After(before), "UpdatedAt should be after the start time")
}
func TestReadStatus_Good(t *testing.T) {
dir := t.TempDir()
status := &WorkspaceStatus{
Status: "completed",
Agent: "codex",
Repo: "go-log",
Task: "add logging",
Branch: "agent/add-logging",
StartedAt: time.Now().Truncate(time.Second),
UpdatedAt: time.Now().Truncate(time.Second),
Runs: 2,
PRURL: "https://forge.lthn.ai/core/go-log/pulls/5",
}
data, err := json.MarshalIndent(status, "", " ")
require.NoError(t, err)
require.True(t, fs.Write(filepath.Join(dir, "status.json"), string(data)).OK)
read, err := ReadStatus(dir)
require.NoError(t, err)
assert.Equal(t, "completed", read.Status)
assert.Equal(t, "codex", read.Agent)
assert.Equal(t, "go-log", read.Repo)
assert.Equal(t, "add logging", read.Task)
assert.Equal(t, "agent/add-logging", read.Branch)
assert.Equal(t, 2, read.Runs)
assert.Equal(t, "https://forge.lthn.ai/core/go-log/pulls/5", read.PRURL)
}
func TestReadStatus_Bad_NoFile(t *testing.T) {
dir := t.TempDir()
_, err := ReadStatus(dir)
assert.Error(t, err)
}
func TestReadStatus_Bad_InvalidJSON(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(filepath.Join(dir, "status.json"), "not json{").OK)
_, err := ReadStatus(dir)
assert.Error(t, err)
}
func TestReadStatus_Good_BlockedWithQuestion(t *testing.T) {
dir := t.TempDir()
status := &WorkspaceStatus{
Status: "blocked",
Agent: "gemini",
Repo: "go-io",
Question: "Which interface should I implement?",
}
data, err := json.MarshalIndent(status, "", " ")
require.NoError(t, err)
require.True(t, fs.Write(filepath.Join(dir, "status.json"), string(data)).OK)
read, err := ReadStatus(dir)
require.NoError(t, err)
assert.Equal(t, "blocked", read.Status)
assert.Equal(t, "Which interface should I implement?", read.Question)
}
func TestWriteReadStatus_Good_Roundtrip(t *testing.T) {
dir := t.TempDir()
original := &WorkspaceStatus{
Status: "running",
Agent: "claude:opus",
Repo: "agent",
Org: "core",
Task: "write tests for agentic package",
Branch: "agent/write-tests",
Issue: 42,
PID: 99999,
StartedAt: time.Now().Truncate(time.Second),
Runs: 3,
}
err := writeStatus(dir, original)
require.NoError(t, err)
read, err := ReadStatus(dir)
require.NoError(t, err)
assert.Equal(t, original.Status, read.Status)
assert.Equal(t, original.Agent, read.Agent)
assert.Equal(t, original.Repo, read.Repo)
assert.Equal(t, original.Org, read.Org)
assert.Equal(t, original.Task, read.Task)
assert.Equal(t, original.Branch, read.Branch)
assert.Equal(t, original.Issue, read.Issue)
assert.Equal(t, original.PID, read.PID)
assert.Equal(t, original.Runs, read.Runs)
}
func TestWriteStatus_Good_OverwriteExisting(t *testing.T) {
dir := t.TempDir()
first := &WorkspaceStatus{Status: "running", Agent: "gemini"}
err := writeStatus(dir, first)
require.NoError(t, err)
second := &WorkspaceStatus{Status: "completed", Agent: "gemini"}
err = writeStatus(dir, second)
require.NoError(t, err)
read, err := ReadStatus(dir)
require.NoError(t, err)
assert.Equal(t, "completed", read.Status)
}
func TestReadStatus_Ugly_EmptyFile(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(filepath.Join(dir, "status.json"), "").OK)
_, 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)
}