Files
CareerNexus/tests/test_storage_db.py
RoyirinRosmontis a10442d78c CareerNexus 职联:岗位能力图谱构建与动态演化分析平台(挑战杯 XH-202621)
- 全链路:多源JD接入/清洗交叉验证/结构化抽取(星火+离线双通道)/时序知识图谱/新岗位发现/演化分析/人岗匹配/学习路径
- WebUI 五视图:驾驶舱/图谱工作台/岗位演化/匹配(单人+批量)/学习路径
- 设置中心(config.json)/Token鉴权/SQLite状态库/语义向量通道(BGE-M3本地或联网API)
- 71 个 pytest 用例;数据目录仅保留结构(.gitkeep),数据由 scripts/generate_dataset.py 复现
2026-08-03 13:15:35 +08:00

106 lines
5.2 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.
"""SQLite 应用状态库测试:建表幂等 / 留痕追加与查询 / 候选增删查 / 任务表 / 旧文件迁移。"""
from __future__ import annotations
import json
from app.storage import db
REVIEW_REC = {"candidate_id": "cand_A", "action": "accept", "operator": "张三",
"reason": "证据充分", "before": {"status": "pending"},
"after": {"status": "active"}, "time": "2026-08-01 10:00:00"}
NODE_REC = {"kind": "node_update", "node_id": "job_X", "operator": "李四",
"reason": "校正", "before": {"level": None},
"after": {"level": "高级"}, "time": "2026-08-01 11:00:00"}
CAND = {"job_id": "cand_旧候选", "title": "旧候选", "category": "新岗位候选",
"required_skills": ["Python"], "status": "pending",
"source_count": 10, "company_count": 6}
def test_init_schema_idempotent(tmp_path):
"""重复打开/建表无副作用,四张表齐备。"""
p = tmp_path / "state.db"
for _ in range(2):
conn = db.connect(p)
tables = {r[0] for r in conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'")}
assert {"audit_log", "review_candidates", "tasks", "meta"} <= tables
conn.close()
def test_audit_append_and_query(tmp_path):
"""留痕追加后按新→旧返回,结构还原为旧 jsonl 行,支持过滤与 limit。"""
p = tmp_path / "state.db"
db.append_audit(p, REVIEW_REC)
db.append_audit(p, NODE_REC)
recs = db.get_audit(p)
assert [r["operator"] for r in recs] == ["李四", "张三"] # 新→旧
assert recs[1]["candidate_id"] == "cand_A" and recs[1]["action"] == "accept"
assert recs[1]["before"] == {"status": "pending"}
assert recs[0]["kind"] == "node_update" and recs[0]["after"] == {"level": "高级"}
assert db.get_audit(p, node_id="cand_A") == [recs[1]] # 过滤
assert len(db.get_audit(p, limit=1)) == 1 # limit
def test_candidates_crud(tmp_path):
"""候选:save/get/list/delete,save 为按 job_id 的 upsert。"""
p = tmp_path / "state.db"
assert db.list_candidates(p) == []
db.save_candidate(p, CAND)
db.save_candidate(p, {**CAND, "job_id": "cand_B", "title": "另一个"})
db.save_candidate(p, {**CAND, "title": "旧候选-改"}) # upsert 覆盖
assert [c["job_id"] for c in db.list_candidates(p)] == ["cand_旧候选", "cand_B"]
assert db.get_candidate(p, "cand_旧候选")["title"] == "旧候选-改"
assert db.get_candidate(p, "cand_不存在") is None
db.delete_candidate(p, "cand_B")
assert [c["job_id"] for c in db.list_candidates(p)] == ["cand_旧候选"]
def test_tasks_upsert_get_list(tmp_path):
"""任务表:upsert 更新状态与 payload,get/list 可读回。"""
p = tmp_path / "state.db"
db.upsert_task(p, "t1", kind="jd_import", status="pending", payload={"status": "pending"})
db.upsert_task(p, "t1", kind="jd_import", status="done",
payload={"status": "done", "result": {"jobs": 18}})
row = db.get_task(p, "t1")
assert row["status"] == "done" and row["payload"]["result"]["jobs"] == 18
assert db.get_task(p, "不存在") is None
assert [t["task_id"] for t in db.list_tasks(p)] == ["t1"]
def test_overrides_crud(tmp_path):
"""人工覆写层:save/get/list/delete,save 为按 name 的 upsert。"""
p = tmp_path / "state.db"
assert db.list_overrides(p) == []
db.save_override(p, "cand_X", {"kind": "job_accept", "job": {"job_id": "cand_X"}})
db.save_override(p, "job_Y", {"kind": "node_update", "node_id": "job_Y",
"updates": {"level": "高级"}})
db.save_override(p, "cand_X", {"kind": "job_accept",
"job": {"job_id": "cand_X", "title": "改"}}) # upsert 覆盖
assert db.get_override(p, "cand_X")["job"]["title"] == "改"
assert db.get_override(p, "不存在") is None
assert [o["kind"] for o in db.list_overrides(p)] == ["job_accept", "node_update"]
db.delete_override(p, "cand_X")
assert [o["kind"] for o in db.list_overrides(p)] == ["node_update"]
def test_legacy_migration(tmp_path):
"""旧 audit.jsonl / pending.json 一次性导入:表空才迁、原文件保留、重复打开不重复迁。"""
audit = tmp_path / "audit.jsonl"
audit.write_text("\n".join(json.dumps(r, ensure_ascii=False)
for r in (REVIEW_REC, NODE_REC)) + "\n", encoding="utf-8")
pending = tmp_path / "pending.json"
pending.write_text(json.dumps([CAND], ensure_ascii=False), encoding="utf-8")
p = tmp_path / "state.db"
db.connect(p, legacy_audit=audit, legacy_pending=pending).close()
recs = db.get_audit(p)
assert len(recs) == 2 and recs[1]["candidate_id"] == "cand_A"
assert [c["job_id"] for c in db.list_candidates(p)] == ["cand_旧候选"]
assert audit.exists() and pending.exists() # 原文件保留
# 再次打开不重复迁移(meta 标记 + 表非空双保险)
db.connect(p, legacy_audit=audit, legacy_pending=pending).close()
assert len(db.get_audit(p)) == 2 and len(db.list_candidates(p)) == 1
# 迁移后新写入的数据不受影响
db.append_audit(p, REVIEW_REC)
assert len(db.get_audit(p)) == 3