test_store.py 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. """SQLite 数据层离线测:写入幂等 + 拍平 + 分页筛选(临时库,不碰 data/app.db)。"""
  2. from __future__ import annotations
  3. from acquisition import store
  4. def _db(tmp_path):
  5. return store.connect(tmp_path / "t.db")
  6. def test_connect_uses_ck_sqlite_path(tmp_path, monkeypatch):
  7. db = tmp_path / "legacy_data" / "app.db"
  8. monkeypatch.setenv("CK_SQLITE_PATH", str(db))
  9. c = store.connect()
  10. try:
  11. assert db.exists()
  12. assert store.list_runs(c) == []
  13. finally:
  14. c.close()
  15. def test_insert_queries_idempotent_and_axes(tmp_path):
  16. c = _db(tmp_path)
  17. items = [{"query": "q1", "实质": "军人", "阶段": "灵感"}, {"query": "q2", "形式": "排比"}]
  18. assert store.insert_queries(c, "r1", "多轴", items, ts=100) == 2
  19. # 重导同 (run,method) → 覆盖不叠加
  20. assert store.insert_queries(c, "r1", "多轴", items, ts=100) == 2
  21. res = store.list_queries(c, method="多轴")
  22. assert res["total"] == 2
  23. assert res["items"][0]["axes"] == {"实质": "军人", "阶段": "灵感"} # query 不进 axes
  24. assert "query" not in res["items"][0]["axes"]
  25. def test_insert_queries_skips_empty_query(tmp_path):
  26. c = _db(tmp_path)
  27. n = store.insert_queries(c, "r1", "m", [{"query": ""}, {"实质": "x"}, {"query": "ok"}], ts=1)
  28. assert n == 1
  29. def test_search_flatten_topk_and_ok(tmp_path):
  30. c = _db(tmp_path)
  31. recs = [
  32. {"method": "M", "query": "q1",
  33. "douyin": {"ok": [{"title": "t", "url": "u", "cover": "/c.jpg", "video": "/v.mp4"},
  34. {"title": "t2", "url": "u2", "cover": "/c2.jpg", "video": "/v2.mp4"}],
  35. "error": None},
  36. "weixin": {"ok": [{"title": "w", "url": "wu", "cover": "/wc.jpg", "nick": "号"}], "error": None}},
  37. {"method": "M", "query": "q2",
  38. "douyin": {"ok": [], "error": "未搜到"},
  39. "weixin": {"ok": [], "error": "搜索失败: x"},
  40. "xiaohongshu": {"ok": [{"title": "x", "url": "xu", "cover": "/xc.jpg", "nick": "薯"}], "error": None}},
  41. ]
  42. # q1(无 xhs 键,向后兼容跳过): 2 抖音 + 1 微信 = 3;q2: 抖音空 + 微信空 + 1 小红书 = 3 → 共 6
  43. assert store.insert_search_results(c, "s1", recs, ts=5) == 6
  44. xhs = store.list_search(c, platform="xiaohongshu", ok=True)
  45. assert xhs["total"] == 1 and xhs["items"][0]["extra"]["nick"] == "薯"
  46. ok_dy = store.list_search(c, platform="douyin", ok=True)
  47. assert ok_dy["total"] == 2 # q1 的两个视频
  48. assert {i["video"] for i in ok_dy["items"]} == {"/v.mp4", "/v2.mp4"}
  49. # 搜过但无结果 → ok=0 行带 error(按 query 取得到,按钮才显示「搜过·无结果」)
  50. bad = store.list_search(c, platform="weixin", ok=False)
  51. assert bad["total"] == 1 and "失败" in bad["items"][0]["extra"]["error"]
  52. good_wx = store.list_search(c, platform="weixin", ok=True)
  53. assert good_wx["items"][0]["extra"]["nick"] == "号"
  54. # summary:q1 有 3 个结果(抖音2+微信1);q2 抖音/微信空 + 小红书 1 → ok=1, total=3 行
  55. s = store.search_summary(c)
  56. assert s["q1"]["ok"] == 3 and s["q2"]["ok"] == 1 and s["q2"]["total"] == 3
  57. def test_pagination(tmp_path):
  58. c = _db(tmp_path)
  59. store.insert_queries(c, "r", "m", [{"query": f"q{i}"} for i in range(25)], ts=1)
  60. p1 = store.list_queries(c, page=1, size=10)
  61. p3 = store.list_queries(c, page=3, size=10)
  62. assert p1["total"] == 25 and len(p1["items"]) == 10
  63. assert len(p3["items"]) == 5 # 余 5 条
  64. assert p1["items"][0]["query"] != p3["items"][0]["query"]
  65. def test_runs_and_methods(tmp_path):
  66. c = _db(tmp_path)
  67. store.insert_queries(c, "rq", "方法A", [{"query": "a"}], ts=2)
  68. store.insert_search_results(c, "rs", [{"method": "方法B", "query": "b",
  69. "douyin": None, "weixin": None}], ts=3)
  70. runs = {r["run_id"]: r["kind"] for r in store.list_runs(c)}
  71. assert runs == {"rq": "queries", "rs": "search"}
  72. assert store.list_methods(c, "queries") == ["方法A"]
  73. assert store.list_methods(c, "search_results") == ["方法B"]