package orch import ( "path/filepath" "testing" ) func TestOrchAnswerAcceptsPayloadJSONWithoutBody(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") threadID := seedBlockedTaskForAnswerCleanupEdgeTests(t, dbPath, "run_blog_answer_001", "T2", "worker-b") answerOut := runOrchCommand( t, "--db", dbPath, "--json", "answer", "--run", "run_blog_answer_001", "--task", "T2", "--payload-json", `{"decision":"stdout","source":"leader"}`, ) var answerResp map[string]any mustDecodeJSON(t, answerOut, &answerResp) message, ok := nestedValue(t, answerResp, "data", "message").(map[string]any) if !ok { t.Fatalf("expected answer message object, got %#v", nestedValue(t, answerResp, "data", "message")) } if got, _ := message["kind"].(string); got != "answer" { t.Fatalf("expected answer message kind, got %#v", message["kind"]) } payload, ok := message["payload_json"].(map[string]any) if !ok { t.Fatalf("expected payload_json object, got %#v", message["payload_json"]) } if got, _ := payload["decision"].(string); got != "stdout" { t.Fatalf("expected payload decision stdout, got %#v", payload["decision"]) } if got, _ := payload["source"].(string); got != "leader" { t.Fatalf("expected payload source leader, got %#v", payload["source"]) } showOut := runInboxCommand( t, "--db", dbPath, "--json", "show", "--thread", threadID, ) var showResp map[string]any mustDecodeJSON(t, showOut, &showResp) messages := nestedArray(t, showResp, "data", "messages") if len(messages) == 0 { t.Fatalf("expected messages in thread %s", threadID) } lastMessage, ok := messages[len(messages)-1].(map[string]any) if !ok { t.Fatalf("expected last message object, got %#v", messages[len(messages)-1]) } if got, _ := lastMessage["kind"].(string); got != "answer" { t.Fatalf("expected latest message kind answer, got %#v", lastMessage["kind"]) } lastPayload, ok := lastMessage["payload_json"].(map[string]any) if !ok { t.Fatalf("expected latest payload_json object, got %#v", lastMessage["payload_json"]) } if got, _ := lastPayload["decision"].(string); got != "stdout" { t.Fatalf("expected latest payload decision stdout, got %#v", lastPayload["decision"]) } } func TestOrchAnswerRejectsEmptyBodyAndPayload(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") _ = seedBlockedTaskForAnswerCleanupEdgeTests(t, dbPath, "run_blog_answer_002", "T2", "worker-b") stdout, _, exitCode := executeOrchCommand( "--db", dbPath, "--json", "answer", "--run", "run_blog_answer_002", "--task", "T2", ) if exitCode != 30 { t.Fatalf("expected exit code 30, got %d\nstdout:\n%s", exitCode, stdout) } assertErrorJSON(t, stdout, "invalid_input") } func TestOrchCleanupRejectsAttemptWithoutTask(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runOrchCommand( t, "--db", dbPath, "--json", "run", "init", "--run", "run_blog_cleanup_002", "--goal", "Validate cleanup selectors", ) stdout, _, exitCode := executeOrchCommand( "--db", dbPath, "--json", "cleanup", "--run", "run_blog_cleanup_002", "--attempt", "1", ) if exitCode != 30 { t.Fatalf("expected exit code 30, got %d\nstdout:\n%s", exitCode, stdout) } assertErrorJSON(t, stdout, "invalid_input") } func TestOrchCleanupReturnsNoMatchingWorkWhenFiltersMiss(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runOrchCommand( t, "--db", dbPath, "--json", "run", "init", "--run", "run_blog_cleanup_003", "--goal", "Validate cleanup empty result", ) runOrchCommand( t, "--db", dbPath, "--json", "task", "add", "--run", "run_blog_cleanup_003", "--task", "T1", "--title", "Prepare cleanup target", ) stdout, _, exitCode := executeOrchCommand( "--db", dbPath, "--json", "cleanup", "--run", "run_blog_cleanup_003", "--task", "T1", ) if exitCode != 10 { t.Fatalf("expected exit code 10, got %d\nstdout:\n%s", exitCode, stdout) } assertErrorJSON(t, stdout, "no_matching_work") } func seedBlockedTaskForAnswerCleanupEdgeTests(t *testing.T, dbPath, runID, taskID, agent string) string { t.Helper() runOrchCommand( t, "--db", dbPath, "--json", "run", "init", "--run", runID, "--goal", "Prepare blocked task for answer edge tests", ) runOrchCommand( t, "--db", dbPath, "--json", "task", "add", "--run", runID, "--task", taskID, "--title", "Build frontend", "--default-to", agent, ) dispatchOut := runOrchCommand( t, "--db", dbPath, "--json", "dispatch", "--run", runID, "--task", taskID, ) var dispatchResp map[string]any mustDecodeJSON(t, dispatchOut, &dispatchResp) threadID := nestedString(t, dispatchResp, "data", "attempt", "thread_id") runInboxCommand( t, "--db", dbPath, "--json", "claim", "--agent", agent, "--thread", threadID, ) runInboxCommand( t, "--db", dbPath, "--json", "update", "--agent", agent, "--thread", threadID, "--status", "blocked", "--summary", "Need logging decision", "--payload-json", `{"question":"stdout or stderr?"}`, ) runOrchCommand( t, "--db", dbPath, "--json", "reconcile", "--run", runID, ) return threadID }