Ctrl K
ComunidadGuías…

backend/tests/test_event_catalog_completeness.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Every event name passed to emit_event must exist in EVENT_CATALOG.

emit_event drops unknown names with a warning, so a missing entry silently
means the event never reaches the WebSocket bridge, hooks or webhooks.
"""

import ast
from pathlib import Path

from events.catalog import EVENT_CATALOG

BACKEND = Path(__file__).resolve().parent.parent
_SKIP_DIRS = {"tests", ".venv", "venv", "node_modules", "__pycache__"}


def _emitted_event_names() -> dict[str, str]:
    names: dict[str, str] = {}
    for path in BACKEND.rglob("*.py"):
        if _SKIP_DIRS & set(path.relative_to(BACKEND).parts):
            continue
        tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
        for node in ast.walk(tree):
            if not isinstance(node, ast.Call) or not node.args:
                continue
            func = node.func
            fname = func.id if isinstance(func, ast.Name) else getattr(func, "attr", None)
            first = node.args[0]
            if (
                fname == "emit_event"
                and isinstance(first, ast.Constant)
                and isinstance(first.value, str)
            ):
                names.setdefault(first.value, f"{path.relative_to(BACKEND)}:{node.lineno}")
    return names


def test_every_emitted_event_is_in_the_catalog():
    emitted = _emitted_event_names()
    assert "subtitle_health_complete" in emitted  # the scanner itself works
    missing = {name: where for name, where in emitted.items() if name not in EVENT_CATALOG}
    assert not missing, f"emit_event names missing from EVENT_CATALOG: {missing}"


def test_batch_process_progress_sends_no_absolute_path():
    """The event reaches hooks and webhooks; the catalog rule omits absolute paths."""
    path = BACKEND / "routes" / "subtitle_processor.py"
    tree = ast.parse(path.read_text(encoding="utf-8"))
    payloads = [
        node.args[1]
        for node in ast.walk(tree)
        if isinstance(node, ast.Call)
        and getattr(node.func, "id", None) == "emit_event"
        and len(node.args) > 1
        and isinstance(node.args[0], ast.Constant)
        and node.args[0].value == "batch_process_progress"
    ]
    assert payloads and all(isinstance(p, ast.Dict) for p in payloads)
    allowed = set(EVENT_CATALOG["batch_process_progress"]["payload_keys"])
    for payload in payloads:
        keys = {k.value for k in payload.keys if isinstance(k, ast.Constant)}
        assert keys <= allowed, keys - allowed


def test_catalog_entries_have_the_common_shape():
    for name, entry in EVENT_CATALOG.items():
        assert {"signal", "label", "description", "payload_keys"} <= set(entry), name
        assert entry["signal"].name == name, name