test_reference_search.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Tests for reference_search.py — BM25 keyword search over CSV reference files.
  5. """
  6. import json
  7. import subprocess
  8. import sys
  9. from pathlib import Path
  10. import pytest
  11. SCRIPT = str(Path(__file__).resolve().parents[1] / "reference_search.py")
  12. CSV_DIR = str(Path(__file__).resolve().parents[2] / "references" / "csv")
  13. def run_search(*args: str) -> dict:
  14. """Run reference_search.py as a subprocess and return parsed JSON."""
  15. result = subprocess.run(
  16. [sys.executable, SCRIPT, "--csv-dir", CSV_DIR, *args],
  17. capture_output=True,
  18. text=True,
  19. )
  20. assert result.returncode == 0, f"Script failed: {result.stderr}"
  21. return json.loads(result.stdout)
  22. class TestSkillAndGenreFiltering:
  23. """Test filtering by skill and genre."""
  24. def test_skill_write_genre_xuanhuan_returns_nr001_not_nr002(self):
  25. """--skill write --table 命名规则 --query 角色命名 --genre 玄幻 → NR-001, not NR-002."""
  26. out = run_search(
  27. "--skill", "write",
  28. "--table", "命名规则",
  29. "--query", "角色命名",
  30. "--genre", "玄幻",
  31. )
  32. assert out["status"] == "success"
  33. ids = [r["编号"] for r in out["data"]["results"]]
  34. assert "NR-001" in ids
  35. assert "NR-002" not in ids
  36. def test_skill_write_cross_table_search(self):
  37. """--skill write --query 战斗描写 → SP-001 from 场景写法."""
  38. out = run_search(
  39. "--skill", "write",
  40. "--query", "战斗描写",
  41. )
  42. assert out["status"] == "success"
  43. assert out["data"]["total"] >= 1
  44. ids = [r["编号"] for r in out["data"]["results"]]
  45. assert "SP-001" in ids
  46. # Verify it comes from the right table
  47. tables = [r["表"] for r in out["data"]["results"] if r["编号"] == "SP-001"]
  48. assert tables[0] == "场景写法"
  49. def test_nonexistent_query_returns_empty(self):
  50. """--skill plan --query nonexistent → empty results, no error."""
  51. out = run_search(
  52. "--skill", "plan",
  53. "--query", "nonexistent",
  54. )
  55. assert out["status"] == "success"
  56. assert out["data"]["total"] == 0
  57. assert out["data"]["results"] == []
  58. class TestErrorHandling:
  59. """Test error cases."""
  60. def test_missing_csv_dir_returns_error(self):
  61. """Missing CSV dir → error JSON."""
  62. result = subprocess.run(
  63. [sys.executable, SCRIPT,
  64. "--csv-dir", "/nonexistent/path/that/does/not/exist",
  65. "--skill", "write",
  66. "--query", "test"],
  67. capture_output=True,
  68. text=True,
  69. )
  70. out = json.loads(result.stdout)
  71. assert out["status"] == "error"
  72. assert "CSV_DIR_NOT_FOUND" in out["error"]["code"]
  73. class TestOutputFormat:
  74. """Test output JSON structure."""
  75. def test_result_has_required_fields(self):
  76. """Each result has 编号, 表, 分类, 层级, 适用题材, 内容摘要."""
  77. out = run_search(
  78. "--skill", "write",
  79. "--table", "命名规则",
  80. "--query", "角色命名",
  81. )
  82. assert out["status"] == "success"
  83. for r in out["data"]["results"]:
  84. assert "编号" in r
  85. assert "表" in r
  86. assert "分类" in r
  87. assert "层级" in r
  88. assert "适用题材" in r
  89. assert "内容摘要" in r
  90. def test_data_envelope_fields(self):
  91. """Data envelope has query, skill, genre, total, results."""
  92. out = run_search(
  93. "--skill", "write",
  94. "--query", "命名",
  95. "--genre", "玄幻",
  96. )
  97. data = out["data"]
  98. assert data["query"] == "命名"
  99. assert data["skill"] == "write"
  100. assert data["genre"] == "玄幻"
  101. assert isinstance(data["total"], int)
  102. assert isinstance(data["results"], list)
  103. def test_max_results_limits_output(self):
  104. """--max-results 1 limits to 1 result."""
  105. out = run_search(
  106. "--skill", "write",
  107. "--query", "命名",
  108. "--max-results", "1",
  109. )
  110. assert out["data"]["total"] <= 1