From 51b6cad63b62bef27ac6100b73b87542b3f97eb9 Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Thu, 23 Jul 2026 02:39:43 +0800 Subject: [PATCH] Add funasr.com static page contract check (#3334) Co-authored-by: LauraGPT --- scripts/check_funasr_website_static.py | 119 ++++++++++++++++++++++ tests/test_check_funasr_website_static.py | 85 ++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100755 scripts/check_funasr_website_static.py create mode 100644 tests/test_check_funasr_website_static.py diff --git a/scripts/check_funasr_website_static.py b/scripts/check_funasr_website_static.py new file mode 100755 index 000000000..6aa831ed1 --- /dev/null +++ b/scripts/check_funasr_website_static.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""Check public funasr.com pages for growth-critical copy regressions.""" + +from __future__ import annotations + +import argparse +import sys +import urllib.request +from dataclasses import dataclass + + +BASE_URL = "https://www.funasr.com" + + +@dataclass(frozen=True) +class PageContract: + required: tuple[str, ...] + forbidden: tuple[str, ...] = () + + +PAGE_CONTRACTS: dict[str, PageContract] = { + f"{BASE_URL}/ecosystem.html": PageContract( + required=( + "35K+", + "/donors.html", + "LiteLLM", + "custom_openai", + "54.3K stars", + ), + forbidden=("16K+",), + ), + f"{BASE_URL}/en/ecosystem.html": PageContract( + required=( + "35K+", + "/en/donors.html", + "LiteLLM", + "custom_openai", + "54.3K stars", + ), + forbidden=("16K+",), + ), + f"{BASE_URL}/donors.html": PageContract( + required=("服务器", "www.funasr.com", "域名"), + ), + f"{BASE_URL}/en/donors.html": PageContract( + required=("servers", "www.funasr.com", "domain"), + ), + f"{BASE_URL}/blog/funasr-cli-transcribe-command-line.html": PageContract( + required=("推荐 funasr ≥ 1.3.26", "/donors.html"), + forbidden=("1.3.10",), + ), + f"{BASE_URL}/en/blog/funasr-cli-transcribe-command-line.html": PageContract( + required=("recommended funasr >= 1.3.26", "/en/donors.html"), + forbidden=("1.3.10",), + ), + f"{BASE_URL}/blog/funasr-llama-cpp-whisper-cpp-alternative.html": PageContract( + required=( + "runtime-llamacpp-v0.1.8", + "funasr-llamacpp-linux-x64-vulkan.tar.gz", + "Vulkan / CUDA", + "/donors.html", + ), + forbidden=("runtime-llamacpp-v0.1.1",), + ), + f"{BASE_URL}/en/blog/funasr-llama-cpp-whisper-cpp-alternative.html": PageContract( + required=( + "runtime-llamacpp-v0.1.8", + "funasr-llamacpp-linux-x64-vulkan.tar.gz", + "Vulkan / CUDA", + "/en/donors.html", + ), + forbidden=("runtime-llamacpp-v0.1.1",), + ), +} + + +def validate_pages(pages: dict[str, str]) -> list[str]: + failures: list[str] = [] + for url, contract in PAGE_CONTRACTS.items(): + text = pages.get(url) + if text is None: + failures.append(f"{url}: page was not fetched") + continue + for needle in contract.required: + if needle not in text: + failures.append(f"{url}: missing `{needle}`") + for needle in contract.forbidden: + if needle in text: + failures.append(f"{url}: forbidden `{needle}`") + return failures + + +def fetch_pages(timeout: float) -> dict[str, str]: + pages: dict[str, str] = {} + for url in PAGE_CONTRACTS: + with urllib.request.urlopen(url, timeout=timeout) as response: + body = response.read() + pages[url] = body.decode("utf-8", errors="replace") + return pages + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--timeout", type=float, default=20.0) + args = parser.parse_args(argv) + + failures = validate_pages(fetch_pages(timeout=args.timeout)) + if failures: + print("funasr.com static page contract failed:", file=sys.stderr) + for failure in failures: + print(f"- {failure}", file=sys.stderr) + return 1 + + print(f"funasr.com static page contract passed for {len(PAGE_CONTRACTS)} pages") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_check_funasr_website_static.py b/tests/test_check_funasr_website_static.py new file mode 100644 index 000000000..c355ff378 --- /dev/null +++ b/tests/test_check_funasr_website_static.py @@ -0,0 +1,85 @@ +import importlib.util +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "scripts" / "check_funasr_website_static.py" + + +def _load_module(): + spec = importlib.util.spec_from_file_location("check_funasr_website_static", SCRIPT) + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_website_contract_accepts_current_public_copy(): + checker = _load_module() + + pages = { + "https://www.funasr.com/ecosystem.html": """ +
35K+
+ 功德榜 + LiteLLM +
custom_openai
+
54.3K stars
+ """, + "https://www.funasr.com/en/ecosystem.html": """ +
35K+
+ Thanks + LiteLLM +
custom_openai
+
54.3K stars
+ """, + "https://www.funasr.com/donors.html": """ + 捐赠金额用于购买服务器与 www.funasr.com 域名 + """, + "https://www.funasr.com/en/donors.html": """ + Donations helped purchase servers and the www.funasr.com domain. + """, + "https://www.funasr.com/blog/funasr-cli-transcribe-command-line.html": """ + pip install -U funasr # 推荐 funasr ≥ 1.3.26 + 功德榜 + """, + "https://www.funasr.com/en/blog/funasr-cli-transcribe-command-line.html": """ + pip install -U funasr # recommended funasr >= 1.3.26 + Thanks + """, + "https://www.funasr.com/blog/funasr-llama-cpp-whisper-cpp-alternative.html": """ + runtime-llamacpp-v0.1.8 + funasr-llamacpp-linux-x64-vulkan.tar.gz + Vulkan / CUDA + 功德榜 + """, + "https://www.funasr.com/en/blog/funasr-llama-cpp-whisper-cpp-alternative.html": """ + runtime-llamacpp-v0.1.8 + funasr-llamacpp-linux-x64-vulkan.tar.gz + Vulkan / CUDA + Thanks + """, + } + + assert checker.validate_pages(pages) == [] + + +def test_website_contract_reports_stale_runtime_and_star_copy(): + checker = _load_module() + + pages = { + url: "" for url in checker.PAGE_CONTRACTS + } + pages["https://www.funasr.com/ecosystem.html"] = "16K+" + pages[ + "https://www.funasr.com/blog/funasr-llama-cpp-whisper-cpp-alternative.html" + ] = "runtime-llamacpp-v0.1.1" + + failures = checker.validate_pages(pages) + + assert any("ecosystem.html" in failure and "forbidden `16K+`" in failure for failure in failures) + assert any( + "funasr-llama-cpp-whisper-cpp-alternative.html" in failure + and "forbidden `runtime-llamacpp-v0.1.1`" in failure + for failure in failures + )