"""Real LocalTransport + runner executors against throwaway scripts (local processes only, no network).""" import os import tempfile import time import unittest from pathlib import Path from agentd.config import Config from agentd.executors import Run, make_executor from agentd.transports.local import LocalTransport from tests.fakes import config_data FAKE_CLAUDE = """#!/usr/bin/env bash prompt="$(cat)" echo "args: $*" >&2 printf '{"type":"result","subtype":"success","is_error":false,"result":"[review] got %s","session_id":"sess-42"}' "${#prompt}" """ def wait(ex, handle, seconds=10): end = time.time() + seconds while time.time() < end: if ex.poll(handle) == "finished": return True time.sleep(0.1) return False class LocalRunnerTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() root = Path(self.tmp.name) fake = root / "fake-claude" fake.write_text(FAKE_CLAUDE) fake.chmod(0o755) data = config_data() data["agentd"]["state_dir"] = str(root / "state") data["terminals"]["oregon"]["bins"] = {"claude": str(fake)} data["terminals"]["oregon"]["repo"] = str(root) self.cfg = Config(data) self.t = LocalTransport(self.cfg, "oregon") self.root = root def tearDown(self): self.tmp.cleanup() def test_shell_executor_round(self): ex = make_executor(self.cfg, "oregon", "shell", self.t) self.assertFalse(ex.resume) run = Run("GW-1-1", str(self.root), "hello prompt", script="read line; echo \"[review] echo: $line\"") h = ex.start(run) self.assertTrue(wait(ex, h)) res = ex.collect(h) self.assertEqual((res.last_message, res.exit_code, res.thread_id), ("[review] echo: hello prompt", 0, None)) def test_claude_executor_round_and_resume(self): ex = make_executor(self.cfg, "oregon", "claude-code", self.t) h = ex.start(Run("GW-2-1", str(self.root), "abc", model="sonnet")) self.assertTrue(wait(ex, h)) res = ex.collect(h) self.assertEqual((res.last_message, res.thread_id, res.exit_code), ("[review] got 3", "sess-42", 0)) h2 = ex.start(Run("GW-2-2", str(self.root), "abcd", model="sonnet", thread_id="sess-42")) self.assertTrue(wait(ex, h2)) self.assertIn("--resume sess-42", self.t.read_text(os.path.join(h2["run_dir"], "stderr.txt"))) def test_kill_and_exit_code(self): ex = make_executor(self.cfg, "oregon", "shell", self.t) h = ex.start(Run("GW-3-1", str(self.root), "", script="sleep 30")) time.sleep(0.5) self.assertEqual(ex.poll(h), "running") ex.kill(h) time.sleep(0.3) self.assertEqual(ex.poll(h), "running") # killed runner never writes exit.txt; core handles it as timeout h = ex.start(Run("GW-3-2", str(self.root), "", script="echo partial; exit 3")) self.assertTrue(wait(ex, h)) res = ex.collect(h) self.assertEqual((res.exit_code, res.last_message), (3, "partial")) if __name__ == "__main__": unittest.main()