Files

175 lines
5.4 KiB
Go

package workspaceruntime
import (
"os"
"path/filepath"
"testing"
"inbox/internal/domain/lane"
"inbox/internal/domain/workspace"
)
func TestDriftedWhenRunnerPortIsMissing(t *testing.T) {
ws := workspace.Workspace{
ID: "ws_1",
RootPath: "/tmp/ws",
}
item := lane.Record{
ID: "chain_1",
ContainerName: "lane-demo",
WorktreePath: "/tmp/ws-chain",
}
runtime := &containerRuntime{serverPort: 3000}
info := validInspect(ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1")
delete(info.NetworkSettings.Ports, runnerContainerPort)
if !runtime.laneDrifted(info, ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1") {
t.Fatalf("expected container without %s mapping to drift", runnerContainerPort)
}
}
func TestDriftedWhenRunnerPortIsPublished(t *testing.T) {
ws := workspace.Workspace{
ID: "ws_1",
RootPath: "/tmp/ws",
}
item := lane.Record{
ID: "chain_1",
ContainerName: "lane-demo",
WorktreePath: "/tmp/ws-chain",
}
runtime := &containerRuntime{serverPort: 3000}
info := validInspect(ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1")
if runtime.laneDrifted(info, ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1") {
t.Fatalf("expected container with %s mapping to remain reusable", runnerContainerPort)
}
}
func TestDriftedWhenContainerUserIsNotRoot(t *testing.T) {
ws := workspace.Workspace{ID: "ws_1", RootPath: "/tmp/ws"}
item := lane.Record{
ID: "chain_1",
ContainerName: "lane-demo",
WorktreePath: "/tmp/ws-chain",
}
runtime := &containerRuntime{serverPort: 3000}
info := validInspect(ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1")
info.Config.User = "runner"
if !runtime.laneDrifted(info, ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1") {
t.Fatalf("expected non-root container user to drift")
}
}
func TestDriftedWhenRuntimeCodexFingerprintChanges(t *testing.T) {
ws := workspace.Workspace{ID: "ws_1", RootPath: "/tmp/ws"}
item := lane.Record{
ID: "chain_1",
ContainerName: "lane-demo",
WorktreePath: "/tmp/ws-chain",
}
runtime := &containerRuntime{serverPort: 3000}
info := validInspect(ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-1")
if !runtime.laneDrifted(info, ws, item, "/tmp/lane-worker", "/tmp/inbox", "codex-sha-2") {
t.Fatalf("expected runtime codex fingerprint mismatch to drift")
}
}
func TestRuntimeCodexFingerprint(t *testing.T) {
root := t.TempDir()
if err := os.WriteFile(filepath.Join(root, "config.toml"), []byte("model = \"gpt-5.3-codex\"\n"), 0644); err != nil {
t.Fatalf("WriteFile(config.toml) error = %v", err)
}
if err := os.WriteFile(filepath.Join(root, "auth.json"), []byte("{\"OPENAI_API_KEY\":\"token\"}\n"), 0600); err != nil {
t.Fatalf("WriteFile(auth.json) error = %v", err)
}
sum1, err := runtimeCodexFingerprint(root)
if err != nil {
t.Fatalf("runtimeCodexFingerprint() error = %v", err)
}
if err := os.WriteFile(filepath.Join(root, "auth.json"), []byte("{\"OPENAI_API_KEY\":\"changed\"}\n"), 0600); err != nil {
t.Fatalf("WriteFile(auth.json) error = %v", err)
}
sum2, err := runtimeCodexFingerprint(root)
if err != nil {
t.Fatalf("runtimeCodexFingerprint() error = %v", err)
}
if sum1 == "" || sum2 == "" {
t.Fatalf("expected non-empty fingerprints, got %q and %q", sum1, sum2)
}
if sum1 == sum2 {
t.Fatalf("expected fingerprint to change when runtime codex contents change")
}
}
func TestRuntimeCodexFingerprintChangesForNestedSkillFiles(t *testing.T) {
root := t.TempDir()
skillDir := filepath.Join(root, "skills", "inbox")
if err := os.MkdirAll(skillDir, 0755); err != nil {
t.Fatalf("MkdirAll(skillDir) error = %v", err)
}
if err := os.WriteFile(filepath.Join(skillDir, "SKILL.md"), []byte("# Inbox\n"), 0644); err != nil {
t.Fatalf("WriteFile(SKILL.md) error = %v", err)
}
sum1, err := runtimeCodexFingerprint(root)
if err != nil {
t.Fatalf("runtimeCodexFingerprint() error = %v", err)
}
if err := os.WriteFile(filepath.Join(skillDir, "SKILL.md"), []byte("# Inbox\n\nUpdated\n"), 0644); err != nil {
t.Fatalf("WriteFile(SKILL.md) error = %v", err)
}
sum2, err := runtimeCodexFingerprint(root)
if err != nil {
t.Fatalf("runtimeCodexFingerprint() error = %v", err)
}
if sum1 == sum2 {
t.Fatalf("expected nested skill file change to affect fingerprint")
}
}
func validInspect(ws workspace.Workspace, item lane.Record, workerBinary, inboxBinary, codexFingerprint string) podmanInspect {
var info podmanInspect
info.ImageName = runtimeBaseImage
info.Path = laneWorkerContainerPath
info.Config.Image = runtimeBaseImage
info.Config.User = "root"
info.Config.WorkingDir = "/workspace"
info.Config.Env = []string{
"INBOX_WORKSPACE=/workspace",
"INBOX_WORKSPACE_ID=" + ws.ID,
"INBOX_LANE_ID=" + item.ID,
"INBOX_RUNTIME_AGENT_ID=" + item.ContainerName,
"INBOX_API_URL=http://host.containers.internal:3000",
"HOME=/root",
"INBOX_RUNTIME_CODEX_SHA=" + codexFingerprint,
}
info.NetworkSettings.Ports = map[string][]struct {
HostIP string `json:"HostIp"`
HostPort string `json:"HostPort"`
}{
runnerContainerPort: {{
HostIP: "127.0.0.1",
HostPort: "40123",
}},
}
info.Mounts = []struct {
Source string `json:"Source"`
Destination string `json:"Destination"`
}{
{Source: item.WorktreePath, Destination: "/workspace"},
{Source: workerBinary, Destination: laneWorkerContainerPath},
{Source: inboxBinary, Destination: inboxContainerPath},
}
return info
}