- paths.go: resolve relative workspace_root against $HOME/Code so workspaces land in the conventional location regardless of launch cwd (MCP stdio vs CLI) - dispatch.go: container mounts use /home/agent (matches DEV_USER), plus runtime-aware dispatch (apple/docker/podman) with GPU toggle per RFC §15.5 - queue.go / runner/queue.go: DispatchConfig adds Runtime/Image/GPU fields; AgentIdentity parsing for the agents: block (RFC §10/§11) - pr.go / commands_forge.go / actions.go: agentic_delete_branch tool + branch/delete CLI (RFC §7) - brain/tools.go / provider.go: Org + IndexedAt fields on Memory (RFC §4) - config/agents.yaml: document new dispatch fields, fix identity table - tests: dispatch_runtime_test.go (21), expanded pr_test.go + queue_test.go, new CLI fixtures for branch/delete and pr/list Co-Authored-By: Virgil <virgil@lethean.io>
203 lines
7.4 KiB
Go
203 lines
7.4 KiB
Go
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package agentic
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
core "dappco.re/go/core"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// --- containerRuntimeBinary ---
|
|
|
|
func TestDispatchRuntime_ContainerRuntimeBinary_Good(t *testing.T) {
|
|
assert.Equal(t, "container", containerRuntimeBinary(RuntimeApple))
|
|
assert.Equal(t, "docker", containerRuntimeBinary(RuntimeDocker))
|
|
assert.Equal(t, "podman", containerRuntimeBinary(RuntimePodman))
|
|
}
|
|
|
|
func TestDispatchRuntime_ContainerRuntimeBinary_Bad(t *testing.T) {
|
|
// Unknown runtime falls back to docker so dispatch never silently breaks.
|
|
assert.Equal(t, "docker", containerRuntimeBinary(""))
|
|
assert.Equal(t, "docker", containerRuntimeBinary("kubernetes"))
|
|
}
|
|
|
|
func TestDispatchRuntime_ContainerRuntimeBinary_Ugly(t *testing.T) {
|
|
// Whitespace-laden runtime name is treated as unknown; docker fallback wins.
|
|
assert.Equal(t, "docker", containerRuntimeBinary(" apple "))
|
|
}
|
|
|
|
// --- runtimeAvailable ---
|
|
|
|
func TestDispatchRuntime_RuntimeAvailable_Good(t *testing.T) {
|
|
// Inspect only the failure path that doesn't depend on host binaries.
|
|
// Apple Container is by definition unavailable on non-darwin.
|
|
if !isDarwin() {
|
|
assert.False(t, runtimeAvailable(RuntimeApple))
|
|
}
|
|
}
|
|
|
|
func TestDispatchRuntime_RuntimeAvailable_Bad(t *testing.T) {
|
|
// Unknown runtimes are never available.
|
|
assert.False(t, runtimeAvailable(""))
|
|
assert.False(t, runtimeAvailable("kubernetes"))
|
|
}
|
|
|
|
func TestDispatchRuntime_RuntimeAvailable_Ugly(t *testing.T) {
|
|
// Apple Container on non-macOS hosts is always unavailable, regardless of
|
|
// whether a binary called "container" happens to be on PATH.
|
|
if !isDarwin() {
|
|
assert.False(t, runtimeAvailable(RuntimeApple))
|
|
}
|
|
}
|
|
|
|
// --- resolveContainerRuntime ---
|
|
|
|
func TestDispatchRuntime_ResolveContainerRuntime_Good(t *testing.T) {
|
|
// Empty preference falls back to one of the known runtimes (docker is the
|
|
// hard fallback, but the function may surface apple/podman when those
|
|
// binaries exist on the test host).
|
|
resolved := resolveContainerRuntime("")
|
|
assert.Contains(t, []string{RuntimeApple, RuntimeDocker, RuntimePodman}, resolved)
|
|
}
|
|
|
|
func TestDispatchRuntime_ResolveContainerRuntime_Bad(t *testing.T) {
|
|
// An unknown runtime preference still resolves to a known runtime.
|
|
resolved := resolveContainerRuntime("kubernetes")
|
|
assert.Contains(t, []string{RuntimeApple, RuntimeDocker, RuntimePodman}, resolved)
|
|
}
|
|
|
|
func TestDispatchRuntime_ResolveContainerRuntime_Ugly(t *testing.T) {
|
|
// Apple preference on non-darwin host falls back to a non-apple runtime.
|
|
if !isDarwin() {
|
|
resolved := resolveContainerRuntime(RuntimeApple)
|
|
assert.NotEqual(t, RuntimeApple, resolved)
|
|
}
|
|
}
|
|
|
|
// --- containerCommandFor ---
|
|
|
|
func TestDispatchRuntime_ContainerCommandFor_Good(t *testing.T) {
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "")
|
|
t.Setenv("DIR_HOME", "/home/dev")
|
|
|
|
// Docker runtime emits docker binary and includes host-gateway alias.
|
|
cmd, args := containerCommandFor(RuntimeDocker, "core-dev", false, "codex", []string{"exec"}, "/ws", "/ws/.meta")
|
|
assert.Equal(t, "docker", cmd)
|
|
joined := strings.Join(args, " ")
|
|
assert.Contains(t, joined, "--add-host=host.docker.internal:host-gateway")
|
|
assert.Contains(t, joined, "core-dev")
|
|
}
|
|
|
|
func TestDispatchRuntime_ContainerCommandFor_Bad(t *testing.T) {
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "")
|
|
t.Setenv("DIR_HOME", "/home/dev")
|
|
|
|
// Empty image resolves to the default rather than passing "" to docker.
|
|
cmd, args := containerCommandFor(RuntimeDocker, "", false, "codex", nil, "/ws", "/ws/.meta")
|
|
assert.Equal(t, "docker", cmd)
|
|
assert.Contains(t, args, defaultDockerImage)
|
|
}
|
|
|
|
func TestDispatchRuntime_ContainerCommandFor_Ugly(t *testing.T) {
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "")
|
|
t.Setenv("DIR_HOME", "/home/dev")
|
|
|
|
// Apple runtime emits the `container` binary and SKIPS the host-gateway
|
|
// alias because Apple Containers don't support `--add-host=host-gateway`.
|
|
cmd, args := containerCommandFor(RuntimeApple, "core-dev", false, "codex", []string{"exec"}, "/ws", "/ws/.meta")
|
|
assert.Equal(t, "container", cmd)
|
|
joined := strings.Join(args, " ")
|
|
assert.NotContains(t, joined, "--add-host=host.docker.internal:host-gateway")
|
|
|
|
// Podman runtime emits the `podman` binary.
|
|
cmd2, _ := containerCommandFor(RuntimePodman, "core-dev", false, "codex", []string{"exec"}, "/ws", "/ws/.meta")
|
|
assert.Equal(t, "podman", cmd2)
|
|
|
|
// GPU passthrough on docker emits `--gpus=all`.
|
|
_, gpuArgs := containerCommandFor(RuntimeDocker, "core-dev", true, "codex", []string{"exec"}, "/ws", "/ws/.meta")
|
|
assert.Contains(t, strings.Join(gpuArgs, " "), "--gpus=all")
|
|
|
|
// GPU passthrough on apple emits `--gpu=metal` for Metal passthrough.
|
|
_, appleGPUArgs := containerCommandFor(RuntimeApple, "core-dev", true, "codex", []string{"exec"}, "/ws", "/ws/.meta")
|
|
assert.Contains(t, strings.Join(appleGPUArgs, " "), "--gpu=metal")
|
|
}
|
|
|
|
// --- dispatchRuntime / dispatchImage / dispatchGPU ---
|
|
|
|
func TestDispatchRuntime_DispatchRuntime_Good(t *testing.T) {
|
|
t.Setenv("CORE_AGENT_RUNTIME", "")
|
|
c := core.New()
|
|
c.Config().Set("agents.dispatch", DispatchConfig{Runtime: "podman"})
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.Equal(t, "podman", s.dispatchRuntime())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchRuntime_Bad(t *testing.T) {
|
|
t.Setenv("CORE_AGENT_RUNTIME", "")
|
|
// Nil subsystem returns the auto default.
|
|
var s *PrepSubsystem
|
|
assert.Equal(t, RuntimeAuto, s.dispatchRuntime())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchRuntime_Ugly(t *testing.T) {
|
|
// Env var override wins over configured runtime.
|
|
t.Setenv("CORE_AGENT_RUNTIME", "apple")
|
|
c := core.New()
|
|
c.Config().Set("agents.dispatch", DispatchConfig{Runtime: "podman"})
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.Equal(t, "apple", s.dispatchRuntime())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchImage_Good(t *testing.T) {
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "")
|
|
c := core.New()
|
|
c.Config().Set("agents.dispatch", DispatchConfig{Image: "core-ml"})
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.Equal(t, "core-ml", s.dispatchImage())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchImage_Bad(t *testing.T) {
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "")
|
|
// Nil subsystem falls back to the default image.
|
|
var s *PrepSubsystem
|
|
assert.Equal(t, defaultDockerImage, s.dispatchImage())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchImage_Ugly(t *testing.T) {
|
|
// Env var override wins over configured image.
|
|
t.Setenv("AGENT_DOCKER_IMAGE", "ad-hoc-image")
|
|
c := core.New()
|
|
c.Config().Set("agents.dispatch", DispatchConfig{Image: "core-ml"})
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.Equal(t, "ad-hoc-image", s.dispatchImage())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchGPU_Good(t *testing.T) {
|
|
c := core.New()
|
|
c.Config().Set("agents.dispatch", DispatchConfig{GPU: true})
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.True(t, s.dispatchGPU())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchGPU_Bad(t *testing.T) {
|
|
// Nil subsystem returns false (GPU off by default).
|
|
var s *PrepSubsystem
|
|
assert.False(t, s.dispatchGPU())
|
|
}
|
|
|
|
func TestDispatchRuntime_DispatchGPU_Ugly(t *testing.T) {
|
|
// Missing dispatch config returns false instead of panicking.
|
|
c := core.New()
|
|
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
|
|
assert.False(t, s.dispatchGPU())
|
|
}
|
|
|
|
// isDarwin checks the host operating system without importing runtime in the
|
|
// test file (the import happens in dispatch.go where it's needed for the real
|
|
// detection logic).
|
|
func isDarwin() bool {
|
|
return goosIsDarwin
|
|
}
|