polish: discovery hardening per code review (review round 2 minors)

- Fail fast with clear RuntimeError when themes/ directory is missing
- Document underscore-prefix skip rule
- Tests: broken-manifest/mismatch/no-manifest skip case, missing-dir fail-fast
- tests/test_theme_pro_v3.py: drop fourth hardcoded list, import from core

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-22 17:28:57 +08:00
parent 204c5039ab
commit b1b4d12a19
4 changed files with 28 additions and 3 deletions
+5 -1
View File
@@ -13,8 +13,12 @@ ROOT = Path(__file__).resolve().parent.parent
def discover_themes():
"""Discover built-in themes from themes/*/manifest.json (single source of truth)."""
themes_dir = ROOT / 'themes'
if not themes_dir.is_dir():
raise RuntimeError(f'themes directory missing: {themes_dir}')
found = []
for entry in sorted((ROOT / 'themes').iterdir()):
for entry in sorted(themes_dir.iterdir()):
# Underscore prefix marks non-theme assets (e.g. _professional_components.html).
if not entry.is_dir() or entry.name.startswith('_'):
continue
try:
@@ -12,6 +12,7 @@
- 工作目录:`C:\Users\30997\Downloads\SiteForge_Factory_V5_Web_Docker\SiteForge_Enterprise_V8_10`(下称 `$ROOT`,bash 里用正斜杠)
- Docker 实例已运行:`http://127.0.0.1:8787`(容器 siteforge-v8-10-siteforge-1/-worker-1),admin 密码在 `.env`
- 测试命令:`python -m pytest -q`(当前 210 项全绿,任何改动后必须保持);JS 检查 `node --check app/static/<file>.js`
- **宿主机快速循环**(V8_12 起):`PYTHONUTF8=1 .venv/Scripts/python -m pytest -q`,基线 **227 passed + 2 known-fail**(test_p0_backup.py::test_sqlite_backup_and_restore_with_media、test_p0_p4_completion_v89.py::test_restore_replaces_directory_atomically_and_preserves_previous——均为 Windows sqlite 文件锁环境问题,改动前即失败);任何新失败都是回归。权威全量验证用容器:`MSYS_NO_PATHCONV=1 docker run --rm -v "$(pwd -W):/project" -w /project python:3.12-slim sh -c "python -m pip install -q -r requirements-dev.lock && python -m pytest -q"`(容器基线 226 passed + 1 node 环境失败)
- cms 设计源:`D:/project/cms/runtimes/astro-site-runtime/src`(tokens.css 250 行、base.css 445 行、components/*.astro、pages/[locale]/*.astro);像素基准 `dist/hesupipe/en/`
- 改动 Python 后需 `docker compose up -d --build` 重建容器再验证 HTTP 层(构建约 1 分钟,有缓存)
- 契约清单已固化于 spec §2(F1-F13),本计划直接引用
+1 -2
View File
@@ -6,8 +6,7 @@ import zipfile
from pathlib import Path
from bs4 import BeautifulSoup
from test_factory_v2 import factory, site, zip_theme
THEMES=('automotive','industrial','corporate','minimal')
from app.core import THEMES
def test_four_complete_theme_families_have_distinct_markup(factory):
main=[]
+21
View File
@@ -14,5 +14,26 @@ def test_discover_returns_builtin_themes_with_manifests():
assert manifest.get('id') == tid
def test_three_whitelists_agree():
# Tripwire: the three names must stay the same object. Fails only if someone
# reintroduces a hardcoded copy of the list anywhere.
assert set(core.THEMES) == set(theme_pro.THEMES) == set(theme_store.BUILTIN)
assert isinstance(core.THEMES, tuple)
def test_discover_skips_broken_theme_dirs(tmp_path, monkeypatch):
themes = tmp_path / 'themes'
good = themes / 'good-one'; good.mkdir(parents=True)
(good / 'manifest.json').write_text(json.dumps({'id': 'good-one'}), encoding='utf-8')
broken = themes / 'broken'; broken.mkdir()
(broken / 'manifest.json').write_text('{not json', encoding='utf-8')
mismatch = themes / 'mismatch'; mismatch.mkdir()
(mismatch / 'manifest.json').write_text(json.dumps({'id': 'other-name'}), encoding='utf-8')
no_manifest = themes / 'no-manifest'; no_manifest.mkdir()
underscore = themes / '_shared'; underscore.mkdir()
monkeypatch.setattr(core, 'ROOT', tmp_path)
assert core.discover_themes() == ('good-one',)
def test_discover_fails_fast_when_themes_dir_missing(tmp_path, monkeypatch):
monkeypatch.setattr(core, 'ROOT', tmp_path)
import pytest
with pytest.raises(RuntimeError, match='themes directory missing'):
core.discover_themes()