None:
out = grep(r"return 42", fp, before="2") # type: ignore[arg-type]
assert out == [
f"{fp}:3-def helper():",
- f"{fp}:4- \"\"\"Compute the answer.\"\"\"",
+ f'{fp}:4- """Compute the answer."""',
f"{fp}:5: return 42",
], out
- print(f"[ok] string coerces to int")
+ print("[ok] string coerces to int")
print("\nALL CHECKS PASSED")
diff --git a/tests/smoke_hn_search.py b/tests/test_hn_search.py
similarity index 93%
rename from tests/smoke_hn_search.py
rename to tests/test_hn_search.py
index 560d08a..4029bf1 100644
--- a/tests/smoke_hn_search.py
+++ b/tests/test_hn_search.py
@@ -1,4 +1,4 @@
-"""End-to-end smoke for the hn-search plugin.
+"""End-to-end test for the hn-search plugin.
Concerns covered:
@@ -28,7 +28,7 @@
`save_structured` config; silent on clean config.
Run with:
- .venv/bin/python -m tests.smoke_hn_search
+ .venv/bin/python -m tests.test_hn_search
"""
from __future__ import annotations
@@ -62,7 +62,7 @@ class _FakeAPI:
def plugin_config(self):
return captured["plugin_config"]
- def register_tool(self, name, fn):
+ def register_tool(self, name, fn, *, role_only=False):
captured["tools"][name] = fn
def log(self, level, message):
@@ -104,15 +104,15 @@ def log(self, level, message):
def _check_plugin_loads_under_default_config() -> None:
- tmp = Path(tempfile.mkdtemp(prefix="pyagent-smoke-hn-"))
+ tmp = Path(tempfile.mkdtemp(prefix="pyagent-test-hn-"))
with mock.patch.object(paths_mod, "config_dir", return_value=tmp):
with mock.patch.object(
plugins, "LOCAL_PLUGINS_DIR", Path(tmp / "no_local_plugins")
):
cfg = config_mod.load()
- assert "hn-search" in cfg["built_in_plugins_enabled"], (
- cfg["built_in_plugins_enabled"]
- )
+ assert "hn-search" in cfg["built_in_plugins_enabled"], cfg[
+ "built_in_plugins_enabled"
+ ]
loaded = plugins.load()
tool_names = set(loaded.tools().keys())
assert "hn_search" in tool_names, tool_names
@@ -160,7 +160,9 @@ def _check_parse_hits_tolerates_weird_payloads() -> None:
}
stories = _parse_hits(payload)
assert len(stories) == 1, stories
- assert stories[0].permalink == "https://news.ycombinator.com/item?id=777", stories[0]
+ assert stories[0].permalink == "https://news.ycombinator.com/item?id=777", stories[
+ 0
+ ]
assert stories[0].points == 0, stories[0]
assert stories[0].num_comments == 0, stories[0]
assert stories[0].type == "comment", stories[0]
@@ -170,7 +172,7 @@ def _check_parse_hits_tolerates_weird_payloads() -> None:
def _check_parse_hits_type_tag_precedence() -> None:
"""Lock the chosen precedence on `_tags = [story, ask_hn, ...]`.
The first non-author/non-id tag wins — currently `story` over
- `ask_hn`. If that flips, the smoke fails loudly and the reviewer
+ `ask_hn`. If that flips, the test fails loudly and the reviewer
can decide whether the new precedence is intentional."""
payload = {
"hits": [
@@ -204,7 +206,11 @@ def _check_parse_hits_type_tag_precedence() -> None:
def _check_url_builder() -> None:
url = _build_url(
- "postgres sqlite", n=10, kind="story", time_window="all", min_points=None,
+ "postgres sqlite",
+ n=10,
+ kind="story",
+ time_window="all",
+ min_points=None,
)
assert url.startswith("https://hn.algolia.com/api/v1/search?"), url
assert "query=postgres+sqlite" in url or "query=postgres%20sqlite" in url, url
@@ -243,8 +249,14 @@ def _check_time_window_filter_helper() -> None:
assert hn_mod._time_window_filter("hour") == f"created_at_i>{fake_now - 3600}"
assert hn_mod._time_window_filter("day") == f"created_at_i>{fake_now - 86400}"
assert hn_mod._time_window_filter("week") == f"created_at_i>{fake_now - 604800}"
- assert hn_mod._time_window_filter("month") == f"created_at_i>{fake_now - 2_592_000}"
- assert hn_mod._time_window_filter("year") == f"created_at_i>{fake_now - 31_536_000}"
+ assert (
+ hn_mod._time_window_filter("month")
+ == f"created_at_i>{fake_now - 2_592_000}"
+ )
+ assert (
+ hn_mod._time_window_filter("year")
+ == f"created_at_i>{fake_now - 31_536_000}"
+ )
# Unknown window → empty (defensive; the tool layer validates first)
assert hn_mod._time_window_filter("century") == ""
print("✓ time_window: produces literal numeric epochs Algolia accepts")
@@ -257,9 +269,7 @@ def _check_tool_returns_attachment() -> None:
hn_search = cap["tools"]["hn_search"]
fixture_stories = _parse_hits(_FIXTURE_HITS_PAYLOAD)
- with mock.patch.object(
- hn_mod, "hn_text_search", return_value=fixture_stories
- ) as m:
+ with mock.patch.object(hn_mod, "hn_text_search", return_value=fixture_stories) as m:
out = hn_search("postgres sqlite", n=2, kind="story")
args, kwargs = m.call_args
@@ -369,10 +379,12 @@ def _check_register_warnings() -> None:
assert any("save_structured must be a bool" in m for m in msgs), msgs
# Clean config: silent.
- cap = _make_fake_api(plugin_config={
- "timeout_s": 15,
- "save_structured": True,
- })
+ cap = _make_fake_api(
+ plugin_config={
+ "timeout_s": 15,
+ "save_structured": True,
+ }
+ )
msgs = [m for level, m in cap["logs"] if level == "warning"]
assert msgs == [], msgs
print("✓ register-time warnings: bogus configs flagged, clean config silent")
@@ -392,7 +404,7 @@ def main() -> None:
_check_validation_paths()
_check_http_failures_translate()
_check_register_warnings()
- print("smoke_hn_search: all checks passed")
+ print("test_hn_search: all checks passed")
if __name__ == "__main__":
diff --git a/tests/smoke_html_tools.py b/tests/test_html_tools.py
similarity index 86%
rename from tests/smoke_html_tools.py
rename to tests/test_html_tools.py
index 737dc19..8c4e93c 100644
--- a/tests/smoke_html_tools.py
+++ b/tests/test_html_tools.py
@@ -1,4 +1,4 @@
-"""End-to-end smoke for the html-tools plugin and the restructured
+"""End-to-end test for the html-tools plugin and the restructured
fetch_url.
Three concerns:
@@ -21,7 +21,7 @@
Run with:
- .venv/bin/python -m tests.smoke_html_tools
+ .venv/bin/python -m tests.test_html_tools
"""
from __future__ import annotations
@@ -34,7 +34,6 @@
from pyagent.plugins.html_tools import extraction
from pyagent.session import Attachment
-
_NEWS_HTML = """
headline
@@ -74,7 +73,7 @@ def _check_extraction_main_content() -> None:
assert "[link](https://example.com)" in md, md
# List survives as bullets.
assert "- one" in md or "* one" in md, md
- print(f"✓ extraction.main_content drops boilerplate, keeps structure")
+ print("✓ extraction.main_content drops boilerplate, keeps structure")
def _check_extraction_full_document() -> None:
@@ -82,7 +81,7 @@ def _check_extraction_full_document() -> None:
# With main_content=False, boilerplate stays.
assert "home / about / contact" in md or "home" in md, md
assert "The Real Story" in md, md
- print(f"✓ extraction.main_content=False preserves the whole document")
+ print("✓ extraction.main_content=False preserves the whole document")
def _check_extraction_select_table() -> None:
@@ -100,9 +99,7 @@ def _check_extraction_select_table() -> None:
def _check_extraction_select_limit() -> None:
- md, total, returned = extraction.html_select_to_markdown(
- _TABLE_HTML, "tr", limit=2
- )
+ md, total, returned = extraction.html_select_to_markdown(_TABLE_HTML, "tr", limit=2)
assert total == 3, total
assert returned == 2, returned
print(f"✓ extraction.select honors limit (matched {total}, kept {returned})")
@@ -111,7 +108,7 @@ def _check_extraction_select_limit() -> None:
def _check_plugin_loads_under_default_config() -> None:
"""With the default config, html-tools is in built_in_plugins_enabled
and load() exposes both tools."""
- tmp = Path(tempfile.mkdtemp(prefix="pyagent-smoke-htmltools-"))
+ tmp = Path(tempfile.mkdtemp(prefix="pyagent-test-htmltools-"))
# Point config_dir at an empty temp dir so user/project config can't
# mask the bundled defaults (e.g. an existing user config that hasn't
# added "html-tools" yet).
@@ -120,9 +117,9 @@ def _check_plugin_loads_under_default_config() -> None:
plugins, "LOCAL_PLUGINS_DIR", Path(tmp / "no_local_plugins")
):
cfg = config_mod.load()
- assert "html-tools" in cfg["built_in_plugins_enabled"], (
- cfg["built_in_plugins_enabled"]
- )
+ assert "html-tools" in cfg["built_in_plugins_enabled"], cfg[
+ "built_in_plugins_enabled"
+ ]
loaded = plugins.load()
tool_names = set(loaded.tools().keys())
assert "html_select" in tool_names, tool_names
@@ -161,7 +158,7 @@ def _check_fetch_url_md_default() -> None:
assert "[link](https://example.com)" in result.preview, result.preview
# Boilerplate must not survive the main-content extraction.
assert "home / about / contact" not in result.preview, result.preview
- print(f"✓ fetch_url(format='md') saves raw + inlines markdown")
+ print("✓ fetch_url(format='md') saves raw + inlines markdown")
def _check_fetch_url_void() -> None:
@@ -169,16 +166,14 @@ def _check_fetch_url_void() -> None:
with mock.patch.object(
tools.requests, "get", return_value=_FakeResponse(_NEWS_HTML)
):
- result = tools.fetch_url(
- "https://example.com/x", format="void"
- )
+ result = tools.fetch_url("https://example.com/x", format="void")
assert isinstance(result, Attachment), type(result)
assert result.content == _NEWS_HTML, "raw still saved"
- assert "format=\"void\"" in result.preview, result.preview
+ assert 'format="void"' in result.preview, result.preview
# Markdown body must NOT appear in preview.
assert "The Real Story" not in result.preview, result.preview
assert "[link]" not in result.preview, result.preview
- print(f"✓ fetch_url(format='void') saves raw, omits markdown body")
+ print("✓ fetch_url(format='void') saves raw, omits markdown body")
def _check_fetch_url_non_html() -> None:
@@ -187,9 +182,7 @@ def _check_fetch_url_non_html() -> None:
with mock.patch.object(
tools.requests,
"get",
- return_value=_FakeResponse(
- body, content_type="application/json"
- ),
+ return_value=_FakeResponse(body, content_type="application/json"),
):
result = tools.fetch_url("https://api.example.com/x")
assert isinstance(result, Attachment), type(result)
@@ -197,7 +190,7 @@ def _check_fetch_url_non_html() -> None:
assert result.suffix == ".json", result.suffix
assert "application/json" in result.preview, result.preview
assert "Non-HTML" in result.preview, result.preview
- print(f"✓ fetch_url skips conversion for non-HTML responses")
+ print("✓ fetch_url skips conversion for non-HTML responses")
def _check_fetch_url_large_md_truncates() -> None:
@@ -208,16 +201,14 @@ def _check_fetch_url_large_md_truncates() -> None:
+ "".join(f"line {i} " + "x " * 200 + "
" for i in range(60))
+ "