# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """Where the personal-data gate learns its forbidden words from. Worth its own test because ``!add_photo`` made one of those sources *writable from a group chat*, and the failure mode is silent and permanent: a label that is also a word in this repository would put that word on the forbidden list and refuse every commit from then on, fixable only by hand-editing an album the committer cannot see. Driven entirely through ``forbidden()``, which is what the gate itself calls. """ from __future__ import annotations import importlib.util import json import sys from pathlib import Path from typing import TYPE_CHECKING, Any import pytest if TYPE_CHECKING: from types import ModuleType REPO = Path(__file__).resolve().parent.parent @pytest.fixture def sources() -> ModuleType: """Import scripts/personal_data_sources.py, which is not a package.""" path = REPO / "scripts" / "personal_data_sources.py" spec = importlib.util.spec_from_file_location("personal_data_sources", path) assert spec is not None assert spec.loader is not None module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module spec.loader.exec_module(module) return module def _album( root: Path, *, triggers: list[str] | None = None, custom: dict[str, Any] | None = None, corrupt_custom: bool = False, ) -> Path: root.mkdir(parents=True, exist_ok=True) people = [{"key": "p1", "triggers": triggers or ["!zenon"]}] (root / "people.json").write_text( json.dumps({"groups": ["g"], "people": people}), encoding="utf-8" ) if corrupt_custom: (root / "custom.json").write_text("{ not json", encoding="utf-8") elif custom is not None: (root / "custom.json").write_text(json.dumps(custom), encoding="utf-8") return root def _words(sources: ModuleType, root: Path, monkeypatch: pytest.MonkeyPatch) -> set[str]: monkeypatch.setenv("PHOTO_PACK_DIR", str(root)) words, _ = sources.forbidden(set()) return set(words) def test_a_people_trigger_is_forbidden( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: assert "zenon" in _words(sources, _album(tmp_path / "pack"), monkeypatch) def test_a_custom_label_is_forbidden_too( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """A label can be a name -- "ciocia_hania" is one however it got there.""" root = _album(tmp_path / "pack", custom={"t1": ["ciocia_hania"]}) assert "ciocia_hania" in _words(sources, root, monkeypatch) def test_a_label_that_is_a_command_word_is_not_forbidden( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The wedge: "!add_photo kot" must not outlaw "kot" in aliases.json.""" root = _album(tmp_path / "pack", custom={"t1": ["kot"], "t2": ["ciocia_hania"]}) words = _words(sources, root, monkeypatch) assert "kot" not in words assert "ciocia_hania" in words @pytest.mark.parametrize("label", ["losowe", "papiez", "komendy", "staty"]) def test_no_command_word_can_be_outlawed_by_a_label( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, label: str ) -> None: """Generalised: every alias in the table, not just the one that bit.""" root = _album(tmp_path / "pack", custom={"t1": [label]}) assert label not in _words(sources, root, monkeypatch) def test_a_people_trigger_is_never_excused_by_the_vocabulary( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Only custom labels get the subtraction. A curated real name turning up in this repository is precisely the leak the gate exists to fail on, so it stays forbidden even when it collides. """ root = _album(tmp_path / "pack", triggers=["!kot"]) assert "kot" in _words(sources, root, monkeypatch) def test_an_album_with_no_custom_file_still_works( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Other sources (the database, .env) contribute too, so this is membership.""" words = _words(sources, _album(tmp_path / "pack"), monkeypatch) assert "zenon" in words def test_a_corrupt_custom_file_is_ignored_rather_than_fatal( sources: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Refusing every commit because an album file got truncated is backwards.""" root = _album(tmp_path / "pack", corrupt_custom=True) assert "zenon" in _words(sources, root, monkeypatch)