Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 2 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,5 @@ jobs:
- name: Ruff
run: python -m ruff check pyagent tests

- name: Smoke tests
run: |
set +e
fail=0
failed=""
for t in tests/test_*.py; do
name=$(basename "$t" .py)
echo "::group::$name"
python -m "tests.$name"
rc=$?
echo "::endgroup::"
if [ $rc -ne 0 ]; then
fail=$((fail+1))
failed="$failed $name"
fi
done
if [ $fail -gt 0 ]; then
echo "FAILED ($fail tests):$failed"
exit 1
fi
echo "All tests passed"
- name: Tests
run: python -m pytest tests/
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,20 @@ Local Ollama needs no key — just the daemon running and a model pulled.

## Tests

Smoke tests live under `tests/` as standalone scripts (no pytest):

```bash
pip install -e '.[dev]'
pre-commit install # one-time: run black + ruff on every commit
python -m tests.test_token_meter # run one
for f in tests/test_*.py; do python -m tests.$(basename "$f" .py); done
pre-commit install # one-time: run black + ruff on every commit
pytest tests/ # run everything
pytest tests/test_token_meter.py # run one file
```

Each `tests/test_*.py` also runs standalone via
`python -m tests.test_token_meter`, but pytest is the default
runner and what CI uses.

Three recall sub-tests in `test_plugins.py` are gated on
`PYAGENT_HEAVY_TESTS=1` because they download a ~130MB embedding
model. CI runs the same loop on every push and pull request.
model.

## License

Expand Down
7 changes: 6 additions & 1 deletion tests/test_agent_label.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the CLI's _agent_label rendering.
"""Test of the CLI's _agent_label rendering.

The label has to survive rich's markup parser and produce
`[<agent_id>] ` in cyan around the brackets. A previous version used
Expand Down Expand Up @@ -51,3 +51,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
2 changes: 1 addition & 1 deletion tests/test_arg_scrubbing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for tool-call arg scrubbing.
"""Test of tool-call arg scrubbing.

Large string args in tool_call dicts get replaced with a short
marker after the tool runs, so a write_file with 50KB of content
Expand Down
7 changes: 6 additions & 1 deletion tests/test_ask_parent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for mid-task subagent ↔ parent conversation (issue #47).
"""Test of mid-task subagent ↔ parent conversation (issue #47).

Drives both sides of `ask_parent` / `reply_to_subagent` against
real `_ChildState` IO threads without spawning subagent
Expand Down Expand Up @@ -325,3 +325,8 @@ def asker(question: str) -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_async_subagent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for async subagent dispatch.
"""Test of async subagent dispatch.

Covers:
1. `_drain_pending_async()` — appends queued replies as user-role
Expand Down Expand Up @@ -183,3 +183,8 @@ def runner():

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_attachment_lru.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the per-session attachments LRU cap (issue #86).
"""Test of the per-session attachments LRU cap (issue #86).

Locks these behaviors:

Expand Down Expand Up @@ -287,3 +287,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_auto_venv.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for venv discovery / creation + the `python_env` tool.
"""Test of venv discovery / creation + the `python_env` tool.

Locks:
1. `discover` finds nothing in an empty workspace.
Expand Down Expand Up @@ -242,3 +242,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_background_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_bench_defaults.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for `pyagent-bench`'s default-budget-by-model table.
"""Test of `pyagent-bench`'s default-budget-by-model table.

Locks the per-model defaults so a future model rename / pricing-table
update doesn't silently make Opus runs halt at the Sonnet budget (or
Expand Down Expand Up @@ -53,3 +53,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
2 changes: 1 addition & 1 deletion tests/test_call_tool.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke tests for `PluginAPI.call_tool` — cross-plugin composition.
"""Tests for `PluginAPI.call_tool` — cross-plugin composition.

Covers:
- Plugin B's tool calls plugin A's tool through `api.call_tool`.
Expand Down
7 changes: 6 additions & 1 deletion tests/test_checklist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the agent-managed checklist (issue #38).
"""Test of the agent-managed checklist (issue #38).

Locks the contract for `pyagent.checklist.Checklist`, the three
tool factories that bind to it, the `checklist` event flowing into
Expand Down Expand Up @@ -307,3 +307,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
2 changes: 1 addition & 1 deletion tests/test_claude_code_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke tests for the claude-code-cli plugin's early-return error paths.
"""Tests for the claude-code-cli plugin's early-return error paths.

All cases exercised here return before subprocess is invoked, so the
tests don't require the `claude` binary to be present on PATH. The
Expand Down
5 changes: 5 additions & 0 deletions tests/test_cli_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_code_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,3 +1069,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_context_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
2 changes: 1 addition & 1 deletion tests/test_controlling_hooks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke tests for v2 controlling hooks (issue #66).
"""Tests for v2 controlling hooks (issue #66).

Covers every acceptance-criteria bullet:

Expand Down
7 changes: 6 additions & 1 deletion tests/test_ctrlc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for clean Ctrl+C behavior.
"""Test of clean Ctrl+C behavior.

Subprocess-launches the full `pyagent` CLI in its own process group
(simulating an interactive terminal), waits for the agent to become
Expand Down Expand Up @@ -93,3 +93,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_doc_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,3 +710,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_edit_file.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for edit_file.
"""Test of edit_file.

Exercises (in-process):
1. Single-match replace, with correct 1-indexed line number in
Expand Down Expand Up @@ -134,3 +134,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_glob.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for glob.
"""Test of glob.

Exercises (in-process):
1. Single-pattern recursive match returns sorted relative paths.
Expand Down Expand Up @@ -150,3 +150,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_grep.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for grep.
"""Test of grep.

Exercises (in-process):
1. Default behavior: bare pattern/path returns `path:lineno:line`,
Expand Down Expand Up @@ -217,3 +217,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_hn_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_html_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_kill_active.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,8 @@ def runner() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_library_usage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the pyagent top-level library surface.
"""Test of the pyagent top-level library surface.

Locks the public re-exports + auto_client behavior. These are the
names the README/library-usage.md teach; if they go missing, the
Expand Down Expand Up @@ -158,3 +158,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
5 changes: 5 additions & 0 deletions tests/test_list_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_memory_category_drift.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the Tier-2 memory category-drift improvements.
"""Test of the Tier-2 memory category-drift improvements.

Locks four behaviors:

Expand Down Expand Up @@ -256,3 +256,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_memory_recall_improvements.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the Tier-1 memory recall improvements.
"""Test of the Tier-1 memory recall improvements.

Locks three behaviors:

Expand Down Expand Up @@ -247,3 +247,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_notify.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the subagent ↔ parent notification protocol (issue #64).
"""Test of the subagent ↔ parent notification protocol (issue #64).

Drives both directions of the new `subagent_note` / `parent_note`
pipe events against real `_ChildState` IO threads without spawning
Expand Down Expand Up @@ -341,3 +341,8 @@ def main() -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
7 changes: 6 additions & 1 deletion tests/test_notify_surface.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Smoke for the parent-side notification surface (issue #65).
"""Test of the parent-side notification surface (issue #65).

Drives `tell_subagent` and `peek_subagent` against real
`_ChildState` IO threads with fake subagent pipes — the same
Expand Down Expand Up @@ -496,3 +496,8 @@ def _capture_unread(count: int, by_sev: dict[str, int]) -> None:

if __name__ == "__main__":
main()


def test_main() -> None:
"""Entry point for pytest; runs the standalone main()."""
main()
Loading
Loading