Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154"""`bin/hook.py`, run the way Claude Code runs it: a payload on stdin, andwhatever it prints read back as the hook's output.
Each test gets a machine of its own: a `PATH` holding only the commands itsays are installed, and a didbot daemon socket or none.
Run with `python3 -m unittest discover -s plugin/tests`."""
import jsonimport osimport socketimport subprocessimport sysimport tempfileimport unittest
HOOK = os.path.join(os.path.dirname(__file__), os.pardir, "bin", "hook.py")
PAYLOAD = {"hook_event_name": "SessionStart", "session_id": "s1", "source": "startup"}
class Machine: """One machine's worth of what the hook looks at."""
def __init__(self, root, atgc=True, didbot=True, daemon=True): self.bin = os.path.join(root, "bin") self.runtime = os.path.join(root, "run") os.makedirs(self.bin) os.makedirs(os.path.join(self.runtime, "didbot-agent")) if atgc: self.install("atgc") if didbot: self.install("didbot") self.listener = None if daemon: self.listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.listener.bind(os.path.join(self.runtime, "didbot-agent", "agent.sock"))
def install(self, name): path = os.path.join(self.bin, name) with open(path, "w") as handle: handle.write("#!/bin/sh\nexit 0\n") os.chmod(path, 0o755)
def close(self): if self.listener: self.listener.close()
def run(self, stdin=None): """The hook's `additionalContext`, or `None` when it prints nothing.""" env = {"PATH": self.bin, "XDG_RUNTIME_DIR": self.runtime} done = subprocess.run( [sys.executable, HOOK], input=json.dumps(PAYLOAD) if stdin is None else stdin, capture_output=True, text=True, env=env, timeout=10, ) assert done.returncode == 0, done.stderr assert done.stderr == "", done.stderr if not done.stdout: return None output = json.loads(done.stdout)["hookSpecificOutput"] assert output["hookEventName"] == "SessionStart", output return output["additionalContext"]
class HookTest(unittest.TestCase): def machine(self, **kwargs): root = tempfile.TemporaryDirectory() self.addCleanup(root.cleanup) machine = Machine(root.name, **kwargs) self.addCleanup(machine.close) return machine
class WithoutAtgc(HookTest): def test_prints_nothing(self): self.assertIsNone(self.machine(atgc=False).run())
def test_prints_nothing_with_didbot_either(self): self.assertIsNone(self.machine(atgc=False, didbot=True, daemon=True).run())
class WithDidbot(HookTest): def setUp(self): self.text = self.machine().run()
def test_says_what_tangled_is(self): self.assertIn("Tangled (tangled.org) is a git forge", self.text)
def test_says_how_to_sign_in_as_the_session(self): self.assertIn(" atgc auth login <your DID>\n", self.text) self.assertIn("in the background", self.text) self.assertIn("`didbot oauth approve`", self.text)
def test_names_no_did(self): # The DID comes from didbot's hook; this one cannot know it. self.assertNotIn("did:", self.text)
def test_says_to_name_the_account_on_every_command(self): self.assertIn("`--account <your DID>`", self.text) self.assertIn("`ATGC_ACCOUNT=<your DID>`", self.text) self.assertIn("linked worktree", self.text)
def test_ends_with_the_briefing(self): self.assertTrue(self.text.endswith("Run `atgc agent` for everything else."))
class WithoutDidbot(HookTest): """A session somebody runs without didbot: no agent identity to sign in as."""
def check(self, text): self.assertIn("Tangled (tangled.org) is a git forge", text) self.assertNotIn("didbot", text) self.assertNotIn("<your DID>", text) self.assertNotIn("auth login", text) self.assertIn("`--account <handle|DID>`", text) self.assertIn("`ATGC_ACCOUNT=<handle|DID>`", text) self.assertIn("`atgc pr create --patch-only`", text) self.assertTrue(text.endswith("Run `atgc agent` for everything else."))
def test_no_didbot_command(self): self.check(self.machine(didbot=False, daemon=False).run())
def test_didbot_without_its_daemon(self): self.check(self.machine(didbot=True, daemon=False).run())
def test_daemon_without_the_didbot_command(self): self.check(self.machine(didbot=False, daemon=True).run())
class NeverInstalls(HookTest): """A hook's output is an instruction; it never tells the agent to install."""
def test_no_install_or_key_setup_in_any_text(self): for kwargs in ({}, {"didbot": False, "daemon": False}): text = self.machine(**kwargs).run() for word in ("install", "cargo", "curl", "key add", "ssh-keygen"): self.assertNotIn(word, text.lower(), kwargs)
class Payload(HookTest): def test_unreadable_stdin_still_prints(self): # The text depends on the machine, not the payload. machine = self.machine() self.assertEqual(machine.run(stdin="not json"), machine.run())
if __name__ == "__main__": unittest.main()