// 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" 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) { server := mcpsdk.NewServer(&mcpsdk.Implementation{Name: "test", Version: "0.1.0"}, &mcpsdk.ServerOptions{ Capabilities: &mcpsdk.ServerCapabilities{ Tools: &mcpsdk.ToolCapabilities{ListChanged: true}, }, }) subsystem := &PrepSubsystem{} subsystem.RegisterTools(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() t.Setenv("CORE_WORKSPACE", 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() t.Setenv("CORE_WORKSPACE", 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() t.Setenv("CORE_WORKSPACE", 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() t.Setenv("CORE_WORKSPACE", 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) }