This commit is contained in:
喆OvO
2026-08-12 09:50:37 +08:00
parent cc43873ad2
commit 15b54f204e
10 changed files with 360 additions and 41 deletions
+87 -2
View File
@@ -22,6 +22,7 @@ import shutil
import sqlite3
import sys
import tempfile
import threading
import time
import urllib.parse
@@ -658,7 +659,7 @@ routes_mod.complete_registration = lambda request, credential: {
"ok": True, "user_id": int(credential.get("user_name", "user_0").split("_")[-1]),
"credential_id": CRED_HEX, "public_key": "abcdef",
}
routes_mod.begin_authentication = lambda request, allow_credential_ids=None: {"challenge": "BB"}
routes_mod.begin_authentication = lambda request, allow_credential_ids=None, challenge_key="login": {"challenge": "BB"}
def _cred_hex(credential):
import base64
cid = credential.get("id", "")
@@ -667,7 +668,7 @@ def _cred_hex(credential):
return base64.urlsafe_b64decode(padded.encode("ascii")).hex()
except Exception:
return "unknown"
routes_mod.complete_authentication = lambda request, credential, stored_credential_id=None, stored_public_key="": {
routes_mod.complete_authentication = lambda request, credential, stored_credential_id=None, stored_public_key="", challenge_key="login": {
"ok": True, "credential_id": _cred_hex(credential),
}
@@ -1073,6 +1074,90 @@ n = conn.execute("SELECT COUNT(*) FROM calendar_groups WHERE name='OK Group'").f
conn.close()
check("失败导入整体回滚(原子性)", n == 0)
print()
print("=" * 60)
print("TEST 21: WebAuthn challenge DB 存储(一次性/覆盖/并发原子)")
print("=" * 60)
from backend.models import save_webauthn_challenge, pop_webauthn_challenge
save_webauthn_challenge("reg_0", "ch1", db_path=db21)
check("challenge 一次性消费成功", pop_webauthn_challenge("reg_0", db_path=db21) == "ch1")
check("challenge 二次消费为 None", pop_webauthn_challenge("reg_0", db_path=db21) is None)
save_webauthn_challenge("reg_0", "a", db_path=db21)
save_webauthn_challenge("reg_0", "b", db_path=db21)
check("challenge 覆盖为最新值", pop_webauthn_challenge("reg_0", db_path=db21) == "b")
save_webauthn_challenge("race", "tok", db_path=db21)
race_results = []
race_lock = threading.Lock()
def _consume():
r = pop_webauthn_challenge("race", db_path=db21)
with race_lock:
race_results.append(r)
ths = [threading.Thread(target=_consume) for _ in range(8)]
[t.start() for t in ths]
[t.join() for t in ths]
check("并发消费仅 1 次成功(DELETE RETURNING 原子)",
race_results.count("tok") == 1 and race_results.count(None) == 7)
print()
print("=" * 60)
print("TEST 22: EventBus 跨进程去重 + 时区即时更新")
print("=" * 60)
from backend.events import EventBus
import os as _os
bus = EventBus()
q = bus.subscribe(user_id=0)
bus.publish("theme_changed", {"mode": "light"}, user_id=0)
n_local = 0
try:
while True:
q.get(timeout=0.3)
n_local += 1
except Exception:
pass
check("本地 publish 恰好投递 1 次", n_local == 1)
# 模拟本进程 NOTIFY echo(pid 相同)→ 必须被去重
echo_payload = json.dumps({"pid": _os.getpid(), "type": "theme_changed",
"data": {"mode": "light"}, "user_id": 0})
bus.handle_remote(echo_payload)
try:
q.get(timeout=0.3)
check("本进程 echo 被去重", False)
except Exception:
check("本进程 echo 被去重", True)
# 模拟其他 worker 的事件 → 应分发
remote_payload = json.dumps({"pid": _os.getpid() + 1, "type": "force_refresh",
"data": {}, "user_id": 0})
bus.handle_remote(remote_payload)
try:
ev = q.get(timeout=0.3)
check("远端事件正确分发", json.loads(ev)["type"] == "force_refresh")
except Exception:
check("远端事件正确分发", False)
# oversized → db_changed 信号
oversized_payload = json.dumps({"pid": _os.getpid() + 2, "type": "custom_notification",
"oversized": True, "user_id": 0})
bus.handle_remote(oversized_payload)
try:
ev = q.get(timeout=0.3)
check("oversized → db_changed 信号", json.loads(ev)["type"] == "db_changed")
except Exception:
check("oversized → db_changed 信号", False)
# 时区即时更新(多 worker 场景:新请求立即读到新时区,无进程级缓存)
app22, db22 = fresh_app("t22")
pw22 = get_temp_password(db22, 0)
c22, _ = do_login(app22.test_client(), pw22)
setup_totp(c22)
r = c22.get("/api/time/config")
check("默认时区 Asia/Shanghai", r.get_json()["timezone"] == "Asia/Shanghai")
r = c22.put("/api/time/config", json={"timezone": "America/New_York"})
check("设置时区成功", r.status_code == 200 and r.get_json().get("ok"))
r = c22.get("/api/time/config")
check("下一个请求立即读到新时区(无陈旧缓存)", r.get_json()["timezone"] == "America/New_York")
st = c22.get("/api/display/state").get_json()
check("display/state 返回新时区", st.get("timezone") == "America/New_York")
print()
print("=" * 60)
print(f"RESULT: {PASS} passed, {FAIL} failed")