tiny-computer-agent / tests /test_cli.py
hkblockchaintechnology's picture
2.0: skills, approval gates, a resumable task runner, structural verification
e2f048d verified
Raw History Blame
6.1 kB
import json
import pytest
from tiny_computer_agent import __main__ as cli
def test_demo_runs_dry_run_by_default(capsys):
assert cli.main([]) == 0
out = capsys.readouterr().out
assert "dry-run" in out
assert "(dry-run) would click" in out
assert "result: success" in out
def test_real_input_needs_the_allow_env_var(monkeypatch, capsys):
monkeypatch.delenv(cli.ALLOW_REAL_INPUT_ENV, raising=False)
assert cli.main(["--real-input"]) == 2
assert "refusing --real-input" in capsys.readouterr().err
def test_bad_script_json_is_reported(capsys):
assert cli.main(["--script", "{not json"]) == 2
assert "not valid JSON" in capsys.readouterr().err
def test_script_replay(capsys):
assert cli.main(["--script", '[{"kind": "done", "reason": "nothing to do"}]']) == 0
assert "nothing to do" in capsys.readouterr().out
def test_failure_exit_code(capsys):
# the planner gives up when the target never appears, so the CLI exits non-zero
assert cli.main(["click 'Nonexistent button'"]) == 1
assert "gave up" in capsys.readouterr().out
def test_doctor_exits_with_a_status(capsys):
assert cli.main(["--doctor"]) in (0, 1)
assert "preflight" in capsys.readouterr().out
def test_live_is_refused_without_a_display(capsys, monkeypatch):
from tiny_computer_agent.desktop import display_available
if display_available()[0]:
pytest.skip("this machine has a display, so --live would really run")
assert cli.main(["--live"]) == 2
captured = capsys.readouterr()
assert "refusing to run live" in captured.err
assert "no DISPLAY" in captured.out
def test_screenshot_replay_needs_no_display(capsys):
"""The whole pipeline - OCR index, menu, action - on a still image."""
from conftest import require_ocr
require_ocr() # one tested guard, shared with the perception tests
assert cli.main(["--image", "example", "--brain", "planner"]) == 0
out = capsys.readouterr().out
assert "screenshot replay" in out
assert "elements detected" in out
assert "would click" in out
def test_a_missing_image_is_reported_not_crashed(capsys):
assert cli.main(["--image", "/nonexistent/shot.png"]) == 2
assert "could not start" in capsys.readouterr().err
def test_vlm_brain_requires_a_live_screen(capsys):
assert cli.main(["--brain", "vlm"]) == 2
assert "needs --live" in capsys.readouterr().err
# --- 2.0: the subcommands, which are how a run becomes a habit --------------- #
def test_skills_list_on_an_empty_library_says_how_to_start(tmp_path, capsys):
assert cli.main(["skills", "list", "--path", str(tmp_path / "none.json")]) == 0
assert "no skills" in capsys.readouterr().out
def test_learning_refuses_without_evidence(tmp_path, capsys):
code = cli.main(["skills", "learn", "click 'Save'",
"--script", '[{"kind": "click", "label": "Save"}]',
"--path", str(tmp_path / "skills.json")])
assert code == 2
assert "refusing to learn" in capsys.readouterr().err
def test_learn_then_list_then_show_then_remove(tmp_path, capsys):
path = str(tmp_path / "skills.json")
script = '[{"kind": "click", "label": "Files"}, {"kind": "click", "label": "Save"}]'
assert cli.main(["skills", "learn", "click 'Save' to save the document",
"--script", script, "--verify-gone", "Name", "--path", path]) == 0
assert "learned 'save-document'" in capsys.readouterr().out
assert cli.main(["skills", "list", "--path", path]) == 0
out = capsys.readouterr().out
assert "save-document" in out and "verified" in out
assert cli.main(["skills", "show", "save-document", "--path", path]) == 0
assert "'Name' goes away" in capsys.readouterr().out
assert cli.main(["skills", "remove", "save-document", "--path", path]) == 0
assert "removed" in capsys.readouterr().out
assert cli.main(["skills", "show", "save-document", "--path", path]) == 1
def test_a_learned_skill_can_be_replayed_by_the_brain(tmp_path):
"""The point of the whole feature: the second run needs no script."""
path = tmp_path / "skills.json"
script = '[{"kind": "click", "label": "Files"}, {"kind": "click", "label": "Save"}]'
assert cli.main(["skills", "learn", "click 'Save' to save the document",
"--script", script, "--verify-gone", "Name", "--path", str(path)]) == 0
from tiny_computer_agent import SkillBrain, SkillLibrary
from tiny_computer_agent.__main__ import demo_screen
from tiny_computer_agent.loop import AgentLoop
library = SkillLibrary.load(path)
loop = AgentLoop(screen=demo_screen(), brain=SkillBrain(library))
episode = loop.run("click 'Save' to save the document")
assert [s.action.label for s in episode.steps if s.action and s.action.kind == "click"] \
== ["Files", "Save"]
def test_batch_runs_a_queue_and_exits_on_its_report(tmp_path, capsys):
tasks = tmp_path / "tasks.json"
tasks.write_text(json.dumps([{"goal": "click 'Save' to save the document",
"verify_gone": "Name"},
{"goal": "click 'Files' to open the file manager",
"verify_text": "untitled"}]))
state = tmp_path / "state.jsonl"
report = tmp_path / "report.json"
assert cli.main(["batch", str(tasks), "--state", str(state), "--report", str(report)]) == 0
out = capsys.readouterr().out
assert "2/2 verified" in out
assert json.loads(report.read_text())["passed"] == 2
assert cli.main(["batch", str(tasks), "--state", str(state)]) == 0
assert "already run" in capsys.readouterr().out
def test_batch_rejects_tasks_that_are_not_tasks(tmp_path, capsys):
bad = tmp_path / "bad.json"
bad.write_text(json.dumps([{"nothing": "here"}]))
assert cli.main(["batch", str(bad)]) == 2
assert "needs at least a 'goal'" in capsys.readouterr().err
missing = tmp_path / "absent.json"
assert cli.main(["batch", str(missing)]) == 2
assert "cannot read" in capsys.readouterr().err