"""Санити-тесты авто-реестра источников (без сети). Проверяют, что сгенерированный `omni/sources_auto.py` не битый: адаптеры существуют, имена/url не дублируются, обязательные поля на месте. Сами фиды тут не дергаются — живость проверяет `tools/auto_expand.py --probe`. """ from __future__ import annotations import re import pytest from omni import feeds, sources from omni.sources_auto import AUTO def test_auto_not_empty(): assert AUTO, "sources_auto.py пустой — прогони tools/auto_expand.py --probe --write" @pytest.mark.parametrize("entry", AUTO, ids=[d.get("name", "?") for d in AUTO]) def test_entry_shape(entry): for key in ("name", "url", "adapter", "kind"): assert entry.get(key), "у записи нет поля %s: %r" % (key, entry) assert entry["url"].startswith("http"), entry["url"] assert entry["adapter"] in feeds.ADAPTERS, "неизвестный адаптер: %s" % entry["adapter"] assert entry.get("category", "abuse") == "abuse" def test_names_and_urls_unique(): names = [d["name"] for d in AUTO] urls = [d["url"] for d in AUTO] assert len(names) == len(set(names)), "дубли имён в AUTO" assert len(urls) == len(set(urls)), "дубли url в AUTO" def test_all_auto_registered_in_sources(): """Каждая живая запись должна попасть в общий реестр (или быть схлопнутой дедупом по url).""" registered = {s.name for s in sources.SOURCES if s.name.startswith("aa_")} existing_urls = {s.url for s in sources.SOURCES} missing = [d["name"] for d in AUTO if d["name"] not in registered and d["url"] not in existing_urls] assert not missing, "не попали в SOURCES: %s" % missing[:5] def test_generated_file_is_marked_generated(): from pathlib import Path import omni path = Path(omni.__file__).parent / "sources_auto.py" head = path.read_text(encoding="utf-8")[:400] assert "генерируется" in head, "шапка файла должна предупреждать, что он генерируется" assert re.search(r"AUTO = \[", path.read_text(encoding="utf-8")), "нет списка AUTO"