主站 - 新增 m0006 invite_codes 迁移;注册强制邀请码(首个管理员除外),消码与建号 同一事务,并发提交只有一个能成功 - 新增 /api/hub-admin/* 服务端点(共享 HUB_ADMIN_TOKEN,先于鉴权校验),供数据 中枢桥接读写会话/密码/模型池/会员/邀请码,并提供供应商模型列表拉取 - 前端:注册表单加邀请码(桌面 login、index.html、移动端);「系统管理」改为 「数据中枢」入口指向 8766,原模型池与会员管理分区移除,仅留「行情管理」; 随之清理陈旧 CSS 数据中枢 - 取消独立账号:删除 hub_admin/hub_sessions 与登录、改密、锁定逻辑,改为校验 主站 xiaobai_session,仅管理员可进,CSRF 由会话派生,危险操作二次确认走主站 - 控制台新增数据源凭证可编辑区(原有内容一项不删)、供应商制模型池(自动拉取 /models,失败退回卡内手动录入)、会员管理与邀请码页 - 日夜双主题:颜色收敛为同名 token 换值,SVG 改用 inline style 以吃到变量 自测 - 主站 verify_baseline 通过(498 项);数据中枢 235 项通过 - tools/verify_datahub_console.py 端到端跑通两服务真实对话; tools/verify_datahub_console_ui.py 浏览器跑通门禁/凭证/模型池/会员/主题/1030 窄屏 Co-authored-by: multica-agent <github@multica.ai>
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
NAV = ROOT / "frontend/m/config/nav.config.js"
|
|
PAGES = ROOT / "frontend/m/js/pages.js"
|
|
ROUTER = ROOT / "frontend/m/js/router.js"
|
|
|
|
|
|
class MobileSystemPagesRegressionTests(unittest.TestCase):
|
|
"""Prevent mobile system-management entries from falling back to placeholders."""
|
|
|
|
def test_nav_system_entries_are_registered_as_real_pages(self) -> None:
|
|
nav = NAV.read_text(encoding="utf-8")
|
|
pages = PAGES.read_text(encoding="utf-8")
|
|
keys = re.findall(r'key:\s*"(system/[^"]+)"', nav)
|
|
self.assertEqual(
|
|
keys,
|
|
[
|
|
"system/profile",
|
|
"system/password",
|
|
"system/membership",
|
|
"system/admin",
|
|
],
|
|
)
|
|
for key in keys:
|
|
self.assertIn(f'"{key}": setupSystemPage', pages)
|
|
self.assertIn(f'"{key}": loadSystem', pages)
|
|
|
|
def test_placeholder_copy_is_only_a_router_fallback(self) -> None:
|
|
router = ROUTER.read_text(encoding="utf-8")
|
|
pages = PAGES.read_text(encoding="utf-8")
|
|
self.assertIn("该功能页将在后续批次实现", router)
|
|
self.assertNotIn("该功能页将在后续批次实现", pages)
|
|
self.assertIn("function setupSystemPage", pages)
|
|
self.assertIn("function loadSystem", pages)
|
|
|
|
def test_system_pages_render_real_controls_not_stubs(self) -> None:
|
|
pages = PAGES.read_text(encoding="utf-8")
|
|
for marker in (
|
|
'data-system-page="home"',
|
|
'data-system-page="profile"',
|
|
'data-system-page="password"',
|
|
'data-system-page="membership"',
|
|
'data-system-page="forbidden"',
|
|
'data-system-admin-panel="market"',
|
|
"m-sys-birth-date",
|
|
"m-sys-password-current",
|
|
"m-sys-token",
|
|
"data-system-switch",
|
|
"data-system-datahub",
|
|
"管理员专区",
|
|
"刷新状态",
|
|
"打开数据中枢",
|
|
'location.assign("/login/")',
|
|
):
|
|
self.assertIn(marker, pages)
|
|
|
|
def test_system_boolean_attrs_do_not_have_stray_quotes(self) -> None:
|
|
pages = PAGES.read_text(encoding="utf-8")
|
|
stray = re.findall(r'data-system-[a-z-]*"(?=[>\s])', pages)
|
|
self.assertEqual(
|
|
stray,
|
|
[],
|
|
"boolean data-system attributes must not have a trailing quote before > or space",
|
|
)
|
|
for name in (
|
|
"data-system-save-birth",
|
|
"data-system-save-password",
|
|
"data-system-save-market",
|
|
"data-system-refresh",
|
|
"data-system-toggle-refresh",
|
|
"data-system-backfill",
|
|
"data-system-datahub",
|
|
):
|
|
self.assertIn(name, pages)
|
|
self.assertNotIn(name + '">', pages)
|