import random from tiny_agent.chat import parse_assistant, render from tiny_agent.tasks import KINDS, check, grounded, make_task, oracle_trajectory from tiny_agent.tools import Workspace, truncate def test_sandbox_isolation(): with Workspace({"a.txt": "hello\n"}) as ws: assert "hello" in ws.tool_bash("cat a.txt") assert "nonet" in ws.tool_bash("curl -s -m 2 https://example.com >/dev/null && echo net || echo nonet") assert "No such file" in ws.tool_bash("ls /home") assert "Read-only" in ws.tool_bash("touch /usr/x") assert "timed out" in ws.tool_bash("sleep 5", timeout=1) def test_read_edit_rules(): with Workspace({"f.py": "x = 1\ny = 1\n"}) as ws: assert "read f.py before" in ws.tool_edit("f.py", "x = 1", "x = 2") out = ws.tool_read("f.py") assert " 1\tx = 1" in out assert "appears 2 times" in ws.tool_edit("f.py", "= 1", "= 3") assert "not found" in ws.tool_edit("f.py", "z = 1", "z = 2") assert "Edited" in ws.tool_edit("f.py", "x = 1", "x = 2") assert "outside /work" in ws.tool_read("../../etc/passwd") assert "unknown tool" in ws.call("browse", {}) assert "bad arguments" in ws.call("read", {"file": "f.py"}) def test_truncation_marker(): t = truncate("x\n" * 10000, limit=600) assert "truncated" in t and len(t) < 800 def test_parse_assistant(): m = parse_assistant('\nhmm\n\n\n{"name": "bash", "arguments": {"command": "ls"}}\n' '{bad json}<|im_end|>') assert m["think"] == "hmm" assert m["tool_calls"][0] == {"name": "bash", "arguments": {"command": "ls"}} assert "error" in m["tool_calls"][1] def test_render_roundtrip_assistant(): msg = {"role": "assistant", "think": "t", "content": "", "tool_calls": [{"name": "submit", "arguments": {"answer": "7"}}]} text = render([msg]).split("<|im_start|>assistant\n", 1)[1] back = parse_assistant(text) assert back["tool_calls"] == msg["tool_calls"] and back["think"] == "t" def test_oracle_solves_every_kind(): for i, kind in enumerate(KINDS * 3): t = make_task(random.Random(5000 + i), kind) msgs, ok = oracle_trajectory(t, random.Random(i)) assert ok, (kind, t.question) assert grounded(t, msgs) def test_wrong_answers_fail(): t = make_task(random.Random(1), "config_value") assert not check(t, "definitely wrong") assert not check(t, None) assert check(t, f" {t.answer}. ")