mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-21 20:22:22 +02:00
175 lines
6.6 KiB
Python
175 lines
6.6 KiB
Python
"""What the run made, what comes home, and what is refused out loud instead of silently.
|
|
|
|
The caps are the point. A truncated file is worse than a refused one, and a file that
|
|
vanishes with no sentence attached is the failure the whole list exists to prevent.
|
|
"""
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from runner.results.deliverables import (
|
|
MAX_FILE_BYTES,
|
|
MAX_FILES,
|
|
MAX_TOTAL_BYTES,
|
|
collect,
|
|
human_bytes,
|
|
)
|
|
from runner.results.report import deliver_files
|
|
from runner.run_spec import CallbackTarget
|
|
|
|
|
|
def write(root, relative: str, payload: bytes) -> str:
|
|
path = os.path.join(str(root), relative)
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "wb") as handle:
|
|
handle.write(payload)
|
|
return path
|
|
|
|
|
|
def test_a_missing_workspace_is_an_empty_harvest_not_a_crash(tmp_path) -> None:
|
|
assert collect(str(tmp_path / "never-made")).files == []
|
|
|
|
|
|
def test_ordinary_files_are_collected_with_their_digest(tmp_path) -> None:
|
|
write(tmp_path, "report.md", b"# Digest\n")
|
|
write(tmp_path, "data/rows.csv", b"a,b\n1,2\n")
|
|
|
|
# Walk order, and it is fixed: this directory's own files first, then subdirectories in name
|
|
# order, so a run's file list does not shuffle between two identical runs.
|
|
harvest = collect(str(tmp_path))
|
|
assert [f.path for f in harvest.files] == ["report.md", "data/rows.csv"]
|
|
assert harvest.files[0].size_bytes == len(b"# Digest\n")
|
|
# A digest travels with every file, so a corrupted upload is detectable rather than assumed fine.
|
|
assert len(harvest.files[0].sha256) == 64
|
|
assert harvest.refused == []
|
|
|
|
|
|
def test_machinery_is_not_a_deliverable(tmp_path) -> None:
|
|
write(tmp_path, "report.md", b"keep me")
|
|
write(tmp_path, ".git/config", b"[core]")
|
|
write(tmp_path, "node_modules/left-pad/index.js", b"module.exports=1")
|
|
write(tmp_path, "__pycache__/x.pyc", b"\x00")
|
|
write(tmp_path, ".claude/worktrees/probe/README", b"scratch")
|
|
|
|
assert [f.path for f in collect(str(tmp_path)).files] == ["report.md"]
|
|
|
|
|
|
def test_an_empty_file_is_neither_delivered_nor_complained_about(tmp_path) -> None:
|
|
write(tmp_path, "touched.txt", b"")
|
|
harvest = collect(str(tmp_path))
|
|
assert harvest.files == []
|
|
assert harvest.refused == []
|
|
|
|
|
|
def test_a_file_over_the_cap_is_refused_whole_and_says_why(tmp_path) -> None:
|
|
write(tmp_path, "render.mp4", b"x" * (MAX_FILE_BYTES + 1))
|
|
write(tmp_path, "notes.md", b"still fine")
|
|
|
|
harvest = collect(str(tmp_path))
|
|
assert [f.path for f in harvest.files] == ["notes.md"]
|
|
assert len(harvest.refused) == 1
|
|
assert harvest.refused[0].path == "render.mp4"
|
|
assert "cannot exceed" in harvest.refused[0].reason
|
|
# Never a fragment: an over-sized file is absent, not shortened.
|
|
assert all(f.path != "render.mp4" for f in harvest.files)
|
|
|
|
|
|
def test_the_run_total_stops_collecting_but_keeps_what_already_fit(tmp_path) -> None:
|
|
chunk = b"x" * MAX_FILE_BYTES
|
|
for index in range(MAX_TOTAL_BYTES // MAX_FILE_BYTES + 1):
|
|
write(tmp_path, f"blob-{index}.bin", chunk)
|
|
|
|
harvest = collect(str(tmp_path))
|
|
assert harvest.total_bytes() <= MAX_TOTAL_BYTES
|
|
assert len(harvest.files) >= 1
|
|
assert harvest.refused, "the file that blew the budget must be named, not dropped"
|
|
assert "limit is" in harvest.refused[0].reason
|
|
|
|
|
|
def test_too_many_files_refuses_the_extras_by_name(tmp_path) -> None:
|
|
for index in range(MAX_FILES + 3):
|
|
write(tmp_path, f"note-{index:03d}.txt", b"hi")
|
|
|
|
harvest = collect(str(tmp_path))
|
|
assert len(harvest.files) == MAX_FILES
|
|
assert len(harvest.refused) == 3
|
|
assert all("maximum of" in item.reason for item in harvest.refused)
|
|
|
|
|
|
def test_a_symlink_out_of_the_workspace_is_never_followed(tmp_path) -> None:
|
|
secret = tmp_path / "outside" / "id_rsa"
|
|
os.makedirs(secret.parent, exist_ok=True)
|
|
secret.write_text("PRIVATE KEY")
|
|
workspace = tmp_path / "ws"
|
|
os.makedirs(workspace, exist_ok=True)
|
|
os.symlink(str(secret), str(workspace / "borrowed.pem"))
|
|
|
|
assert collect(str(workspace)).files == []
|
|
|
|
|
|
def test_with_nowhere_to_send_files_the_run_says_so_per_file(tmp_path) -> None:
|
|
write(tmp_path, "report.md", b"the answer")
|
|
reported = deliver_files(None, str(tmp_path), collect(str(tmp_path)))
|
|
|
|
assert len(reported) == 1
|
|
assert reported[0].delivered is False
|
|
assert "nowhere to send files" in (reported[0].reason or "")
|
|
|
|
|
|
def test_a_control_plane_with_no_file_route_is_reported_not_guessed(tmp_path) -> None:
|
|
write(tmp_path, "report.md", b"the answer")
|
|
callback = CallbackTarget(url="https://cloud.test/report", token="two-party")
|
|
assert callback.artifacts_url is None
|
|
|
|
reported = deliver_files(callback, str(tmp_path), collect(str(tmp_path)))
|
|
assert reported[0].delivered is False
|
|
|
|
|
|
def test_refusals_reach_the_report_even_when_nothing_was_delivered(tmp_path) -> None:
|
|
write(tmp_path, "render.mp4", b"x" * (MAX_FILE_BYTES + 1))
|
|
reported = deliver_files(None, str(tmp_path), collect(str(tmp_path)))
|
|
|
|
assert len(reported) == 1
|
|
assert reported[0].path == "render.mp4"
|
|
assert reported[0].delivered is False
|
|
assert "cannot exceed" in (reported[0].reason or "")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"count,expected",
|
|
[(512, "512 B"), (2048, "2 KB"), (5 * 1024 * 1024, "5.0 MB"), (3 * 1024**3, "3.0 GB")],
|
|
)
|
|
def test_sizes_are_written_the_way_a_person_reads_them(count: int, expected: str) -> None:
|
|
assert human_bytes(count) == expected
|
|
|
|
|
|
def test_a_failed_run_still_hands_over_what_it_managed_to_make(tmp_path, monkeypatch) -> None:
|
|
"""A workflow that dies on step 3 may have written a perfectly good report on step 1."""
|
|
from runner import main
|
|
from runner.run_spec import RunSpec
|
|
|
|
write(tmp_path, "partial-report.md", b"# What I got through\n")
|
|
spec = RunSpec.model_validate({
|
|
"run_id": "cr-fail",
|
|
"workflow": {"id": "wf-1", "steps": [{"id": "s1", "text": "go"}]},
|
|
"credentials": [{"provider": "anthropic", "auth_type": "api_key", "api_key": "sk-test"}],
|
|
})
|
|
|
|
sent: list = []
|
|
monkeypatch.setattr(main, "send_report", lambda callback, report: sent.append(report) or True)
|
|
code = main.p_fail(spec, "failure", "step 3 blew up", main.EXIT_WORKFLOW_FAILED, str(tmp_path))
|
|
|
|
assert code == main.EXIT_WORKFLOW_FAILED
|
|
assert [f.path for f in sent[0].files] == ["partial-report.md"]
|
|
|
|
|
|
def test_a_failure_with_no_workspace_reports_no_files_rather_than_guessing(monkeypatch) -> None:
|
|
from runner import main
|
|
|
|
sent: list = []
|
|
monkeypatch.setattr(main, "send_report", lambda callback, report: sent.append(report) or True)
|
|
main.p_fail(None, "failure", "bad spec", main.EXIT_BAD_SPEC)
|
|
|
|
assert sent[0].files == []
|