utils/leetcode_coach/leetcode_coach/tests/test_claude_cli.py

75 lines
2.5 KiB
Python

"""Tests for the one `claude -p` call shared by the REPL and the checker."""
from __future__ import annotations
import json
import shutil
import subprocess
from typing import TYPE_CHECKING
from leetcode_coach import claude_cli
if TYPE_CHECKING:
from pathlib import Path
import pytest
def _stub(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, body: str) -> Path:
stub = tmp_path / "claude"
stub.write_text(body, encoding="utf-8")
stub.chmod(0o755)
monkeypatch.setenv("LEETCODE_CLAUDE_BIN", str(stub))
return stub
def test_ask_parses_a_failing_claude_stub(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
body = json.dumps({"is_error": True, "result": "You've hit your session limit"})
(tmp_path / "out.json").write_text(body, encoding="utf-8")
stub = _stub(
tmp_path,
monkeypatch,
f"#!/bin/sh\ncat {tmp_path / 'out.json'}\necho boom >&2\nexit 1\n",
)
reply = claude_cli.ask([], "hi")
assert claude_cli.is_limit_hit(reply)
assert reply["_stderr"] == "boom"
stub.write_text("#!/bin/sh\necho not-json\nexit 3\n", encoding="utf-8")
assert claude_cli.ask([], "hi")["result"].startswith("[claude exited 3]")
stub.write_text("#!/bin/sh\necho '[1]'\n", encoding="utf-8")
assert claude_cli.ask([], "hi") == {"_stderr": ""}
def test_ask_passes_args_then_the_prompt(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_stub(tmp_path, monkeypatch, '#!/bin/sh\nprintf \'{"result": "%s"}\' "$*"\n')
got = claude_cli.ask(["--model", "haiku"], "go")["result"]
assert got == "-p --model haiku --output-format json go"
def test_a_missing_binary_is_an_error_reply_not_a_crash() -> None:
reply = claude_cli.ask([], "hi")
assert reply["result"].startswith("[claude could not run")
def test_a_hung_claude_times_out(monkeypatch: pytest.MonkeyPatch) -> None:
def hang(*_a: object, **_kw: object) -> subprocess.CompletedProcess[str]:
msg = "claude"
raise subprocess.TimeoutExpired(msg, 1.0)
monkeypatch.setattr(subprocess, "run", hang)
assert claude_cli.ask([], "hi", timeout=1.0)["result"].startswith(
"[claude could not run"
)
def test_the_binary_is_looked_up_per_call(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LEETCODE_CLAUDE_BIN", "")
monkeypatch.setattr(shutil, "which", lambda _n: "/opt/claude")
assert claude_cli.claude_bin() == "/opt/claude"
monkeypatch.setattr(shutil, "which", lambda _n: None)
assert claude_cli.claude_bin() == "claude"