From 90676df647fbc1bd5784e8e3b435aa8da20a51ae Mon Sep 17 00:00:00 2001 From: huangruiteng Date: Sat, 3 Oct 2026 03:09:50 +0800 Subject: [PATCH] test: isolate source census from dependency churn Signed-off-by: huangruiteng --- ...st_chat_capabilities_route_single_owner.py | 48 +++++++++++++++++-- .../test_source_cli_entrypoint.py | 4 +- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/tests/architecture/test_chat_capabilities_route_single_owner.py b/tests/architecture/test_chat_capabilities_route_single_owner.py index c903610b6b..f2822f6154 100644 --- a/tests/architecture/test_chat_capabilities_route_single_owner.py +++ b/tests/architecture/test_chat_capabilities_route_single_owner.py @@ -16,6 +16,7 @@ import ast import http.client +import os from pathlib import Path import pytest @@ -42,11 +43,48 @@ def _web_sources(root: Path) -> list[Path]: web = root / "apps" if not web.is_dir(): return [] - return sorted( - path - for path in web.rglob("*.ts") - if "node_modules" not in path.relative_to(root).parts - ) + + def fail_on_source_error(error: OSError) -> None: + raise error + + sources = [] + for directory, directories, files in os.walk(web, onerror=fail_on_source_error): + # Dependency installation can replace these directories during the + # census. Prune before traversing; first-party I/O errors still fail. + directories[:] = [name for name in directories if name != "node_modules"] + sources.extend(Path(directory) / name for name in files if name.endswith(".ts")) + return sorted(sources) + + +def test_web_census_never_enters_dependencies(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + source = tmp_path / "apps" / "client" / "route.ts" + source.parent.mkdir(parents=True) + source.write_text("export const route = '/api/chat/capabilities';", encoding="utf-8") + dependencies = source.parent / "node_modules" + dependencies.mkdir() + scan = os.scandir + + def guarded_scan(path): + if Path(path) == dependencies: + raise FileNotFoundError("dependency tree was concurrently replaced") + return scan(path) + + monkeypatch.setattr(os, "scandir", guarded_scan) + assert _web_sources(tmp_path) == [source] + + +def test_web_census_does_not_hide_first_party_io_errors( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, +) -> None: + source_root = tmp_path / "apps" + source_root.mkdir() + + def inaccessible_source(path): + raise PermissionError("first-party source is unreadable") + + monkeypatch.setattr(os, "scandir", inaccessible_source) + with pytest.raises(PermissionError, match="first-party source"): + _web_sources(tmp_path) def _module_bindings(root: Path) -> dict[str, int]: diff --git a/tests/control_plane/test_source_cli_entrypoint.py b/tests/control_plane/test_source_cli_entrypoint.py index 1ff0145055..37b091c7d6 100644 --- a/tests/control_plane/test_source_cli_entrypoint.py +++ b/tests/control_plane/test_source_cli_entrypoint.py @@ -311,7 +311,9 @@ def test_source_first_usage_disclosure_keeps_json_pure_and_does_not_send(tmp_pat assert json.loads(result.stdout)["ok"] is True assert "random installation ID" in result.stderr stored = json.loads((state / "usage-ping.json").read_text()) - assert stored["notice"]["version"] == 5 + # Version 6 discloses the installation profile and overlapping runtime + # clocks. Pin the public contract independently of the implementation. + assert stored["notice"]["version"] == 6 assert "last_attempt_day" not in stored and "counters" not in stored