omnigent-ai--omnigent
63 行
2.6 KiB
Python
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"
|
|
)
|