74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
"""E2E 自检守卫:fixture 只能使用 seed 脚本真正创建的运营账号。
|
||||
|
|
|
|||
|
|
为什么需要它(本轮发现的真实缺陷):
|
|||
|
|
|
|||
|
|
库中 operator 账号**没有任何迁移种子**,`DemoDataSeeder` 也不创建运营账号
|
|||
|
|
(只统计数量)。因此全新库(CI)里唯一的运营账号来源是
|
|||
|
|
`tests/e2e/sql/seed_e2e_operators.sql`。
|
|||
|
|
|
|||
|
|
而 `conftest.py` 的 `admin_token` 曾写死 `username="admin"` —— 那是某台
|
|||
|
|
开发机上手工创建的遗留账号。本机有它,所以本地跑绿;CI 全新库没有它,
|
|||
|
|
该 fixture 必然 401 → **整个 E2E job 从未真正通过过**。
|
|||
|
|
|
|||
|
|
这类"依赖本机手工数据"的写法没有任何运行期提示,只能靠机械检查拦住。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import re
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
E2E_DIR = Path(__file__).resolve().parent
|
|||
|
|
CONFTEST = E2E_DIR / "conftest.py"
|
|||
|
|
SEED_SQL = E2E_DIR / "sql" / "seed_e2e_operators.sql"
|
|||
|
|
MIGRATION_DIR = E2E_DIR.parent.parent / "backend" / "src" / "main" / "resources" / "db" / "migration"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _seeded_usernames() -> set[str]:
|
|||
|
|
sql = SEED_SQL.read_text(encoding="utf-8")
|
|||
|
|
# seed 用的是 VALUES 行:('uuid', '名称', 'username', crypt(...), 'active')
|
|||
|
|
return set(re.findall(r"'([A-Za-z0-9_]+)',\s+crypt\(", sql))
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _usernames_used_in_conftest() -> set[str]:
|
|||
|
|
src = CONFTEST.read_text(encoding="utf-8")
|
|||
|
|
used = set(re.findall(r'"username"\s*:\s*"([A-Za-z0-9_]+)"', src))
|
|||
|
|
used |= set(re.findall(r'ADMIN_USERNAME\s*=\s*"([A-Za-z0-9_]+)"', src))
|
|||
|
|
return used
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_conftest_only_logs_in_with_seeded_accounts() -> None:
|
|||
|
|
seeded = _seeded_usernames()
|
|||
|
|
assert seeded, f"未能从 {SEED_SQL.name} 解析出任何账号,守卫已失效"
|
|||
|
|
|
|||
|
|
used = _usernames_used_in_conftest()
|
|||
|
|
assert used, f"未能从 {CONFTEST.name} 解析出任何登录用户名,守卫已失效"
|
|||
|
|
|
|||
|
|
unknown = sorted(used - seeded)
|
|||
|
|
assert not unknown, (
|
|||
|
|
"conftest 使用了 seed 脚本未创建的运营账号:"
|
|||
|
|
f"{unknown}(seed 现有:{sorted(seeded)})。\n"
|
|||
|
|
"全新库(CI)里这些账号不存在,对应 fixture 会 401,E2E 必然失败。"
|
|||
|
|
"请改用 seed 中的账号,或在 seed 脚本中补上该账号。"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_no_operator_account_is_seeded_by_migrations() -> None:
|
|||
|
|
"""锁住上面那条推理的前提:迁移确实没有 operator 种子。
|
|||
|
|
|
|||
|
|
若将来有人在迁移里加了 admin 种子,本用例会变红——那时应重新评估
|
|||
|
|
`admin_token` 的选择(迁移种子同样是自举的,可作为合法来源),
|
|||
|
|
而不是继续凭本机的遗留账号。
|
|||
|
|
"""
|
|||
|
|
hits: list[str] = []
|
|||
|
|
for sql_file in MIGRATION_DIR.glob("*.sql"):
|
|||
|
|
text = sql_file.read_text(encoding="utf-8", errors="replace")
|
|||
|
|
if re.search(r"INSERT\s+INTO\s+operators\b", text, re.IGNORECASE):
|
|||
|
|
hits.append(sql_file.name)
|
|||
|
|
|
|||
|
|
assert not hits, (
|
|||
|
|
f"迁移中出现了 operator 种子:{hits}。"
|
|||
|
|
"本次 E2E fixture 的选型前提('运营账号只能来自 seed 脚本')已改变,"
|
|||
|
|
"请重新评估 conftest 的 ADMIN_USERNAME 与本文档说明。"
|
|||
|
|
)
|