fix(v2.1.1): 修复执行问题记录中的真实 bug + 文档对齐

源码修复(5 项):
1. search.py --time-range choices 加入 week(对齐 SearXNG API 四档)
2. fetch.py stdlib 路径处理 gzip/deflate 解压(被沙箱伪响应掩盖的真实 bug,
   无 requests 环境抓取压缩服务器会全页 U+FFFD 乱码)
3. search.py --research 模式实现跨角度合并去重,输出 merged_results 字段
   (兑现文档承诺 "Results are merged and deduplicated")
4. search.py fetch_page 返回 error_code 字段 + AdaptiveThrottle 用
   E_RATE_LIMIT 结构化检测 429(原字符串匹配 "429" 会漏判
   "Too Many Requests")
5. search.py _retry_with_backoff 复用 compute_backoff_delay(60s 封顶)
   + 处理 Retry-After header,与 fetch.py 保持一致

增强(3 项):
- common.py 精确化 baidu 子域列表(pan.baidu.com/cloud.baidu.com 不再误伤)
- search.py expand_research_queries 根据主题语言切换中英文后缀
- search.py 新增 _warn_unresponsive_engines,识别实例侧引擎挂起并提示

文档/版本:
- _config.py VERSION 2.1.0 → 2.1.1
- SKILL.md 同步更新(time-range week、merged_results、error_code、baidu 精确化)
- README.md 同步更新 + 测试数量 503 → 539

测试: 539 个全部通过,含 6 个新增验证测试
This commit is contained in:
2026-08-03 12:54:27 +08:00
parent e94cbe0783
commit 0c8fdc1e45
9 changed files with 257 additions and 41 deletions
+43
View File
@@ -14,6 +14,7 @@ from search import (
_merge_headers,
_normalize_csv,
_read_queries_file,
_warn_unresponsive_engines,
deduplicate_results,
filter_results_by_domain,
load_config,
@@ -224,6 +225,14 @@ def test_build_params_time_range_none_excluded():
assert "time_range" not in p
def test_build_params_time_range_week_included():
"""v2.1.1: 'week' is a valid SearXNG API time_range tier and must be
passed through to params. Previously argparse choices omitted 'week',
forcing users to use config-file workaround."""
p = _build_params("x", _Args(time_range="week"))
assert p["time_range"] == "week"
def test_build_params_pageno_as_string():
p = _build_params("x", _Args(pageno=3))
assert p["pageno"] == "3"
@@ -252,6 +261,40 @@ def test_merge_headers_all_none():
assert _merge_headers(None, None) == {}
# ----- _warn_unresponsive_engines (v2.1.1) -----
def test_warn_unresponsive_no_field():
"""No unresponsive_engines field → no warning, no exception."""
_warn_unresponsive_engines({}, "test")
def test_warn_unresponsive_empty_list():
"""Empty unresponsive_engines list → no warning."""
_warn_unresponsive_engines({"unresponsive_engines": []}, "test")
def test_warn_unresponsive_with_reasons():
"""[engine, reason] format should be formatted as 'engine (reason)'."""
results = {"unresponsive_engines": [
["brave", "Suspended: too many requests"],
["duckduckgo", "CAPTCHA"],
]}
# Should not raise; logger.warning is called internally
_warn_unresponsive_engines(results, "test", result_count=1)
def test_warn_unresponsive_engine_only():
"""[engine] single-element format should be handled."""
results = {"unresponsive_engines": [["brave"]]}
_warn_unresponsive_engines(results, "test", result_count=5)
def test_warn_unresponsive_string_format():
"""Plain string entries (non-list) should be handled gracefully."""
results = {"unresponsive_engines": ["brave", "duckduckgo"]}
_warn_unresponsive_engines(results, "test", result_count=0)
# ----- deduplicate_results -----
def test_dedup_removes_exact_duplicate_url():