UI 改进: - 设置页面内嵌显示(不再弹窗) - 临时邮箱卡片添加验证码提取按钮 - 导入账号弹窗 textarea 样式优化 - 账号头像 8 色渐变轮转 - 临时邮箱页顶栏邮箱点击复制 - 邮箱管理页自动选中默认分组 - 切换分组时重置邮件列 UI - 修复收件箱/垃圾邮件 Tab 切换(.folder-tab → .email-tab) - 修复从临时邮箱返回后账号列数据残留 - 仪表盘分组列表可点击导航 恢复: - 恢复 .github/ CI 工作流 - 恢复 tests/ 测试目录 - 恢复 docs/ 文档目录 - 恢复 CONTRIBUTING.md, SECURITY.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
78 lines
2.8 KiB
Python
78 lines
2.8 KiB
Python
import ast
|
||
import unittest
|
||
from pathlib import Path
|
||
|
||
|
||
class ModuleBoundaryTests(unittest.TestCase):
|
||
@staticmethod
|
||
def _collect_import_modules(path: Path) -> set[str]:
|
||
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
||
modules: set[str] = set()
|
||
|
||
for node in ast.walk(tree):
|
||
if isinstance(node, ast.Import):
|
||
for alias in node.names:
|
||
modules.add(alias.name)
|
||
elif isinstance(node, ast.ImportFrom):
|
||
if node.module:
|
||
modules.add(node.module)
|
||
return modules
|
||
|
||
def _assert_forbidden_imports(
|
||
self, *, path: Path, forbidden_prefixes: tuple[str, ...]
|
||
):
|
||
imports = self._collect_import_modules(path)
|
||
for module in sorted(imports):
|
||
for prefix in forbidden_prefixes:
|
||
if module == prefix or module.startswith(prefix + "."):
|
||
self.fail(
|
||
f"模块边界违规:{path} 导入了禁止依赖 {module!r}(匹配前缀 {prefix!r})"
|
||
)
|
||
|
||
def test_routes_do_not_import_services_repos_or_legacy(self):
|
||
repo_root = Path(__file__).resolve().parents[1]
|
||
routes_dir = repo_root / "outlook_web" / "routes"
|
||
self.assertTrue(routes_dir.exists())
|
||
|
||
forbidden = (
|
||
"outlook_web.legacy",
|
||
"outlook_web.services",
|
||
"outlook_web.repositories",
|
||
"outlook_web.db",
|
||
)
|
||
for path in sorted(routes_dir.glob("*.py")):
|
||
if path.name == "__init__.py":
|
||
continue
|
||
self._assert_forbidden_imports(path=path, forbidden_prefixes=forbidden)
|
||
|
||
def test_repositories_do_not_depend_on_flask_routes_or_services(self):
|
||
repo_root = Path(__file__).resolve().parents[1]
|
||
repos_dir = repo_root / "outlook_web" / "repositories"
|
||
self.assertTrue(repos_dir.exists())
|
||
|
||
forbidden = (
|
||
"flask",
|
||
"outlook_web.routes",
|
||
"outlook_web.services",
|
||
"outlook_web.legacy",
|
||
)
|
||
for path in sorted(repos_dir.glob("*.py")):
|
||
if path.name == "__init__.py":
|
||
continue
|
||
self._assert_forbidden_imports(path=path, forbidden_prefixes=forbidden)
|
||
|
||
def test_services_do_not_depend_on_flask_routes_or_legacy(self):
|
||
repo_root = Path(__file__).resolve().parents[1]
|
||
services_dir = repo_root / "outlook_web" / "services"
|
||
self.assertTrue(services_dir.exists())
|
||
|
||
forbidden = (
|
||
"flask",
|
||
"outlook_web.routes",
|
||
"outlook_web.legacy",
|
||
)
|
||
for path in sorted(services_dir.glob("*.py")):
|
||
if path.name == "__init__.py":
|
||
continue
|
||
self._assert_forbidden_imports(path=path, forbidden_prefixes=forbidden)
|