2026-03-21 15:05:40 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package agentic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
2026-03-21 15:05:40 +00:00
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestWriteStatus_Good(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
status := &WorkspaceStatus{
|
|
|
|
|
Status: "running",
|
|
|
|
|
Agent: "gemini",
|
|
|
|
|
Repo: "go-io",
|
|
|
|
|
Task: "fix tests",
|
|
|
|
|
PID: 12345,
|
|
|
|
|
StartedAt: time.Now(),
|
|
|
|
|
Runs: 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := writeStatus(dir, status)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2026-03-22 03:41:07 +00:00
|
|
|
// Verify file was written via core.Fs
|
|
|
|
|
r := fs.Read(filepath.Join(dir, "status.json"))
|
|
|
|
|
require.True(t, r.OK)
|
2026-03-21 15:05:40 +00:00
|
|
|
|
|
|
|
|
var read WorkspaceStatus
|
2026-03-22 03:41:07 +00:00
|
|
|
err = json.Unmarshal([]byte(r.Value.(string)), &read)
|
2026-03-21 15:05:40 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "running", read.Status)
|
|
|
|
|
assert.Equal(t, "gemini", read.Agent)
|
|
|
|
|
assert.Equal(t, "go-io", read.Repo)
|
|
|
|
|
assert.Equal(t, "fix tests", read.Task)
|
|
|
|
|
assert.Equal(t, 12345, read.PID)
|
|
|
|
|
assert.Equal(t, 1, read.Runs)
|
|
|
|
|
assert.False(t, read.UpdatedAt.IsZero(), "UpdatedAt should be set by writeStatus")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWriteStatus_Good_UpdatesTimestamp(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
before := time.Now().Add(-time.Second)
|
|
|
|
|
|
|
|
|
|
status := &WorkspaceStatus{
|
|
|
|
|
Status: "running",
|
|
|
|
|
Agent: "claude",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := writeStatus(dir, status)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.True(t, status.UpdatedAt.After(before), "UpdatedAt should be after the start time")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReadStatus_Good(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
status := &WorkspaceStatus{
|
|
|
|
|
Status: "completed",
|
|
|
|
|
Agent: "codex",
|
|
|
|
|
Repo: "go-log",
|
|
|
|
|
Task: "add logging",
|
|
|
|
|
Branch: "agent/add-logging",
|
|
|
|
|
StartedAt: time.Now().Truncate(time.Second),
|
|
|
|
|
UpdatedAt: time.Now().Truncate(time.Second),
|
|
|
|
|
Runs: 2,
|
|
|
|
|
PRURL: "https://forge.lthn.ai/core/go-log/pulls/5",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := json.MarshalIndent(status, "", " ")
|
|
|
|
|
require.NoError(t, err)
|
2026-03-22 03:41:07 +00:00
|
|
|
require.True(t, fs.Write(filepath.Join(dir, "status.json"), string(data)).OK)
|
2026-03-21 15:05:40 +00:00
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
read, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "completed", read.Status)
|
|
|
|
|
assert.Equal(t, "codex", read.Agent)
|
|
|
|
|
assert.Equal(t, "go-log", read.Repo)
|
|
|
|
|
assert.Equal(t, "add logging", read.Task)
|
|
|
|
|
assert.Equal(t, "agent/add-logging", read.Branch)
|
|
|
|
|
assert.Equal(t, 2, read.Runs)
|
|
|
|
|
assert.Equal(t, "https://forge.lthn.ai/core/go-log/pulls/5", read.PRURL)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReadStatus_Bad_NoFile(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
2026-03-24 13:02:41 +00:00
|
|
|
_, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReadStatus_Bad_InvalidJSON(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
2026-03-22 03:41:07 +00:00
|
|
|
require.True(t, fs.Write(filepath.Join(dir, "status.json"), "not json{").OK)
|
2026-03-21 15:05:40 +00:00
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
_, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReadStatus_Good_BlockedWithQuestion(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
status := &WorkspaceStatus{
|
|
|
|
|
Status: "blocked",
|
|
|
|
|
Agent: "gemini",
|
|
|
|
|
Repo: "go-io",
|
|
|
|
|
Question: "Which interface should I implement?",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := json.MarshalIndent(status, "", " ")
|
|
|
|
|
require.NoError(t, err)
|
2026-03-22 03:41:07 +00:00
|
|
|
require.True(t, fs.Write(filepath.Join(dir, "status.json"), string(data)).OK)
|
2026-03-21 15:05:40 +00:00
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
read, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "blocked", read.Status)
|
|
|
|
|
assert.Equal(t, "Which interface should I implement?", read.Question)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWriteReadStatus_Good_Roundtrip(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
original := &WorkspaceStatus{
|
|
|
|
|
Status: "running",
|
|
|
|
|
Agent: "claude:opus",
|
|
|
|
|
Repo: "agent",
|
|
|
|
|
Org: "core",
|
|
|
|
|
Task: "write tests for agentic package",
|
|
|
|
|
Branch: "agent/write-tests",
|
|
|
|
|
Issue: 42,
|
|
|
|
|
PID: 99999,
|
|
|
|
|
StartedAt: time.Now().Truncate(time.Second),
|
|
|
|
|
Runs: 3,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := writeStatus(dir, original)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
read, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, original.Status, read.Status)
|
|
|
|
|
assert.Equal(t, original.Agent, read.Agent)
|
|
|
|
|
assert.Equal(t, original.Repo, read.Repo)
|
|
|
|
|
assert.Equal(t, original.Org, read.Org)
|
|
|
|
|
assert.Equal(t, original.Task, read.Task)
|
|
|
|
|
assert.Equal(t, original.Branch, read.Branch)
|
|
|
|
|
assert.Equal(t, original.Issue, read.Issue)
|
|
|
|
|
assert.Equal(t, original.PID, read.PID)
|
|
|
|
|
assert.Equal(t, original.Runs, read.Runs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWriteStatus_Good_OverwriteExisting(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
first := &WorkspaceStatus{Status: "running", Agent: "gemini"}
|
|
|
|
|
err := writeStatus(dir, first)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
second := &WorkspaceStatus{Status: "completed", Agent: "gemini"}
|
|
|
|
|
err = writeStatus(dir, second)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
read, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, "completed", read.Status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReadStatus_Ugly_EmptyFile(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
2026-03-22 03:41:07 +00:00
|
|
|
require.True(t, fs.Write(filepath.Join(dir, "status.json"), "").OK)
|
2026-03-21 15:05:40 +00:00
|
|
|
|
2026-03-24 13:02:41 +00:00
|
|
|
_, err := ReadStatus(dir)
|
2026-03-21 15:05:40 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|