Files
danyang-match/tests/e2e/fixture_account_guard_test.py
WorkBuddy af5412646b test(e2e): 运营登录只用 seed 账号;补幂等 E2E 与两处机械守卫
1) 修复一处从未暴露的 CI 缺陷

库中 operator 账号没有任何迁移种子,DemoDataSeeder 也不创建运营账号
(只统计数量),全新库唯一的账号来源是 seed_e2e_operators.sql。
而 conftest 的 admin_token 写死 username="admin" —— 那是某台开发机上
手工创建的遗留账号:本机有它所以本地一直跑绿,CI 全新库没有它 →
该 fixture 必然 401 → e2e job 从未真正通过过。
同一文件里「外加迁移 seed 的 admin」这句注释也是纯粹的虚构。

改用 seed 创建的 e2e_admin(已绑定 super_admin,语义等价)。

2) fixture_account_guard_test.py(2 用例)

机械拦住「fixture 依赖本机手工数据」:从 seed SQL 解析账号清单,
扫描 conftest 的所有登录用户名,任何一个不在 seed 内即变红;
另一条锁住推理前提(迁移里确实没有 operator 种子)。
两条都带「解析失败即失败」断言 —— 守卫自身静默失效是本项目反复踩过的坑。

3) idempotency_test.py(7 用例)

同键重放响应逐字节相同、同键重复提交只产生一条记录、body 字段幂等未回归、
不同键相互独立、同键作用域按 actor 隔离、5 线程并发同键业务恰好落库一次、
无键重复 Like 不得返回 500。

4) conftest.ApiClient.call 支持 headers 覆盖,供幂等用例携带自定义键。
2026-09-18 16:37:33 +08:00

74 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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 与本文档说明。"
)