Files

201 lines
8.4 KiB
Python

"""个人设置 e2e:偏好读写校验、改用户名、改密码、历史语言筛选。
用法:python tests/e2e_settings.py
"""
import json
import os
import sqlite3
import subprocess
import sys
import time
import urllib.request
import urllib.error
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
BASE = "http://127.0.0.1:5101"
_TS = int(time.time())
UA = f"sxa{_TS}"
UB = f"sxb{_TS}"
NEW_NAME = f"sxc{_TS}"
PASS = "set_e2e_123"
COOKIE = {"value": ""}
def call(method, path, payload=None):
url = BASE + path
data = None if payload is None else json.dumps(payload).encode("utf-8")
req = urllib.request.Request(url, data=data, method=method)
if payload is not None:
req.add_header("Content-Type", "application/json")
if COOKIE["value"]:
req.add_header("Cookie", COOKIE["value"])
try:
with urllib.request.urlopen(req, timeout=60) as resp:
body = resp.read()
sc = resp.headers.get("Set-Cookie")
if sc and not COOKIE["value"]:
COOKIE["value"] = sc.split(";")[0]
return resp.status, json.loads(body or b"{}")
except urllib.error.HTTPError as e:
try:
return e.code, json.loads(e.read() or b"{}")
except Exception:
return e.code, {}
def drop_cookie():
COOKIE["value"] = ""
def insert_record(user_id, title, language, created_at):
from config import DB_PATH
conn = sqlite3.connect(str(DB_PATH))
conn.execute(
"INSERT INTO ppt_records (user_id,title,content,scene,language,color_scheme,"
"page_min,page_max,status,created_at) VALUES (?,?,?,?,?,?,?,?,?,?)",
(user_id, title, "内容示例文本,用于语言筛选测试", "report", language,
"blue", 10, 15, "done", created_at),
)
conn.commit()
uid = conn.execute("SELECT id FROM ppt_records WHERE title=?", (title,)).fetchone()[0]
conn.close()
return uid
def main():
failures = []
def check(name, cond, extra=""):
print(f" [{'OK' if cond else 'NG'}] {name} {extra}")
if not cond:
failures.append(name)
env = {**os.environ, "PYTHONIOENCODING": "utf-8"}
subprocess.run([sys.executable, "-m", "src.db.init"], cwd=str(ROOT),
capture_output=True, env=env)
proc = subprocess.Popen(
[sys.executable, "-c",
f"import sys; sys.path.insert(0, r'{ROOT}');"
"from src.web.app import app; app.run(host='127.0.0.1', port=5101)"],
cwd=str(ROOT), env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
try:
up = False
for _ in range(30):
time.sleep(0.5)
try:
call("GET", "/api/me")
up = True
break
except Exception:
continue
check("server up", up)
s, b = call("POST", "/api/register",
{"username": UA, "password": PASS, "confirm_password": PASS})
check("register A", s == 200 and b.get("code") == 0)
# ---- GET /api/settings 默认值 ----
s, b = call("GET", "/api/settings")
p = (b.get("data") or {}).get("preferences") or {}
check("settings defaults", s == 200 and b["data"]["username"] == UA
and p.get("default_scene") == "training" and p.get("default_color") == "blue"
and p.get("default_language") == "zh" and p.get("default_canvas") == "ppt169"
and p.get("default_image_strategy") == "web"
and p.get("default_page_min") == 10 and p.get("default_page_max") == 15,
str(p)[:80])
# ---- 偏好保存(部分字段)----
s, b = call("PUT", "/api/settings/preferences",
{"default_color": "purple", "default_language": "ja",
"default_page_min": 5, "default_page_max": 8})
p = ((b.get("data") or {}).get("preferences")) or {}
check("save prefs partial", s == 200 and p.get("default_color") == "purple"
and p.get("default_language") == "ja" and p.get("default_page_min") == 5
and p.get("default_page_max") == 8 and p.get("default_scene") == "training",
str(p)[:80])
s, b = call("PUT", "/api/settings/preferences", {"default_color": "pink"})
check("prefs invalid enum -> 5002", s == 400 and b.get("code") == 5002, str(b)[:60])
s, b = call("PUT", "/api/settings/preferences", {"default_page_min": 20, "default_page_max": 8})
check("prefs min>max -> 2002", s == 400 and b.get("code") == 2002, str(b)[:60])
# ---- 改用户名 ----
s, b = call("PUT", "/api/settings/username", {"username": "ab"})
check("username too short -> 5002", s == 400 and b.get("code") == 5002, str(b)[:50])
s, b = call("POST", "/api/register",
{"username": UB, "password": PASS, "confirm_password": PASS})
drop_cookie()
call("POST", "/api/login", {"username": UA, "password": PASS})
s, b = call("PUT", "/api/settings/username", {"username": UB})
check("username duplicate -> 1001", s == 409 and b.get("code") == 1001, str(b)[:50])
new_name = NEW_NAME
s, b = call("PUT", "/api/settings/username", {"username": new_name})
s2, b2 = call("GET", "/api/me")
check("username changed", s == 200 and b2["data"]["username"] == new_name,
str(b2)[:60])
# ---- 改密码 ----
s, b = call("PUT", "/api/settings/password",
{"old_password": "wrong_old", "new_password": "newpwd456",
"confirm_password": "newpwd456"})
check("wrong old pwd -> 5001", s == 400 and b.get("code") == 5001, str(b)[:50])
s, b = call("PUT", "/api/settings/password",
{"old_password": PASS, "new_password": "newpwd456",
"confirm_password": "different"})
check("confirm mismatch -> 1003", s == 400 and b.get("code") == 1003, str(b)[:50])
s, b = call("PUT", "/api/settings/password",
{"old_password": PASS, "new_password": "abc",
"confirm_password": "abc"})
check("short new pwd -> 5002", s == 400 and b.get("code") == 5002, str(b)[:50])
s, b = call("PUT", "/api/settings/password",
{"old_password": PASS, "new_password": "newpwd456",
"confirm_password": "newpwd456"})
check("password changed", s == 200 and b.get("code") == 0)
drop_cookie()
s, _ = call("POST", "/api/login", {"username": new_name, "password": PASS})
check("old password rejected -> 401", s == 401)
s, b = call("POST", "/api/login", {"username": new_name, "password": "newpwd456"})
check("login with new password", s == 200 and b.get("code") == 0)
# ---- 历史语言筛选 ----
uid = None
from src.agent import memory as mem
u = mem.get_user_by_name(new_name)
uid = u["id"]
insert_record(uid, "筛选测试中文稿", "zh", "2026-08-25 10:00:00")
insert_record(uid, "フィルタ日本語資料", "ja", "2026-08-25 10:01:00")
s, b = call("GET", "/api/records?language=ja")
titles_ja = [r["title"] for r in b["data"]["records"]]
s, b = call("GET", "/api/records?language=zh")
titles_zh = [r["title"] for r in b["data"]["records"]]
s, b = call("GET", "/api/records")
titles_all = [r["title"] for r in b["data"]["records"]]
check("filter ja only", "フィルタ日本語資料" in titles_ja
and "筛选测试中文稿" not in titles_ja, str(titles_ja)[:70])
check("filter zh only", "筛选测试中文稿" in titles_zh
and "フィルタ日本語資料" not in titles_zh, str(titles_zh)[:70])
check("no filter shows both", "筛选测试中文稿" in titles_all
and "フィルタ日本語資料" in titles_all, f"n={len(titles_all)}")
# 清理测试插入的记录
conn = sqlite3.connect(str(ROOT / "src" / "web" / "data" / "ppt.db"))
conn.execute("DELETE FROM ppt_records WHERE title IN (?,?)",
("筛选测试中文稿", "フィルタ日本語資料"))
conn.commit()
conn.close()
finally:
proc.terminate()
print("\n" + ("SETTINGS E2E ALL PASSED" if not failures
else "FAILURES: " + "; ".join(failures)))
sys.exit(1 if failures else 0)
if __name__ == "__main__":
main()