YCode / Code-agent-main /tests /test_cli.py
Yazhiniyan's picture
Upload 190 files
5ef3875 verified
Raw History Blame Contribute Delete
4.36 kB
import pytest
from ycode import __version__, cli
from ycode.llm.base import LLMProvider
from tests.conftest import FakeProvider, reply
def test_version(capsys):
with pytest.raises(SystemExit) as exc:
cli.main(["--version"])
assert exc.value.code == 0
assert __version__ in capsys.readouterr().out
def test_missing_api_key_exits_cleanly(workspace, capsys, monkeypatch, tmp_path):
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "xdg"))
code = cli.main(["-C", str(workspace), "--provider", "anthropic", "do something"])
out = capsys.readouterr().out
assert code == 1
assert "ANTHROPIC_API_KEY" in out
assert "Traceback" not in out
assert "AI Coding Agent" in out # the banner still shows
def test_default_needs_no_api_key(workspace, capsys, monkeypatch):
"""Out of the box, YCode answers with its own bundled model: no key, no training."""
import ycode.llm.local as local
from ycode.config import DEFAULT_BUNDLED_MODEL
seen = {}
class FakeLM:
num_params, version = 100_000_000, 4
def chat(self, turns, *, on_text=None, **_kw):
if on_text:
on_text("Use sorted(items).")
return "Use sorted(items)."
def fake_load(self, device):
seen["dir"] = self.model_dir
return FakeLM()
import ycode.config as config
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
monkeypatch.setattr(config, "available_memory_gb", lambda: 16.0)
monkeypatch.setattr(local.LocalProvider, "_load", fake_load)
code = cli.main(["-C", str(workspace), "--no-banner", "how do I sort a list?"])
assert code == 0
assert seen["dir"].name == DEFAULT_BUNDLED_MODEL
assert "sorted(items)" in capsys.readouterr().out
def test_bad_config_exits_cleanly(workspace, capsys, isolated_home):
(isolated_home / "config.toml").write_text("max_steps = -1\n")
assert cli.main(["-C", str(workspace), "x"]) == 2
assert "max_steps" in capsys.readouterr().out
def test_missing_directory(capsys, tmp_path):
assert cli.main(["-C", str(tmp_path / "nope"), "x"]) == 2
def test_one_shot_task(workspace, capsys, monkeypatch):
provider = FakeProvider([reply("", [("list_directory", {})]), reply("All good.")])
monkeypatch.setattr(cli, "create_provider", lambda config: provider)
code = cli.main(["-C", str(workspace), "--no-banner", "--max-steps", "7", "inspect", "the", "project"])
out = capsys.readouterr().out
assert code == 0
assert "All good." in out and "YCode completed the task." in out
assert provider.requests[0]["messages"][0].text == "inspect the project"
def test_cli_flags_reach_config(workspace, monkeypatch):
seen = {}
def fake_create(config):
seen["config"] = config
return FakeProvider([reply("ok")])
monkeypatch.setattr(cli, "create_provider", fake_create)
cli.main(["-C", str(workspace), "--no-banner", "--model", "claude-sonnet-5-5", "--yes", "-p", "hi"])
assert seen["config"].model == "claude-sonnet-5-5"
assert seen["config"].approval_mode == "auto"
def test_repl_handles_commands_tasks_and_exit(workspace, monkeypatch, capsys):
provider = FakeProvider([reply("Hello there.")])
monkeypatch.setattr(cli, "create_provider", lambda config: provider)
lines = iter(["/status", "say hello", "/exit"])
monkeypatch.setattr("builtins.input", lambda prompt="": next(lines))
assert cli.main(["-C", str(workspace), "--no-banner"]) == 0
out = capsys.readouterr().out
assert "Steps used" in out and "Hello there." in out and "Goodbye." in out
assert len(provider.requests) == 1
def test_repl_survives_internal_errors_and_eof(workspace, monkeypatch, capsys):
class Exploding(LLMProvider):
name = "boom"
def complete(self, **kwargs):
raise RuntimeError("unexpected")
monkeypatch.setattr(cli, "create_provider", lambda config: Exploding("m"))
lines = iter(["do it"])
def fake_input(prompt=""):
try:
return next(lines)
except StopIteration:
raise EOFError
monkeypatch.setattr("builtins.input", fake_input)
assert cli.main(["-C", str(workspace), "--no-banner"]) == 0
out = capsys.readouterr().out
assert "Internal error: RuntimeError: unexpected" in out
assert "Traceback" not in out