Files
PlantAI-rag-new/sdk/app/test_db.py
T
jerryandClaude Fable 5 0875bb036c P0/M1: 四部分重构——frontend→ui、backend→sdk 改名 + 引用修复 + 骨架
- git mv 保留历史:ui/(Vue3+Vite)、sdk/(BFF 中间层)
- 路径语义修正:db_path/figures_cache 改相对 sdk/(BFF cwd),
  历史库 sdk/backend/data 迁移至 sdk/data/(会话/图片缓存无损)
- 引用修复 ~15 处:config.py、deploy/*.sh、vitest/playwright 配置、
  单测导入路径、docs、README、gitignore、.env.example
- 骨架:agent/{qa,methodology,legacy-rag,vendor}、knowledge/manifest.yaml、
  tooling/{compile,kb,crypto,assemble,audit,tools,scripts} + README
- 门禁验证:sdk pytest 125 通过、ui vitest 158 通过、
  生产 BFF 脚本重启四上游全绿 + 静态托管 + 会话库无损

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:56:47 +08:00

76 lines
2.7 KiB
Python

"""app/test_db.py —— SQLite 层单测(aiosqlite + WAL,幂等迁移,JSON 编解码)。
覆盖:connect 建表/索引、重复 connect 幂等、外键级联删除、JSON 编解码容错。
"""
from __future__ import annotations
import pytest
from app.db import Database, dumps_json, loads_json
async def test_connect_creates_schema_and_wal(tmp_path):
db = Database(tmp_path / "t1" / "plantai.db")
await db.connect()
try:
# WAL 模式
row = await db.fetchone("PRAGMA journal_mode")
assert row[0].lower() == "wal"
# 表存在
tables = await db.fetchall("SELECT name FROM sqlite_master WHERE type='table'")
names = {t["name"] for t in tables}
assert {"sessions", "messages"} <= names
# 索引存在
indexes = await db.fetchall(
"SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_%'"
)
inames = {i["name"] for i in indexes}
assert "idx_messages_session" in inames
assert "idx_sessions_updated" in inames
finally:
await db.close()
async def test_connect_idempotent(tmp_path):
db = Database(tmp_path / "t2" / "plantai.db")
await db.connect()
await db.close()
await db.connect() # 二次迁移不报错
await db.close()
async def test_cascade_delete_messages(tmp_path):
db = Database(tmp_path / "t3" / "plantai.db")
await db.connect()
try:
await db.execute(
"INSERT INTO sessions (id, title, status, meta, created_at, updated_at) "
"VALUES ('s1', 't', 'idle', '{}', '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z')"
)
await db.execute(
"INSERT INTO messages (id, session_id, role, kind, status, blocks, raw, text, "
"created_at, updated_at) VALUES "
"('m1', 's1', 'user', 'qa', 'done', '[]', NULL, 'hi', "
"'2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z')"
)
row = await db.fetchone("SELECT COUNT(*) AS n FROM messages WHERE session_id='s1'")
assert row["n"] == 1
await db.execute("DELETE FROM sessions WHERE id='s1'")
row = await db.fetchone("SELECT COUNT(*) AS n FROM messages WHERE session_id='s1'")
assert row["n"] == 0 # ON DELETE CASCADE
finally:
await db.close()
def test_dumps_loads_json_roundtrip():
payload = {"a": 1, "b": [1, 2], "c": {"d": "中文"}}
assert loads_json(dumps_json(payload)) == payload
assert loads_json(None, default=[]) == []
assert loads_json("not-json", default={}) == {}
def test_dumps_json_fallback_on_unserializable():
import threading
assert dumps_json({"lock": threading.Lock()}, default=None) == "null"