test_matcher.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from standards_service.service.matcher import StandardMatcher
  2. from .conftest import row, write_csv
  3. def test_exact_code_and_title_returns_perfect_match(standards_csv):
  4. matcher = StandardMatcher.from_csv(standards_csv)
  5. candidates = matcher.search("GB 50330-2013 建筑边坡工程技术规范")
  6. assert candidates[0]["code"] == "GB 50330-2013"
  7. assert candidates[0]["match_score"] == 1.0
  8. assert candidates[0]["relation_text"] == "替代 GB 50330-2002"
  9. def test_full_width_dash_is_normalized(standards_csv):
  10. matcher = StandardMatcher.from_csv(standards_csv)
  11. candidates = matcher.search("GB 50330—2013")
  12. assert candidates[0]["code"] == "GB 50330-2013"
  13. assert candidates[0]["match_score"] == 1.0
  14. def test_spaces_around_code_separators_are_normalized(standards_csv):
  15. matcher = StandardMatcher.from_csv(standards_csv)
  16. candidates = matcher.search("GB / T 6441 - 1986")
  17. assert candidates[0]["code"] == "GB 6441-1986"
  18. assert candidates[0]["match_score"] == 0.93
  19. def test_two_digit_year_matches_four_digit_year(standards_csv):
  20. matcher = StandardMatcher.from_csv(standards_csv)
  21. candidates = matcher.search("CJJ 69-95")
  22. assert candidates[0]["code"] == "CJJ 69-1995"
  23. assert candidates[0]["match_score"] == 0.98
  24. def test_standard_type_variant_is_returned_below_exact_score(standards_csv):
  25. matcher = StandardMatcher.from_csv(standards_csv)
  26. candidates = matcher.search("GB/T 6441-1986")
  27. assert candidates[0]["code"] == "GB 6441-1986"
  28. assert candidates[0]["match_score"] == 0.93
  29. def test_code_without_year_returns_only_top_three_versions(standards_csv):
  30. matcher = StandardMatcher.from_csv(standards_csv)
  31. candidates = matcher.search("GB 50330")
  32. assert [item["code"] for item in candidates] == [
  33. "GB 50330-2023",
  34. "GB 50330-2018",
  35. "GB 50330-2013",
  36. ]
  37. assert all(item["match_score"] == 0.9 for item in candidates)
  38. def test_exact_title_returns_only_top_three_versions(standards_csv):
  39. matcher = StandardMatcher.from_csv(standards_csv)
  40. candidates = matcher.search("建筑边坡工程技术规范")
  41. assert len(candidates) == 3
  42. assert candidates[0]["code"] == "GB 50330-2023"
  43. assert all(item["match_score"] == 1.0 for item in candidates)
  44. def test_title_typo_can_be_recalled(standards_csv):
  45. matcher = StandardMatcher.from_csv(standards_csv)
  46. candidates = matcher.search("绕结多孔砖和多孔砌块")
  47. assert candidates[0]["code"] == "GB/T 13544-2011"
  48. assert 0.88 <= candidates[0]["match_score"] < 1.0
  49. def test_one_character_code_typo_needs_title_support(standards_csv):
  50. matcher = StandardMatcher.from_csv(standards_csv)
  51. candidates = matcher.search("TG/T 5440-2018 公路隧道加固技术规范")
  52. assert candidates[0]["code"] == "JTG/T 5440-2018"
  53. assert candidates[0]["match_score"] < 1.0
  54. def test_different_year_with_related_title_is_returned_for_agent_review(standards_csv):
  55. matcher = StandardMatcher.from_csv(standards_csv)
  56. candidates = matcher.search("JTG D70/2-2014 公路隧道设计规范")
  57. assert candidates[0]["code"] == "JTG D70-2004"
  58. assert 0 < candidates[0]["match_score"] < 0.8
  59. def test_same_code_stem_different_year_and_exact_title_is_returned(standards_csv):
  60. matcher = StandardMatcher.from_csv(standards_csv)
  61. candidates = matcher.search("钢筋焊接及验收规程JGJ 18-2022")
  62. assert candidates[0]["code"] == "JGJ 18-2012"
  63. assert candidates[0]["match_score"] == 0.91
  64. def test_exact_title_is_returned_even_when_query_code_is_unrelated(standards_csv):
  65. matcher = StandardMatcher.from_csv(standards_csv)
  66. candidates = matcher.search("XYZ 999-2022 钢筋焊接及验收规程")
  67. assert candidates[0]["code"] == "JGJ 18-2012"
  68. assert candidates[0]["match_score"] == 0.7
  69. def test_short_generic_title_has_reduced_score(standards_csv):
  70. matcher = StandardMatcher.from_csv(standards_csv)
  71. candidates = matcher.search("验收规程")
  72. assert candidates[0]["code"] == "JGJ 18-2012"
  73. assert 0.75 <= candidates[0]["match_score"] < 0.9
  74. def test_relation_text_is_not_searchable(standards_csv):
  75. matcher = StandardMatcher.from_csv(standards_csv)
  76. candidates = matcher.search("OLD 100-2000")
  77. assert candidates == []
  78. def test_unknown_query_returns_empty_list(standards_csv):
  79. matcher = StandardMatcher.from_csv(standards_csv)
  80. assert matcher.search("本地规范库中不存在的规范") == []
  81. def test_gb18030_csv_is_supported(tmp_path, standard_rows):
  82. path = write_csv(tmp_path / "standards-gb18030.csv", standard_rows, encoding="gb18030")
  83. matcher = StandardMatcher.from_csv(path)
  84. assert matcher.search("混凝土结构设计规范")[0]["code"] == "GB 50010-2010"
  85. def test_record_without_title_is_still_searchable_by_code(tmp_path):
  86. path = write_csv(tmp_path / "code-only.csv", [row("GB 99999-2026", "")])
  87. matcher = StandardMatcher.from_csv(path)
  88. candidates = matcher.search("GB 99999-2026")
  89. assert candidates[0]["code"] == "GB 99999-2026"
  90. assert candidates[0]["title"] == ""