diff --git a/tests/test_seo_meta.py b/tests/test_seo_meta.py new file mode 100644 index 0000000..9e36664 --- /dev/null +++ b/tests/test_seo_meta.py @@ -0,0 +1,83 @@ +"""Tests for /r/{job_id} SEO meta — title diversity drives long-tail SEO, +so model name must appear in the page title, not just domain + verdict. +Without per-model variation, two reports on the same relay collide on the +same title text and Google deduplicates them out of the index. +""" + +from __future__ import annotations + +from web.server import _seo_meta_for_report + + +def _base_report(**overrides): + """Minimal finished-report shape that _seo_meta_for_report consumes.""" + report = { + "base_url": "https://www.fucheers.top/v1", + "protocol": "openai", + "target_model": "gpt-5.5", + "total_score": 91.0, + "verdict": "marginal", + "results": [ + {"status": "pass"} for _ in range(7) + ] + [ + {"status": "fail"} for _ in range(2) + ], + } + report.update(overrides) + return report + + +def test_seo_title_includes_target_model(): + """Each report's title must embed the detected model so reports across + the same relay become distinct indexable pages.""" + meta = _seo_meta_for_report(_base_report()) + title = meta["seo_title"] + + assert "www.fucheers.top" in title + assert "OpenAI" in title + assert "gpt-5.5" in title, f"model missing from title: {title!r}" + assert "91/100" in title + assert "存在风险" in title + + +def test_seo_title_falls_back_when_target_model_missing(): + """Legacy reports / probe failures may not have target_model. Title + must still render coherently — must NOT print empty 'OpenAI 中转站 检测:'.""" + meta = _seo_meta_for_report(_base_report(target_model="")) + title = meta["seo_title"] + + # No leading/trailing extra space, no " " (double space) artifact + assert " " not in title, f"double-space artifact in fallback title: {title!r}" + assert "www.fucheers.top" in title + assert "OpenAI 中转站检测" in title + assert "91/100" in title + + +def test_seo_title_distinct_per_model_same_relay(): + """Regression for the long-tail SEO intent: same domain + same score + + different model should yield different titles (otherwise Google + de-duplicates them and we lose the indexable surface).""" + a = _seo_meta_for_report(_base_report(target_model="gpt-5.5"))["seo_title"] + b = _seo_meta_for_report(_base_report(target_model="gpt-5.4-mini"))["seo_title"] + c = _seo_meta_for_report(_base_report(target_model="gpt-4o"))["seo_title"] + + assert a != b != c + assert "gpt-5.5" in a + assert "gpt-5.4-mini" in b + assert "gpt-4o" in c + + +def test_seo_title_respects_155_char_cap(): + """The 155-char cap is a hard SEO ceiling — long model snapshot IDs + must not push the title over.""" + meta = _seo_meta_for_report(_base_report(target_model="gpt-5.5-2026-04-23")) + assert len(meta["seo_title"]) <= 155 + assert "gpt-5.5-2026-04-23" in meta["seo_title"] + + +def test_seo_description_still_mentions_model(): + """The description block already mentioned model before this change — + guarding it here so a future title refactor doesn't accidentally drop + it from the meta description.""" + meta = _seo_meta_for_report(_base_report(target_model="gpt-5.5")) + assert "gpt-5.5" in meta["seo_description"] diff --git a/web/server.py b/web/server.py index 938c646..c188896 100644 --- a/web/server.py +++ b/web/server.py @@ -591,9 +591,18 @@ def _seo_meta_for_report(report: dict) -> dict[str, str]: fail_count = sum(1 for r in results if isinstance(r, dict) and r.get("status") == "fail") total = len(results) - title = ( - f"{domain} {proto_label} 中转站检测:{score:.0f}/100 {verdict_zh} | Veridrop" - ) + # Title diversity drives long-tail SEO: each report becomes its own + # indexable page with a distinct query target ("X 站测 gpt-5.5 怎么样"). + # Without the model, every report on the same domain shared one title. + if model: + title = ( + f"{domain} {proto_label} 中转站 {model} 检测:" + f"{score:.0f}/100 {verdict_zh} | Veridrop" + ) + else: + title = ( + f"{domain} {proto_label} 中转站检测:{score:.0f}/100 {verdict_zh} | Veridrop" + ) description = ( f"对 {domain} 进行 {proto_label} 中转站检测的完整报告:" f"模型 {model},总分 {score:.0f}/100,判定为「{verdict_zh}」。" diff --git a/web/templates/result.html b/web/templates/result.html index f871475..9cfa78b 100644 --- a/web/templates/result.html +++ b/web/templates/result.html @@ -22,9 +22,12 @@ {% endblock %} {% block content %} -{# Breadcrumb: 首页 › 红黑榜 › {domain} › 报告 #{job_id}. +{# Breadcrumb: 首页 › 红黑榜 › {domain} › {target_model}. When the report's base_url didn't yield a valid domain (legacy reports, - garbled URLs), drop the {domain} crumb and link straight to /leaderboard. #} + garbled URLs), drop the {domain} crumb and link straight to /leaderboard. + The trailing crumb shows the detected model so each report is uniquely + identifiable in nav and crawler-readable for long-tail SEO; falls back + to "报告 #{job_id}" when target_model is missing. #} {% set perf = report.performance or {} %}