agent/pkg/agentic/prep_test.go
Snider 7a531d1112 perf(mcp): reduce tool count from 189 to 82
Gate non-essential MCP tools behind CORE_MCP_FULL=1 env var.
Core factory tools (dispatch, status, plan, issue, PR, scan, mirror,
watch, brain, files) always registered. Extended tools (session, sprint,
state, phase, task, template, message, content, platform, epic, remote,
review-queue, setup, metrics, RAG, webview) only when full mode enabled.

189 → 82 tools in default mode. Fixes slow MCP startup and tool
registration timeout in Claude Code.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-08 19:17:21 +01:00

1291 lines
44 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
core "dappco.re/go/core"
"dappco.re/go/core/forge"
coremcp "dappco.re/go/mcp/pkg/mcp"
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPrep_EnvOr_Good_EnvSet(t *testing.T) {
t.Setenv("TEST_ENVVAR_CUSTOM", "custom-value")
assert.Equal(t, "custom-value", envOr("TEST_ENVVAR_CUSTOM", "default"))
}
func TestPrep_EnvOr_Good_Fallback(t *testing.T) {
t.Setenv("TEST_ENVVAR_MISSING", "")
assert.Equal(t, "default-value", envOr("TEST_ENVVAR_MISSING", "default-value"))
}
func TestPrep_EnvOr_Good_UnsetUsesFallback(t *testing.T) {
t.Setenv("TEST_ENVVAR_TOTALLY_MISSING", "")
assert.Equal(t, "fallback", envOr("TEST_ENVVAR_TOTALLY_MISSING", "fallback"))
}
func TestPrep_DetectLanguage_Good_Go(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "go.mod"), "module test").OK)
assert.Equal(t, "go", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_PHP(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "composer.json"), "{}").OK)
assert.Equal(t, "php", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_TypeScript(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "package.json"), "{}").OK)
assert.Equal(t, "ts", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_Rust(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "Cargo.toml"), "[package]").OK)
assert.Equal(t, "rust", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_Python(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "requirements.txt"), "flask").OK)
assert.Equal(t, "py", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_Cpp(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "CMakeLists.txt"), "cmake_minimum_required").OK)
assert.Equal(t, "cpp", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_Docker(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "Dockerfile"), "FROM alpine").OK)
assert.Equal(t, "docker", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Good_DefaultsToGo(t *testing.T) {
dir := t.TempDir()
assert.Equal(t, "go", detectLanguage(dir))
}
func TestPrep_DetectBuildCmd_Good(t *testing.T) {
tests := []struct {
file string
content string
expected string
}{
{"go.mod", "module test", "go build ./..."},
{"composer.json", "{}", "composer install"},
{"package.json", "{}", "npm run build"},
{"requirements.txt", "flask", "pip install -e ."},
{"Cargo.toml", "[package]", "cargo build"},
{"CMakeLists.txt", "cmake", "cmake --build ."},
}
for _, tt := range tests {
t.Run(tt.file, func(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, tt.file), tt.content).OK)
assert.Equal(t, tt.expected, detectBuildCmd(dir))
})
}
}
func TestPrep_DetectBuildCmd_Good_DefaultsToGo(t *testing.T) {
dir := t.TempDir()
assert.Equal(t, "go build ./...", detectBuildCmd(dir))
}
func TestPrep_DetectTestCmd_Good(t *testing.T) {
tests := []struct {
file string
content string
expected string
}{
{"go.mod", "module test", "go test ./..."},
{"composer.json", "{}", "composer test"},
{"package.json", "{}", "npm test"},
{"requirements.txt", "flask", "pytest"},
{"Cargo.toml", "[package]", "cargo test"},
{"CMakeLists.txt", "cmake", "ctest"},
}
for _, tt := range tests {
t.Run(tt.file, func(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, tt.file), tt.content).OK)
assert.Equal(t, tt.expected, detectTestCmd(dir))
})
}
}
func TestPrep_DetectTestCmd_Good_DefaultsToGo(t *testing.T) {
dir := t.TempDir()
assert.Equal(t, "go test ./...", detectTestCmd(dir))
}
func TestSanitise_SanitiseBranchSlug_Good(t *testing.T) {
assert.Equal(t, "fix-login-bug", sanitiseBranchSlug("Fix login bug!", 40))
assert.Equal(t, "trim-me", sanitiseBranchSlug("---Trim Me---", 40))
}
func TestSanitise_SanitiseBranchSlug_Good_Truncates(t *testing.T) {
assert.Equal(t, "feature", sanitiseBranchSlug("feature--extra", 7))
}
func TestSanitise_SanitiseFilename_Good(t *testing.T) {
assert.Equal(t, "Core---Agent-Notes", sanitiseFilename("Core / Agent:Notes"))
}
func TestPrep_NewPrep_Good_Defaults(t *testing.T) {
t.Setenv("FORGE_TOKEN", "")
t.Setenv("GITEA_TOKEN", "")
t.Setenv("CORE_BRAIN_KEY", "")
t.Setenv("FORGE_URL", "")
t.Setenv("CORE_BRAIN_URL", "")
t.Setenv("SPECS_PATH", "")
t.Setenv("CODE_PATH", "")
s := NewPrep()
assert.Equal(t, "https://forge.lthn.ai", s.forgeURL)
assert.Equal(t, "https://api.lthn.sh", s.brainURL)
assert.NotEmpty(t, s.codePath)
}
func TestPrep_NewPrep_Good_EnvOverrides(t *testing.T) {
t.Setenv("FORGE_URL", "https://custom-forge.example.com")
t.Setenv("FORGE_TOKEN", "test-token")
t.Setenv("CORE_BRAIN_URL", "https://custom-brain.example.com")
t.Setenv("CORE_BRAIN_KEY", "brain-key-123")
t.Setenv("SPECS_PATH", "/custom/specs")
t.Setenv("CODE_PATH", "/custom/code")
s := NewPrep()
assert.Equal(t, "https://custom-forge.example.com", s.forgeURL)
assert.Equal(t, "test-token", s.forgeToken)
assert.Equal(t, "https://custom-brain.example.com", s.brainURL)
assert.Equal(t, "brain-key-123", s.brainKey)
assert.Equal(t, "/custom/code", s.codePath)
}
func TestPrep_NewPrep_Good_CoreHomeOverride(t *testing.T) {
tmpHome := t.TempDir()
claudeDir := core.JoinPath(tmpHome, ".claude")
require.True(t, fs.EnsureDir(claudeDir).OK)
require.True(t, fs.Write(core.JoinPath(claudeDir, "brain.key"), "core-home-key").OK)
t.Setenv("CORE_HOME", tmpHome)
t.Setenv("HOME", "/ignored-home")
t.Setenv("DIR_HOME", "/ignored-dir")
t.Setenv("CORE_BRAIN_KEY", "")
t.Setenv("CODE_PATH", "")
s := NewPrep()
assert.Equal(t, core.JoinPath(tmpHome, "Code"), s.codePath)
assert.Equal(t, "core-home-key", s.brainKey)
}
func TestPrep_NewPrep_Good_GiteaTokenFallback(t *testing.T) {
t.Setenv("FORGE_TOKEN", "")
t.Setenv("GITEA_TOKEN", "gitea-fallback-token")
s := NewPrep()
assert.Equal(t, "gitea-fallback-token", s.forgeToken)
}
func TestPrep_Subsystem_Good_Name(t *testing.T) {
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{})}
assert.Equal(t, "agentic", s.Name())
}
func TestPrep_Subsystem_SetCore_Good_WiresServiceRuntime(t *testing.T) {
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.SetCore(c)
require.NotNil(t, s.ServiceRuntime)
assert.Equal(t, c, s.Core())
}
func TestPrep_Subsystem_SetCore_Bad_NilCoreDoesNothing(t *testing.T) {
s := &PrepSubsystem{}
s.SetCore(nil)
assert.Nil(t, s.ServiceRuntime)
}
func TestPrep_Subsystem_SetCore_Ugly_NilReceiverDoesNotPanic(t *testing.T) {
c := core.New(core.WithOption("name", "test"))
assert.NotPanics(t, func() {
var s *PrepSubsystem
s.SetCore(c)
})
}
// --- sanitiseBranchSlug Bad/Ugly ---
func TestSanitise_SanitiseBranchSlug_Bad_EmptyString(t *testing.T) {
assert.Equal(t, "", sanitiseBranchSlug("", 40))
}
func TestSanitise_SanitiseBranchSlug_Bad_OnlySpecialChars(t *testing.T) {
assert.Equal(t, "", sanitiseBranchSlug("!@#$%^&*()", 40))
}
func TestSanitise_SanitiseBranchSlug_Bad_OnlyDashes(t *testing.T) {
assert.Equal(t, "", sanitiseBranchSlug("------", 40))
}
func TestSanitise_SanitiseBranchSlug_Ugly_VeryLongString(t *testing.T) {
long := strings.Repeat("abcdefghij", 100)
result := sanitiseBranchSlug(long, 50)
assert.LessOrEqual(t, len(result), 50)
}
func TestSanitise_SanitiseBranchSlug_Ugly_Unicode(t *testing.T) {
// Unicode chars should be replaced with dashes, then edges trimmed
result := sanitiseBranchSlug("\u00e9\u00e0\u00fc\u00f1\u00f0", 40)
assert.NotContains(t, result, "\u00e9")
// All replaced with dashes, then trimmed = empty
assert.Equal(t, "", result)
}
func TestSanitise_SanitiseBranchSlug_Ugly_ZeroMax(t *testing.T) {
// max=0 means no limit
result := sanitiseBranchSlug("hello-world", 0)
assert.Equal(t, "hello-world", result)
}
// --- sanitisePlanSlug Bad/Ugly ---
func TestSanitise_SanitisePlanSlug_Bad_EmptyString(t *testing.T) {
assert.Equal(t, "", sanitisePlanSlug(""))
}
func TestSanitise_SanitisePlanSlug_Bad_OnlySpecialChars(t *testing.T) {
assert.Equal(t, "", sanitisePlanSlug("!@#$%^&*()"))
}
func TestSanitise_SanitisePlanSlug_Bad_OnlySpaces(t *testing.T) {
// Spaces become dashes, then collapsed, then trimmed
assert.Equal(t, "", sanitisePlanSlug(" "))
}
func TestSanitise_SanitisePlanSlug_Ugly_VeryLongString(t *testing.T) {
long := strings.Repeat("abcdefghij ", 20)
result := sanitisePlanSlug(long)
assert.LessOrEqual(t, len(result), 30)
}
func TestSanitise_SanitisePlanSlug_Ugly_Unicode(t *testing.T) {
result := sanitisePlanSlug("\u00e9\u00e0\u00fc\u00f1\u00f0")
assert.Equal(t, "", result, "unicode chars should be stripped, leaving empty string")
}
func TestSanitise_SanitisePlanSlug_Ugly_AllDashInput(t *testing.T) {
assert.Equal(t, "", sanitisePlanSlug("---"))
}
// --- sanitiseFilename Bad/Ugly ---
func TestSanitise_SanitiseFilename_Bad_EmptyString(t *testing.T) {
assert.Equal(t, "", sanitiseFilename(""))
}
func TestSanitise_SanitiseFilename_Bad_OnlySpecialChars(t *testing.T) {
result := sanitiseFilename("!@#$%^&*()")
// All replaced with dashes
assert.Equal(t, "----------", result)
}
func TestSanitise_SanitiseFilename_Ugly_VeryLongString(t *testing.T) {
long := strings.Repeat("a", 1000)
result := sanitiseFilename(long)
assert.Equal(t, 1000, len(result))
}
func TestSanitise_SanitiseFilename_Ugly_Unicode(t *testing.T) {
result := sanitiseFilename("\u00e9\u00e0\u00fc\u00f1\u00f0")
// All replaced with dashes
for _, r := range result {
assert.Equal(t, '-', r)
}
}
func TestSanitise_SanitiseFilename_Ugly_PreservesDotsUnderscores(t *testing.T) {
assert.Equal(t, "my_file.test.txt", sanitiseFilename("my_file.test.txt"))
}
// --- collapseRepeatedRune Bad/Ugly ---
func TestSanitise_CollapseRepeatedRune_Bad_EmptyString(t *testing.T) {
assert.Equal(t, "", collapseRepeatedRune("", '-'))
}
func TestSanitise_CollapseRepeatedRune_Bad_AllTarget(t *testing.T) {
assert.Equal(t, "-", collapseRepeatedRune("-----", '-'))
}
func TestSanitise_CollapseRepeatedRune_Ugly_Unicode(t *testing.T) {
assert.Equal(t, "h\u00e9llo", collapseRepeatedRune("h\u00e9\u00e9\u00e9llo", '\u00e9'))
}
func TestSanitise_CollapseRepeatedRune_Ugly_VeryLong(t *testing.T) {
long := strings.Repeat("--a", 500)
result := collapseRepeatedRune(long, '-')
assert.NotContains(t, result, "--")
}
// --- trimRuneEdges Bad/Ugly ---
func TestSanitise_TrimRuneEdges_Bad_EmptyString(t *testing.T) {
assert.Equal(t, "", trimRuneEdges("", '-'))
}
func TestSanitise_TrimRuneEdges_Bad_AllTarget(t *testing.T) {
assert.Equal(t, "", trimRuneEdges("-----", '-'))
}
func TestSanitise_TrimRuneEdges_Ugly_Unicode(t *testing.T) {
assert.Equal(t, "hello", trimRuneEdges("\u00e9hello\u00e9\u00e9", '\u00e9'))
}
func TestSanitise_TrimRuneEdges_Ugly_NoMatch(t *testing.T) {
assert.Equal(t, "hello", trimRuneEdges("hello", '-'))
}
// --- PrepSubsystem Name Bad/Ugly ---
func TestPrep_Name_Bad(t *testing.T) {
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{})}
name := s.Name()
assert.NotEmpty(t, name, "Name should never return empty")
assert.Equal(t, "agentic", name)
}
func TestPrep_Name_Ugly(t *testing.T) {
// Zero-value struct — Name() should still work
var s PrepSubsystem
assert.NotPanics(t, func() {
name := s.Name()
assert.Equal(t, "agentic", name)
})
}
// --- NewPrep Bad/Ugly ---
func TestPrep_NewPrep_Bad(t *testing.T) {
// Call without any env — verify doesn't panic, returns valid struct
t.Setenv("FORGE_TOKEN", "")
t.Setenv("GITEA_TOKEN", "")
t.Setenv("CORE_BRAIN_KEY", "")
t.Setenv("FORGE_URL", "")
t.Setenv("CORE_BRAIN_URL", "")
t.Setenv("SPECS_PATH", "")
t.Setenv("CODE_PATH", "")
assert.NotPanics(t, func() {
s := NewPrep()
assert.NotNil(t, s)
})
}
func TestPrep_NewPrep_Ugly(t *testing.T) {
// Verify returned struct has non-nil backoff/failCount maps
t.Setenv("FORGE_TOKEN", "")
t.Setenv("GITEA_TOKEN", "")
s := NewPrep()
assert.NotNil(t, s.backoff, "backoff map must not be nil")
assert.NotNil(t, s.failCount, "failCount map must not be nil")
assert.NotNil(t, s.forge, "Forge client must not be nil")
}
// --- OnStartup Good/Bad/Ugly ---
func TestPrep_OnStartup_Good_CreatesPokeCh(t *testing.T) {
// StartRunner is now a no-op — pokeCh is no longer initialised by OnStartup.
// Verify OnStartup succeeds and pokeCh remains nil.
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
assert.Nil(t, s.pokeCh, "pokeCh should be nil before OnStartup")
r := s.OnStartup(context.Background())
assert.True(t, r.OK)
assert.Nil(t, s.pokeCh, "pokeCh should remain nil — queue drain is owned by pkg/runner")
}
func TestPrep_OnStartup_Good_FrozenByDefault(t *testing.T) {
// Frozen state is now owned by pkg/runner.Service, not agentic.
// Verify OnStartup succeeds without asserting frozen state.
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
assert.True(t, s.OnStartup(context.Background()).OK)
}
func TestPrep_OnStartup_Good_NoError(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
assert.True(t, s.OnStartup(context.Background()).OK)
}
func TestPrep_OnStartup_Good_RegistersPlanActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.dispatch.sync").Exists())
assert.True(t, c.Action("plan.create").Exists())
assert.True(t, c.Action("plan.get").Exists())
assert.True(t, c.Action("plan.read").Exists())
assert.True(t, c.Action("plan.update").Exists())
assert.True(t, c.Action("plan.update_status").Exists())
assert.True(t, c.Action("plan.from.issue").Exists())
assert.True(t, c.Action("plan.check").Exists())
assert.True(t, c.Action("plan.archive").Exists())
assert.True(t, c.Action("plan.delete").Exists())
assert.True(t, c.Action("plan.list").Exists())
assert.True(t, c.Action("phase.get").Exists())
assert.True(t, c.Action("phase.update_status").Exists())
assert.True(t, c.Action("phase.add_checkpoint").Exists())
assert.True(t, c.Action("task.create").Exists())
assert.True(t, c.Action("task.update").Exists())
assert.True(t, c.Action("task.toggle").Exists())
}
func TestPrep_OnStartup_Good_RegistersDispatchControlActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.dispatch.start").Exists())
assert.True(t, c.Action("agentic.dispatch.shutdown").Exists())
assert.True(t, c.Action("agentic.dispatch.shutdown_now").Exists())
}
func TestPrep_OnStartup_Good_RegistersSessionActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("session.start").Exists())
assert.True(t, c.Action("session.get").Exists())
assert.True(t, c.Action("session.list").Exists())
assert.True(t, c.Action("session.continue").Exists())
assert.True(t, c.Action("session.end").Exists())
assert.True(t, c.Action("session.complete").Exists())
assert.True(t, c.Action("session.log").Exists())
assert.True(t, c.Action("session.artifact").Exists())
assert.True(t, c.Action("session.handoff").Exists())
assert.True(t, c.Action("session.resume").Exists())
assert.True(t, c.Action("session.replay").Exists())
assert.True(t, c.Action("state.set").Exists())
assert.True(t, c.Action("state.get").Exists())
assert.True(t, c.Action("state.list").Exists())
assert.True(t, c.Action("state.delete").Exists())
assert.True(t, c.Action("issue.create").Exists())
assert.True(t, c.Action("issue.get").Exists())
assert.True(t, c.Action("issue.list").Exists())
assert.True(t, c.Action("issue.update").Exists())
assert.True(t, c.Action("issue.assign").Exists())
assert.True(t, c.Action("issue.comment").Exists())
assert.True(t, c.Action("issue.report").Exists())
assert.True(t, c.Action("issue.archive").Exists())
assert.True(t, c.Action("agentic.message.send").Exists())
assert.True(t, c.Action("agent.message.send").Exists())
assert.True(t, c.Action("agentic.message.inbox").Exists())
assert.True(t, c.Action("agent.message.inbox").Exists())
assert.True(t, c.Action("agentic.message.conversation").Exists())
assert.True(t, c.Action("agent.message.conversation").Exists())
assert.True(t, c.Action("agentic.issue.update").Exists())
assert.True(t, c.Action("agentic.issue.create").Exists())
assert.True(t, c.Action("agentic.issue.assign").Exists())
assert.True(t, c.Action("agentic.issue.comment").Exists())
assert.True(t, c.Action("agentic.issue.report").Exists())
assert.True(t, c.Action("agentic.issue.archive").Exists())
assert.True(t, c.Action("sprint.create").Exists())
assert.True(t, c.Action("sprint.get").Exists())
assert.True(t, c.Action("sprint.list").Exists())
assert.True(t, c.Action("sprint.update").Exists())
assert.True(t, c.Action("sprint.archive").Exists())
}
func TestPrep_OnStartup_Good_RegistersNamespacedActionAliases(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.plan.create").Exists())
assert.True(t, c.Action("agentic.plan.read").Exists())
assert.True(t, c.Action("agentic.phase.get").Exists())
assert.True(t, c.Action("agentic.task.create").Exists())
assert.True(t, c.Action("agentic.session.start").Exists())
assert.True(t, c.Action("agentic.state.set").Exists())
assert.True(t, c.Action("agentic.content.generate").Exists())
assert.True(t, c.Action("agentic.content.schema.generate").Exists())
}
func TestPrep_OnStartup_Good_RegistersForgeActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.pr.get").Exists())
assert.True(t, c.Action("agentic.pr.list").Exists())
assert.True(t, c.Action("agentic.pr.merge").Exists())
assert.True(t, c.Action("agentic.pr.close").Exists())
assert.True(t, c.Action("agentic.commit").Exists())
}
func TestPrep_OnStartup_Good_RegistersContentActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("content.generate").Exists())
assert.True(t, c.Action("agentic.generate").Exists())
assert.True(t, c.Action("content.batch").Exists())
assert.True(t, c.Action("content.batch.generate").Exists())
assert.True(t, c.Action("content.batch_generate").Exists())
assert.True(t, c.Action("content_batch").Exists())
assert.True(t, c.Action("content.brief.create").Exists())
assert.True(t, c.Action("content.brief.get").Exists())
assert.True(t, c.Action("content.brief.list").Exists())
assert.True(t, c.Action("content.status").Exists())
assert.True(t, c.Action("content.usage.stats").Exists())
assert.True(t, c.Action("content.usage_stats").Exists())
assert.True(t, c.Action("content.from.plan").Exists())
assert.True(t, c.Action("content.from_plan").Exists())
assert.True(t, c.Action("content.schema.generate").Exists())
assert.True(t, c.Action("agentic.content.generate").Exists())
assert.True(t, c.Action("agentic.content.batch").Exists())
assert.True(t, c.Action("agentic.content.schema.generate").Exists())
}
func TestPrep_OnStartup_Good_RegistersTemplateActions(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("template.list").Exists())
assert.True(t, c.Action("agentic.template.list").Exists())
assert.True(t, c.Action("template.preview").Exists())
assert.True(t, c.Action("agentic.template.preview").Exists())
assert.True(t, c.Action("template.create_plan").Exists())
assert.True(t, c.Action("agentic.template.create_plan").Exists())
}
func TestPrep_OnStartup_Good_RegistersPlatformActionAliases(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.True(t, c.Action("agentic.sync.push").Exists())
assert.True(t, c.Action("agent.sync.push").Exists())
assert.True(t, c.Action("agentic.auth.provision").Exists())
assert.True(t, c.Action("agent.auth.provision").Exists())
assert.True(t, c.Action("agentic.auth.revoke").Exists())
assert.True(t, c.Action("agent.auth.revoke").Exists())
assert.True(t, c.Action("agentic.fleet.register").Exists())
assert.True(t, c.Action("agent.fleet.register").Exists())
assert.True(t, c.Action("agentic.credits.balance").Exists())
assert.True(t, c.Action("agent.credits.balance").Exists())
assert.True(t, c.Action("agentic.fleet.events").Exists())
assert.True(t, c.Action("agent.fleet.events").Exists())
assert.True(t, c.Action("agentic.subscription.budget.update").Exists())
assert.True(t, c.Action("agent.subscription.budget.update").Exists())
}
func TestPrep_OnStartup_Good_RegistersPlatformCommandAlias(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.Contains(t, c.Commands(), "auth/provision")
assert.Contains(t, c.Commands(), "agentic:auth/provision")
assert.Contains(t, c.Commands(), "auth/revoke")
assert.Contains(t, c.Commands(), "agentic:auth/revoke")
assert.Contains(t, c.Commands(), "message/send")
assert.Contains(t, c.Commands(), "messages/send")
assert.Contains(t, c.Commands(), "agentic:message/send")
assert.Contains(t, c.Commands(), "agentic:messages/send")
assert.Contains(t, c.Commands(), "message/inbox")
assert.Contains(t, c.Commands(), "messages/inbox")
assert.Contains(t, c.Commands(), "agentic:message/inbox")
assert.Contains(t, c.Commands(), "agentic:messages/inbox")
assert.Contains(t, c.Commands(), "message/conversation")
assert.Contains(t, c.Commands(), "messages/conversation")
assert.Contains(t, c.Commands(), "agentic:message/conversation")
assert.Contains(t, c.Commands(), "agentic:messages/conversation")
assert.Contains(t, c.Commands(), "subscription/budget/update")
assert.Contains(t, c.Commands(), "subscription/update-budget")
assert.Contains(t, c.Commands(), "agentic:subscription/budget/update")
assert.Contains(t, c.Commands(), "agentic:subscription/update-budget")
assert.Contains(t, c.Commands(), "fleet/events")
assert.Contains(t, c.Commands(), "agentic:fleet/events")
}
func TestPrep_RegisterTools_Good_RegistersCompletionTool(t *testing.T) {
t.Setenv("CORE_MCP_FULL", "1")
svc, err := coremcp.New(coremcp.Options{Unrestricted: true})
require.NoError(t, err)
subsystem := &PrepSubsystem{}
subsystem.RegisterTools(svc)
server := svc.Server()
client := mcpsdk.NewClient(&mcpsdk.Implementation{Name: "test", Version: "0.1.0"}, nil)
clientTransport, serverTransport := mcpsdk.NewInMemoryTransports()
serverSession, err := server.Connect(context.Background(), serverTransport, nil)
require.NoError(t, err)
t.Cleanup(func() { _ = serverSession.Close() })
clientSession, err := client.Connect(context.Background(), clientTransport, nil)
require.NoError(t, err)
t.Cleanup(func() { _ = clientSession.Close() })
result, err := clientSession.ListTools(context.Background(), nil)
require.NoError(t, err)
var toolNames []string
for _, tool := range result.Tools {
toolNames = append(toolNames, tool.Name)
}
assert.Contains(t, toolNames, "agentic_complete")
assert.Contains(t, toolNames, "prompt_version")
assert.Contains(t, toolNames, "agentic_prompt_version")
assert.Contains(t, toolNames, "agentic_setup")
assert.Contains(t, toolNames, "agentic_issue_create")
assert.Contains(t, toolNames, "agentic_issue_assign")
assert.Contains(t, toolNames, "agentic_session_start")
assert.Contains(t, toolNames, "agentic_task_create")
assert.Contains(t, toolNames, "agentic_state_set")
assert.Contains(t, toolNames, "agentic_sprint_create")
assert.Contains(t, toolNames, "session_complete")
assert.Contains(t, toolNames, "agentic_message_send")
assert.Contains(t, toolNames, "agent_send")
assert.Contains(t, toolNames, "agentic_message_inbox")
assert.Contains(t, toolNames, "agent_inbox")
assert.Contains(t, toolNames, "agentic_message_conversation")
assert.Contains(t, toolNames, "agent_conversation")
}
func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
t.Setenv("CORE_AGENT_DISPATCH", "")
c := core.New(core.WithOption("name", "test"))
s := NewPrep()
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
require.True(t, s.OnStartup(context.Background()).OK)
assert.Contains(t, c.Commands(), "generate")
assert.Contains(t, c.Commands(), "agentic:generate")
assert.Contains(t, c.Commands(), "complete")
assert.Contains(t, c.Commands(), "dispatch/sync")
assert.Contains(t, c.Commands(), "agentic:plan")
assert.Contains(t, c.Commands(), "prep-workspace")
assert.Contains(t, c.Commands(), "setup")
assert.Contains(t, c.Commands(), "agentic:setup")
assert.True(t, c.Action("agentic.setup").Exists())
assert.Contains(t, c.Commands(), "watch")
assert.Contains(t, c.Commands(), "workspace/watch")
assert.Contains(t, c.Commands(), "agentic:watch")
assert.Contains(t, c.Commands(), "dispatch/start")
assert.Contains(t, c.Commands(), "agentic:dispatch/start")
assert.Contains(t, c.Commands(), "dispatch/shutdown")
assert.Contains(t, c.Commands(), "agentic:dispatch/shutdown")
assert.Contains(t, c.Commands(), "dispatch/shutdown-now")
assert.Contains(t, c.Commands(), "agentic:dispatch/shutdown-now")
assert.Contains(t, c.Commands(), "brain/ingest")
assert.Contains(t, c.Commands(), "brain/seed-memory")
assert.Contains(t, c.Commands(), "brain/list")
assert.Contains(t, c.Commands(), "brain/forget")
assert.Contains(t, c.Commands(), "lang/detect")
assert.Contains(t, c.Commands(), "lang/list")
assert.Contains(t, c.Commands(), "epic")
assert.Contains(t, c.Commands(), "agentic:epic")
assert.Contains(t, c.Commands(), "plan-cleanup")
assert.Contains(t, c.Commands(), "commit")
assert.Contains(t, c.Commands(), "agentic:commit")
assert.Contains(t, c.Commands(), "plan/from-issue")
assert.Contains(t, c.Commands(), "session/end")
assert.Contains(t, c.Commands(), "agentic:session/end")
assert.Contains(t, c.Commands(), "session/resume")
assert.Contains(t, c.Commands(), "session/replay")
assert.Contains(t, c.Commands(), "review-queue")
assert.Contains(t, c.Commands(), "agentic:review-queue")
assert.Contains(t, c.Commands(), "flow/preview")
assert.Contains(t, c.Commands(), "agentic:flow/preview")
assert.Contains(t, c.Commands(), "prompt")
assert.Contains(t, c.Commands(), "agentic:prompt")
assert.Contains(t, c.Commands(), "prompt/version")
assert.Contains(t, c.Commands(), "agentic:prompt/version")
assert.True(t, c.Action("agentic.prompt.version").Exists())
assert.Contains(t, c.Commands(), "task")
assert.Contains(t, c.Commands(), "task/create")
assert.Contains(t, c.Commands(), "task/update")
assert.Contains(t, c.Commands(), "task/toggle")
assert.Contains(t, c.Commands(), "phase")
assert.Contains(t, c.Commands(), "agentic:phase")
assert.Contains(t, c.Commands(), "phase/get")
assert.Contains(t, c.Commands(), "agentic:phase/get")
assert.Contains(t, c.Commands(), "phase/update_status")
assert.Contains(t, c.Commands(), "agentic:phase/update_status")
assert.Contains(t, c.Commands(), "phase/add_checkpoint")
assert.Contains(t, c.Commands(), "agentic:phase/add_checkpoint")
assert.Contains(t, c.Commands(), "state")
assert.Contains(t, c.Commands(), "state/set")
assert.Contains(t, c.Commands(), "state/get")
assert.Contains(t, c.Commands(), "state/list")
assert.Contains(t, c.Commands(), "state/delete")
}
func TestPrep_OnStartup_Bad(t *testing.T) {
// OnStartup with nil ServiceRuntime — panics because
// registerCommands calls s.Core().Command().
s := &PrepSubsystem{
ServiceRuntime: nil,
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
assert.Panics(t, func() {
_ = s.OnStartup(context.Background())
}, "OnStartup without core should panic on registerCommands")
}
func TestPrep_OnStartup_Ugly(t *testing.T) {
// OnStartup called twice with valid core — second call should not panic
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
c := core.New(core.WithOption("name", "test"))
s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{})
assert.NotPanics(t, func() {
_ = s.OnStartup(context.Background())
_ = s.OnStartup(context.Background())
})
}
// --- OnShutdown Good/Bad ---
func TestPrep_OnShutdown_Good_FreezesQueue(t *testing.T) {
t.Setenv("CORE_WORKSPACE", t.TempDir())
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}), frozen: false}
r := s.OnShutdown(context.Background())
assert.True(t, r.OK)
assert.True(t, s.frozen, "OnShutdown must set frozen=true")
}
func TestPrep_OnShutdown_Good_AlreadyFrozen(t *testing.T) {
// Calling OnShutdown twice must be idempotent
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}), frozen: true}
r := s.OnShutdown(context.Background())
assert.True(t, r.OK)
assert.True(t, s.frozen)
}
func TestPrep_OnShutdown_Good_NoError(t *testing.T) {
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{})}
assert.True(t, s.OnShutdown(context.Background()).OK)
}
func TestPrep_OnShutdown_Ugly_NilCore(t *testing.T) {
// OnShutdown must not panic even if s.core is nil
s := &PrepSubsystem{ServiceRuntime: nil, frozen: false}
assert.NotPanics(t, func() {
_ = s.OnShutdown(context.Background())
})
assert.True(t, s.frozen)
}
func TestPrep_OnShutdown_Bad(t *testing.T) {
// OnShutdown without Core
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
assert.NotPanics(t, func() {
r := s.OnShutdown(context.Background())
assert.True(t, r.OK)
})
assert.True(t, s.frozen)
}
// --- Shutdown Bad/Ugly ---
func TestPrep_Shutdown_Bad(t *testing.T) {
// Shutdown always returns nil
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
err := s.Shutdown(context.Background())
assert.NoError(t, err)
assert.Nil(t, err)
}
func TestPrep_Shutdown_Ugly(t *testing.T) {
// Shutdown on zero-value struct
var s PrepSubsystem
assert.NotPanics(t, func() {
err := s.Shutdown(context.Background())
assert.NoError(t, err)
})
}
// --- EnvOr Bad/Ugly ---
func TestPrep_EnvOr_Bad(t *testing.T) {
// Both env empty and fallback empty
t.Setenv("TEST_ENVVAR_EMPTY_ALL", "")
assert.Equal(t, "", envOr("TEST_ENVVAR_EMPTY_ALL", ""))
}
func TestPrep_EnvOr_Ugly(t *testing.T) {
// Env set to whitespace — whitespace is non-empty, so returned as-is
t.Setenv("TEST_ENVVAR_WHITESPACE", " ")
assert.Equal(t, " ", envOr("TEST_ENVVAR_WHITESPACE", "fallback"))
}
// --- DetectLanguage Bad/Ugly ---
func TestPrep_DetectLanguage_Bad(t *testing.T) {
// Empty dir — defaults to go
dir := t.TempDir()
assert.Equal(t, "go", detectLanguage(dir))
}
func TestPrep_DetectLanguage_Ugly(t *testing.T) {
// Dir with multiple project files (go.mod + package.json) — go wins (first match)
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "go.mod"), "module test").OK)
require.True(t, fs.Write(core.JoinPath(dir, "package.json"), "{}").OK)
assert.Equal(t, "go", detectLanguage(dir), "go.mod checked first, so go wins")
}
// --- DetectBuildCmd Bad/Ugly ---
func TestPrep_DetectBuildCmd_Bad(t *testing.T) {
// Unknown/non-existent path — defaults to go build
assert.Equal(t, "go build ./...", detectBuildCmd("/nonexistent/path/that/does/not/exist"))
}
func TestPrep_DetectBuildCmd_Ugly(t *testing.T) {
// Path that doesn't exist at all — defaults to go build
assert.NotPanics(t, func() {
result := detectBuildCmd("")
assert.Equal(t, "go build ./...", result)
})
}
// --- PrepareWorkspace ---
func TestPrep_PrepareWorkspace_Good(t *testing.T) {
root := t.TempDir()
setTestWorkspace(t, root)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// Valid input but repo won't exist — still exercises the public wrapper delegation
_, _, err := s.PrepareWorkspace(context.Background(), PrepInput{
Repo: "go-io",
Issue: 1,
})
// Error expected (no local clone) but we verified it delegates to prepWorkspace
assert.Error(t, err)
}
func TestPrep_PrepareWorkspace_Bad(t *testing.T) {
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// Missing repo — should return error
_, _, err := s.PrepareWorkspace(context.Background(), PrepInput{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "repo is required")
}
func TestPrep_PrepareWorkspace_Ugly(t *testing.T) {
root := t.TempDir()
setTestWorkspace(t, root)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// Bare ".." is caught as invalid repo name by PathBase check
_, _, err := s.PrepareWorkspace(context.Background(), PrepInput{
Repo: "..",
Issue: 1,
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid repo name")
}
// --- BuildPrompt ---
func TestPrep_BuildPrompt_Good(t *testing.T) {
dir := t.TempDir()
require.True(t, fs.Write(core.JoinPath(dir, "go.mod"), "module test").OK)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
prompt, memories, consumers := s.BuildPrompt(context.Background(), PrepInput{
Task: "Review code",
Org: "core",
Repo: "go-io",
}, "dev", dir)
assert.NotEmpty(t, prompt)
assert.Contains(t, prompt, "TASK: Review code")
assert.Contains(t, prompt, "REPO: core/go-io on branch dev")
assert.Equal(t, 0, memories)
assert.Equal(t, 0, consumers)
}
func TestPrep_BuildPrompt_Bad(t *testing.T) {
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// Empty inputs — should still return a prompt string without panicking
prompt, memories, consumers := s.BuildPrompt(context.Background(), PrepInput{}, "", "")
assert.NotEmpty(t, prompt)
assert.Contains(t, prompt, "TASK:")
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()
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
codePath: t.TempDir(),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
// Unicode in all fields — should not panic
prompt, _, _ := s.BuildPrompt(context.Background(), PrepInput{
Task: "\u00e9nchantr\u00efx \u2603 \U0001f600",
Org: "c\u00f6re",
Repo: "g\u00f6-i\u00f6",
}, "\u00e9-branch", dir)
assert.NotEmpty(t, prompt)
assert.Contains(t, prompt, "\u00e9nchantr\u00efx")
}
// --- collapseRepeatedRune / sanitisePlanSlug / trimRuneEdges Good ---
func TestPrep_CollapseRepeatedRune_Good(t *testing.T) {
assert.Equal(t, "hello-world", collapseRepeatedRune("hello---world", '-'))
}
func TestPrep_SanitisePlanSlug_Good(t *testing.T) {
assert.Equal(t, "my-cool-plan", sanitisePlanSlug("My Cool Plan"))
}
func TestPrep_TrimRuneEdges_Good(t *testing.T) {
assert.Equal(t, "hello", trimRuneEdges("--hello--", '-'))
}
// --- DetectTestCmd Bad/Ugly ---
func TestPrep_DetectTestCmd_Bad(t *testing.T) {
// Unknown path — defaults to go test
assert.Equal(t, "go test ./...", detectTestCmd("/nonexistent/path/that/does/not/exist"))
}
func TestPrep_DetectTestCmd_Ugly(t *testing.T) {
// Path that doesn't exist — defaults to go test
assert.NotPanics(t, func() {
result := detectTestCmd("")
assert.Equal(t, "go test ./...", result)
})
}
// --- getGitLog ---
func TestPrep_GetGitLog_Good(t *testing.T) {
dir := t.TempDir()
gitEnv := []string{"GIT_AUTHOR_NAME=Test", "GIT_AUTHOR_EMAIL=test@test.com", "GIT_COMMITTER_NAME=Test", "GIT_COMMITTER_EMAIL=test@test.com"}
run := func(args ...string) {
t.Helper()
r := testCore.Process().RunWithEnv(context.Background(), dir, gitEnv, args[0], args[1:]...)
require.True(t, r.OK, "cmd %v failed: %s", args, r.Value)
}
run("git", "init", "-b", "main")
run("git", "config", "user.name", "Test")
run("git", "config", "user.email", "test@test.com")
require.True(t, fs.Write(core.JoinPath(dir, "README.md"), "# Test").OK)
run("git", "add", "README.md")
run("git", "commit", "-m", "initial commit")
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
log := s.getGitLog(dir)
assert.NotEmpty(t, log)
assert.Contains(t, log, "initial commit")
}
func TestPrep_GetGitLog_Bad(t *testing.T) {
// Non-git dir returns empty
dir := t.TempDir()
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
log := s.getGitLog(dir)
assert.Empty(t, log)
}
func TestPrep_GetGitLog_Ugly(t *testing.T) {
// Git repo with no commits — git log should fail, returns empty
dir := t.TempDir()
testCore.Process().RunIn(context.Background(), dir, "git", "init", "-b", "main")
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
log := s.getGitLog(dir)
assert.Empty(t, log)
}
// --- prepWorkspace Good ---
func TestPrep_PrepWorkspace_Good(t *testing.T) {
root := t.TempDir()
setTestWorkspace(t, root)
// Mock Forge API for issue body
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(core.JSONMarshalString(map[string]any{
"number": 1,
"title": "Fix tests",
"body": "Tests are broken",
})))
}))
t.Cleanup(srv.Close)
// Create a source repo to clone from
srcRepo := core.JoinPath(root, "src", "core", "test-repo")
gitEnv := []string{"GIT_AUTHOR_NAME=Test", "GIT_AUTHOR_EMAIL=test@test.com", "GIT_COMMITTER_NAME=Test", "GIT_COMMITTER_EMAIL=test@test.com"}
run := func(dir string, args ...string) {
t.Helper()
r := testCore.Process().RunWithEnv(context.Background(), dir, gitEnv, args[0], args[1:]...)
require.True(t, r.OK, "cmd %v failed: %s", args, r.Value)
}
require.True(t, fs.EnsureDir(srcRepo).OK)
run(srcRepo, "git", "init", "-b", "main")
run(srcRepo, "git", "config", "user.name", "Test")
run(srcRepo, "git", "config", "user.email", "test@test.com")
require.True(t, fs.Write(core.JoinPath(srcRepo, "README.md"), "# Test").OK)
run(srcRepo, "git", "add", "README.md")
run(srcRepo, "git", "commit", "-m", "initial commit")
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
forge: forge.NewForge(srv.URL, "test-token"),
codePath: core.JoinPath(root, "src"),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
_, out, err := s.PrepareWorkspace(context.Background(), PrepInput{
Repo: "test-repo",
Issue: 1,
Task: "Fix tests",
})
require.NoError(t, err)
assert.True(t, out.Success)
assert.NotEmpty(t, out.WorkspaceDir)
assert.NotEmpty(t, out.Branch)
assert.Contains(t, out.Branch, "agent/")
assert.NotEmpty(t, out.PromptVersion)
promptIndexPath := core.JoinPath(WorkspaceMetaDir(out.WorkspaceDir), "prompt-version.json")
require.True(t, fs.Exists(promptIndexPath))
promptIndexResult := fs.Read(promptIndexPath)
require.True(t, promptIndexResult.OK)
var promptSnapshot PromptVersionSnapshot
require.True(t, core.JSONUnmarshalString(promptIndexResult.Value.(string), &promptSnapshot).OK)
assert.Equal(t, out.PromptVersion, promptSnapshot.Hash)
assert.Contains(t, promptSnapshot.Content, "TASK: Fix tests")
promptSnapshotPath := core.JoinPath(WorkspaceMetaDir(out.WorkspaceDir), "prompt-versions", core.Concat(out.PromptVersion, ".json"))
require.True(t, fs.Exists(promptSnapshotPath))
}
func TestPrep_TestPrepWorkspace_Good(t *testing.T) {
root := t.TempDir()
setTestWorkspace(t, root)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(core.JSONMarshalString(map[string]any{
"number": 1,
"title": "Fix tests",
"body": "Tests are broken",
})))
}))
t.Cleanup(srv.Close)
srcRepo := core.JoinPath(root, "src", "core", "test-repo")
gitEnv := []string{"GIT_AUTHOR_NAME=Test", "GIT_AUTHOR_EMAIL=test@test.com", "GIT_COMMITTER_NAME=Test", "GIT_COMMITTER_EMAIL=test@test.com"}
run := func(dir string, args ...string) {
t.Helper()
r := testCore.Process().RunWithEnv(context.Background(), dir, gitEnv, args[0], args[1:]...)
require.True(t, r.OK, "cmd %v failed: %s", args, r.Value)
}
require.True(t, fs.EnsureDir(srcRepo).OK)
run(srcRepo, "git", "init", "-b", "main")
run(srcRepo, "git", "config", "user.name", "Test")
run(srcRepo, "git", "config", "user.email", "test@test.com")
require.True(t, fs.Write(core.JoinPath(srcRepo, "README.md"), "# Test").OK)
run(srcRepo, "git", "add", "README.md")
run(srcRepo, "git", "commit", "-m", "initial commit")
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
forge: forge.NewForge(srv.URL, "test-token"),
codePath: core.JoinPath(root, "src"),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
_, out, err := s.TestPrepWorkspace(context.Background(), PrepInput{
Repo: "test-repo",
Issue: 1,
Task: "Fix tests",
})
require.NoError(t, err)
assert.True(t, out.Success)
assert.NotEmpty(t, out.WorkspaceDir)
}
func TestPrep_TestPrepWorkspace_Bad(t *testing.T) {
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
_, _, err := s.TestPrepWorkspace(context.Background(), PrepInput{Repo: "."})
require.Error(t, err)
}
func TestPrep_TestPrepWorkspace_Ugly(t *testing.T) {
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
_, _, err := s.TestPrepWorkspace(context.Background(), PrepInput{Repo: ".."})
require.Error(t, err)
}