Files
donutbrowser/sdk/python/tests/test_coverage.py
T
2026-09-09 10:09:14 +04:00

74 lines
2.8 KiB
Python

"""The SDK cannot silently drift from the app's API.
``sdk/api-paths.json`` is generated from ``src-tauri/src/api_server.rs`` and
lists every operation the desktop app publishes. These tests hold it against
the SDK's own table in both directions, so a new endpoint in the app fails here
until it is wrapped or deliberately omitted with a reason.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any, Dict, Set, Tuple
from donutbrowser import DonutClient
from donutbrowser.coverage import OMITTED, OPERATIONS
SNAPSHOT = Path(__file__).resolve().parents[2] / "api-paths.json"
def published() -> Set[Tuple[str, str]]:
document: Dict[str, Any] = json.loads(SNAPSHOT.read_text(encoding="utf-8"))
return {
(operation["method"], operation["path"]) for operation in document["operations"]
}
def test_the_snapshot_is_readable_and_not_empty() -> None:
document = json.loads(SNAPSHOT.read_text(encoding="utf-8"))
assert document["source"] == "src-tauri/src/api_server.rs"
assert document["operation_count"] == len(document["operations"])
assert document["operation_count"] > 0
assert len(published()) == document["operation_count"], "the app has two identical operations"
def test_every_published_operation_is_wrapped_or_omitted() -> None:
known = set(OPERATIONS) | set(OMITTED)
missing = sorted(published() - known)
assert not missing, (
"the app publishes operations this SDK does not handle: "
f"{missing}. Wrap each one, or add it to coverage.OMITTED with a reason."
)
def test_the_sdk_claims_nothing_the_app_does_not_publish() -> None:
stale = sorted((set(OPERATIONS) | set(OMITTED)) - published())
assert not stale, (
"this SDK handles operations the app no longer publishes: "
f"{stale}. Regenerate the snapshot with sdk/tools/extract-api-paths.py, "
"then drop or fix each entry."
)
def test_an_operation_is_either_wrapped_or_omitted_but_not_both() -> None:
both = sorted(set(OPERATIONS) & set(OMITTED))
assert not both, f"listed twice: {both}"
def test_every_omission_gives_a_reason() -> None:
for operation, reason in OMITTED.items():
assert len(reason.strip()) > 40, f"{operation} is omitted without a real reason"
def test_every_wrapped_operation_names_a_real_method() -> None:
for operation, method_name in OPERATIONS.items():
attribute = getattr(DonutClient, method_name, None)
assert callable(attribute), f"{operation} names {method_name}, which is not a method"
def test_no_two_operations_share_a_method() -> None:
names = list(OPERATIONS.values())
duplicates = sorted({name for name in names if names.count(name) > 1})
assert not duplicates, f"one method is claimed by several operations: {duplicates}"