项目文件夹

文件
2026-07-13 13:12:00 +08:00

63 行
2.6 KiB
Python

"""LIVE drift canary for the keyless DuckDuckGo web_search backend.
The backend parses an uncontrolled third-party HTML page, so its real risk is
silent markup drift (DDG has already dropped the ``/l/?uddg=`` redirect wrapper
once). The offline tests in ``tests/tools/test_web_search_duckduckgo.py`` pin
behavior against a *captured* page; this canary is the only thing that notices
when the *live* page changes shape.
It is deliberately fenced off from normal CI two ways, so it can never red the
PR/push gate:
1. It lives under ``tests/e2e_live/``, which the default ``addopts`` ignores
(``--ignore=tests/e2e_live`` in pyproject.toml) — unit collection never sees
it.
2. It is marked ``@pytest.mark.nightly`` — even when the dir is named
explicitly, PR/push runs pass ``-m "not nightly"`` and skip it; only the
scheduled / ``workflow_dispatch`` pass runs it.
Run it on demand::
uv run pytest tests/e2e_live/ -m nightly
A FAIL here means DDG drifted: re-capture the golden fixture with
``tests/tools/fixtures/refresh_ddg_fixture.py`` and fix the parser in
``omnigent/tools/builtins/web_search_duckduckgo.py``. Transient throttling
(429 / 5xx / timeout) SKIPS — it is noise, not drift.
"""
from __future__ import annotations
import pytest
from omnigent.tools.builtins.web_search_duckduckgo import _search_duckduckgo
@pytest.mark.nightly
def test_ddg_live_canary_returns_results() -> None:
"""Hit real html.duckduckgo.com/html/ for an evergreen query and assert
INVARIANTS only (never a specific result), so result churn can't flake it.
Catches the whole silent-200 family in one check: an anti-bot block page,
a ``result__a`` / ``result__snippet`` rename, or the snippet leaving its
element all collapse into "evergreen query → 0 results / wrong shape"."""
# "wikipedia" is evergreen — a non-zero result set is a true invariant.
out = _search_duckduckgo("wikipedia", {})
if out.startswith("DuckDuckGo search error"):
pytest.skip(f"transient DDG error, not drift: {out}") # 429/5xx/timeout
assert out != "No results found.", (
"DDG returned zero results for an evergreen query — blocked or the "
"result__a / result__snippet selectors drifted. Refresh the golden "
"fixture and check the parser."
)
assert out.startswith("1. "), "result formatting changed"
assert "http" in out, "no URL decoded from any result"
blocks = out.split("\n\n")
with_snippet = sum(1 for b in blocks if len(b.splitlines()) >= 3 and b.splitlines()[2].strip())
assert with_snippet >= len(blocks) // 2, (
"snippets mostly empty — result__snippet likely moved or was renamed"
)