Add orch strict worktree dispatch
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package orch
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
@@ -504,3 +505,224 @@ func TestOrchDispatchRejectsNonReadyTask(t *testing.T) {
|
||||
}
|
||||
assertErrorJSON(t, stdout, "invalid_state")
|
||||
}
|
||||
|
||||
func TestOrchDispatchCreatesStrictWorktree(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dbPath := filepath.Join(t.TempDir(), "coord.db")
|
||||
repoPath := initGitRepo(t)
|
||||
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"run", "init",
|
||||
"--run", "run_blog_worktree_001",
|
||||
"--goal", "Validate strict worktree dispatch",
|
||||
)
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"task", "add",
|
||||
"--run", "run_blog_worktree_001",
|
||||
"--task", "T1",
|
||||
"--title", "Implement backend",
|
||||
"--default-to", "worker-a",
|
||||
)
|
||||
|
||||
dispatchOut := runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"dispatch",
|
||||
"--run", "run_blog_worktree_001",
|
||||
"--task", "T1",
|
||||
"--repo-path", repoPath,
|
||||
"--workspace-root", ".orch/worktrees",
|
||||
"--strict-worktree",
|
||||
"--body", "Implement inside isolated worktree.",
|
||||
)
|
||||
|
||||
var dispatchResp map[string]any
|
||||
mustDecodeJSON(t, dispatchOut, &dispatchResp)
|
||||
attempt, ok := nestedValue(t, dispatchResp, "data", "attempt").(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected attempt object, got %#v", nestedValue(t, dispatchResp, "data", "attempt"))
|
||||
}
|
||||
|
||||
if got, _ := attempt["base_ref"].(string); got != "HEAD" {
|
||||
t.Fatalf("expected base_ref HEAD, got %#v", attempt["base_ref"])
|
||||
}
|
||||
expectedCommit := gitHeadCommit(t, repoPath)
|
||||
if got, _ := attempt["base_commit"].(string); got != expectedCommit {
|
||||
t.Fatalf("expected base_commit %q, got %#v", expectedCommit, attempt["base_commit"])
|
||||
}
|
||||
if got, _ := attempt["branch_name"].(string); got != "orch/run-blog-worktree-001/T1/attempt-1" {
|
||||
t.Fatalf("unexpected branch name %#v", attempt["branch_name"])
|
||||
}
|
||||
|
||||
worktreePath, _ := attempt["worktree_path"].(string)
|
||||
if worktreePath == "" {
|
||||
t.Fatalf("expected worktree_path, got %#v", attempt["worktree_path"])
|
||||
}
|
||||
if got, _ := attempt["workspace_status"].(string); got != "created" {
|
||||
t.Fatalf("expected workspace_status created, got %#v", attempt["workspace_status"])
|
||||
}
|
||||
|
||||
if _, err := os.Stat(worktreePath); err != nil {
|
||||
t.Fatalf("stat worktree path %s: %v", worktreePath, err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(worktreePath, "README.md")); err != nil {
|
||||
t.Fatalf("expected README.md in worktree: %v", err)
|
||||
}
|
||||
|
||||
message, ok := nestedValue(t, dispatchResp, "data", "message").(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected message object, got %#v", nestedValue(t, dispatchResp, "data", "message"))
|
||||
}
|
||||
payload, ok := message["payload_json"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected payload_json object, got %#v", message["payload_json"])
|
||||
}
|
||||
if got, _ := payload["worktree_path"].(string); got != worktreePath {
|
||||
t.Fatalf("expected payload worktree path %q, got %#v", worktreePath, payload["worktree_path"])
|
||||
}
|
||||
|
||||
threadID := nestedString(t, dispatchResp, "data", "attempt", "thread_id")
|
||||
runInboxCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"claim",
|
||||
"--agent", "worker-a",
|
||||
"--thread", threadID,
|
||||
)
|
||||
runInboxCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"update",
|
||||
"--agent", "worker-a",
|
||||
"--thread", threadID,
|
||||
"--status", "in_progress",
|
||||
"--summary", "Started inside worktree",
|
||||
)
|
||||
|
||||
reconcileOut := runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"reconcile",
|
||||
"--run", "run_blog_worktree_001",
|
||||
)
|
||||
|
||||
var reconcileResp map[string]any
|
||||
mustDecodeJSON(t, reconcileOut, &reconcileResp)
|
||||
updatedTasks := nestedArray(t, reconcileResp, "data", "updated_tasks")
|
||||
if len(updatedTasks) != 1 {
|
||||
t.Fatalf("expected one updated task after worktree reconcile, got %#v", updatedTasks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrchStrictWorktreeRejectsDirtyRepoWithoutBaseRef(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dbPath := filepath.Join(t.TempDir(), "coord.db")
|
||||
repoPath := initGitRepo(t)
|
||||
|
||||
if err := os.WriteFile(filepath.Join(repoPath, "dirty.txt"), []byte("dirty\n"), 0o644); err != nil {
|
||||
t.Fatalf("write dirty file: %v", err)
|
||||
}
|
||||
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"run", "init",
|
||||
"--run", "run_blog_worktree_002",
|
||||
"--goal", "Validate dirty repo rejection",
|
||||
)
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"task", "add",
|
||||
"--run", "run_blog_worktree_002",
|
||||
"--task", "T1",
|
||||
"--title", "Implement backend",
|
||||
"--default-to", "worker-a",
|
||||
)
|
||||
|
||||
stdout, _, exitCode := executeOrchCommand(
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"dispatch",
|
||||
"--run", "run_blog_worktree_002",
|
||||
"--task", "T1",
|
||||
"--repo-path", repoPath,
|
||||
"--workspace-root", ".orch/worktrees",
|
||||
"--strict-worktree",
|
||||
)
|
||||
if exitCode != 30 {
|
||||
t.Fatalf("expected invalid_state exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
|
||||
}
|
||||
assertErrorJSON(t, stdout, "invalid_state")
|
||||
|
||||
if _, err := os.Stat(filepath.Join(repoPath, ".orch", "worktrees", "run_blog_worktree_002", "T1", "attempt-1")); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected no worktree directory on strict failure, got err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrchStrictWorktreeAllowsExplicitBaseRefOnDirtyRepo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dbPath := filepath.Join(t.TempDir(), "coord.db")
|
||||
repoPath := initGitRepo(t)
|
||||
baseCommit := gitHeadCommit(t, repoPath)
|
||||
|
||||
if err := os.WriteFile(filepath.Join(repoPath, "dirty.txt"), []byte("dirty\n"), 0o644); err != nil {
|
||||
t.Fatalf("write dirty file: %v", err)
|
||||
}
|
||||
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"run", "init",
|
||||
"--run", "run_blog_worktree_003",
|
||||
"--goal", "Validate explicit base ref on dirty repo",
|
||||
)
|
||||
runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"task", "add",
|
||||
"--run", "run_blog_worktree_003",
|
||||
"--task", "T1",
|
||||
"--title", "Implement backend",
|
||||
"--default-to", "worker-a",
|
||||
)
|
||||
|
||||
dispatchOut := runOrchCommand(
|
||||
t,
|
||||
"--db", dbPath,
|
||||
"--json",
|
||||
"dispatch",
|
||||
"--run", "run_blog_worktree_003",
|
||||
"--task", "T1",
|
||||
"--repo-path", repoPath,
|
||||
"--workspace-root", ".orch/worktrees",
|
||||
"--strict-worktree",
|
||||
"--base-ref", "HEAD",
|
||||
)
|
||||
|
||||
var dispatchResp map[string]any
|
||||
mustDecodeJSON(t, dispatchOut, &dispatchResp)
|
||||
if got := nestedString(t, dispatchResp, "data", "attempt", "base_ref"); got != "HEAD" {
|
||||
t.Fatalf("expected explicit base_ref HEAD, got %q", got)
|
||||
}
|
||||
if got := nestedString(t, dispatchResp, "data", "attempt", "base_commit"); got != baseCommit {
|
||||
t.Fatalf("expected base_commit %q, got %q", baseCommit, got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user