blob: 2767bd7c98f5014870309fdafc87af183de8d2c8 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Contracts for the categorized bilingual documentation system."""
from __future__ import annotations
import re
from pathlib import Path
from urllib.parse import unquote, urlsplit
PROJECT_ROOT = Path(__file__).resolve().parents[1]
DOCS_ROOT = PROJECT_ROOT / "docs"
LICENSE_MARKER = "Licensed to the Apache Software Foundation (ASF)"
MARKDOWN_LINK = re.compile(r"(?<!!)\[[^\]]+\]\(([^)\n]+)\)")
TOPIC_PAIRS = (
("docs/README.md", "docs/README.zh-CN.md"),
(
"docs/getting-started/quickstart.md",
"docs/getting-started/quickstart.zh-CN.md",
),
("docs/architecture/overview.md", "docs/architecture/overview.zh-CN.md"),
(
"docs/architecture/request-lifecycle.md",
"docs/architecture/request-lifecycle.zh-CN.md",
),
(
"docs/capabilities/tool-domains.md",
"docs/capabilities/tool-domains.zh-CN.md",
),
(
"docs/capabilities/availability.md",
"docs/capabilities/availability.zh-CN.md",
),
(
"docs/protocol/mcp-2026-07-28.md",
"docs/protocol/mcp-2026-07-28.zh-CN.md",
),
(
"docs/security/security-model.md",
"docs/security/security-model.zh-CN.md",
),
(
"docs/operations/deployment.md",
"docs/operations/deployment.zh-CN.md",
),
(
"docs/operations/reliability.md",
"docs/operations/reliability.zh-CN.md",
),
(
"docs/operations/troubleshooting.md",
"docs/operations/troubleshooting.zh-CN.md",
),
(
"docs/reference/configuration.md",
"docs/reference/configuration.zh-CN.md",
),
("docs/integrations/hosts.md", "docs/integrations/hosts.zh-CN.md"),
(
"docs/development/contributing.md",
"docs/development/contributing.zh-CN.md",
),
("docs/migration/1.0.0.md", "docs/migration/1.0.0.zh-CN.md"),
("docs/releases/1.0.0.md", "docs/releases/1.0.0.zh-CN.md"),
(
"docs/custom-tool-providers.md",
"docs/custom-tool-providers.zh-CN.md",
),
(
"docs/doris-fine-grained-access-control.md",
"docs/doris-fine-grained-access-control.zh-CN.md",
),
)
def _markdown_files() -> tuple[Path, ...]:
files = [PROJECT_ROOT / "README.md", PROJECT_ROOT / "README.zh-CN.md"]
files.extend(sorted(DOCS_ROOT.rglob("*.md")))
files.append(PROJECT_ROOT / "test" / "README.md")
return tuple(files)
def _relative_link_target(source: Path, raw_target: str) -> Path | None:
target = raw_target.strip()
if target.startswith("<") and ">" in target:
target = target[1 : target.index(">")]
else:
target = target.split(maxsplit=1)[0]
parsed = urlsplit(target)
if parsed.scheme or parsed.netloc or not parsed.path:
return None
if parsed.path.startswith("/"):
return None
return (source.parent / unquote(parsed.path)).resolve()
def test_root_readmes_are_bounded_entry_points() -> None:
assert len((PROJECT_ROOT / "README.md").read_text().splitlines()) <= 300
assert len((PROJECT_ROOT / "README.zh-CN.md").read_text().splitlines()) <= 300
def test_bilingual_topics_exist_and_cross_link() -> None:
for english_name, chinese_name in TOPIC_PAIRS:
english = PROJECT_ROOT / english_name
chinese = PROJECT_ROOT / chinese_name
assert english.is_file(), english_name
assert chinese.is_file(), chinese_name
english_content = english.read_text(encoding="utf-8")
chinese_content = chinese.read_text(encoding="utf-8")
assert chinese.name in english_content, english_name
assert english.name in chinese_content, chinese_name
assert english_content.count("\n## ") == chinese_content.count("\n## "), (
english_name,
chinese_name,
)
def test_new_topic_guides_have_apache_license_headers() -> None:
checked = {PROJECT_ROOT / name for pair in TOPIC_PAIRS for name in pair}
for path in checked:
content = path.read_text(encoding="utf-8")
assert content.count(LICENSE_MARKER) == 1, path
assert len(re.findall(r"^# [^#]", content, flags=re.MULTILINE)) == 1, path
def test_documentation_relative_links_resolve() -> None:
broken: list[str] = []
for source in _markdown_files():
content = source.read_text(encoding="utf-8")
for match in MARKDOWN_LINK.finditer(content):
target = _relative_link_target(source, match.group(1))
if target is not None and not target.exists():
broken.append(f"{source.relative_to(PROJECT_ROOT)} -> {match.group(1)}")
assert not broken, "Broken documentation links:\n" + "\n".join(broken)
def test_documentation_indexes_cover_the_public_contract() -> None:
english = (DOCS_ROOT / "README.md").read_text(encoding="utf-8")
chinese = (DOCS_ROOT / "README.zh-CN.md").read_text(encoding="utf-8")
for marker in (
"architecture/overview.md",
"capabilities/tool-domains.md",
"protocol/mcp-2026-07-28.md",
"security/security-model.md",
"operations/reliability.md",
"releases/1.0.0.md",
"issues/189",
):
assert marker in english
for marker in (
"architecture/overview.zh-CN.md",
"capabilities/tool-domains.zh-CN.md",
"protocol/mcp-2026-07-28.zh-CN.md",
"security/security-model.zh-CN.md",
"operations/reliability.zh-CN.md",
"releases/1.0.0.zh-CN.md",
"issues/189",
):
assert marker in chinese