from standards_service.service.matcher import StandardMatcher from .conftest import row, write_csv def test_exact_code_and_title_returns_perfect_match(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("GB 50330-2013 建筑边坡工程技术规范") assert candidates[0]["code"] == "GB 50330-2013" assert candidates[0]["match_score"] == 1.0 assert candidates[0]["relation_text"] == "替代 GB 50330-2002" def test_full_width_dash_is_normalized(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("GB 50330—2013") assert candidates[0]["code"] == "GB 50330-2013" assert candidates[0]["match_score"] == 1.0 def test_spaces_around_code_separators_are_normalized(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("GB / T 6441 - 1986") assert candidates[0]["code"] == "GB 6441-1986" assert candidates[0]["match_score"] == 0.93 def test_two_digit_year_matches_four_digit_year(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("CJJ 69-95") assert candidates[0]["code"] == "CJJ 69-1995" assert candidates[0]["match_score"] == 0.98 def test_standard_type_variant_is_returned_below_exact_score(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("GB/T 6441-1986") assert candidates[0]["code"] == "GB 6441-1986" assert candidates[0]["match_score"] == 0.93 def test_code_without_year_returns_only_top_three_versions(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("GB 50330") assert [item["code"] for item in candidates] == [ "GB 50330-2023", "GB 50330-2018", "GB 50330-2013", ] assert all(item["match_score"] == 0.9 for item in candidates) def test_exact_title_returns_only_top_three_versions(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("建筑边坡工程技术规范") assert len(candidates) == 3 assert candidates[0]["code"] == "GB 50330-2023" assert all(item["match_score"] == 1.0 for item in candidates) def test_title_typo_can_be_recalled(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("绕结多孔砖和多孔砌块") assert candidates[0]["code"] == "GB/T 13544-2011" assert 0.88 <= candidates[0]["match_score"] < 1.0 def test_one_character_code_typo_needs_title_support(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("TG/T 5440-2018 公路隧道加固技术规范") assert candidates[0]["code"] == "JTG/T 5440-2018" assert candidates[0]["match_score"] < 1.0 def test_different_year_with_related_title_is_returned_for_agent_review(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("JTG D70/2-2014 公路隧道设计规范") assert candidates[0]["code"] == "JTG D70-2004" assert 0 < candidates[0]["match_score"] < 0.8 def test_same_code_stem_different_year_and_exact_title_is_returned(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("钢筋焊接及验收规程JGJ 18-2022") assert candidates[0]["code"] == "JGJ 18-2012" assert candidates[0]["match_score"] == 0.91 def test_exact_title_is_returned_even_when_query_code_is_unrelated(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("XYZ 999-2022 钢筋焊接及验收规程") assert candidates[0]["code"] == "JGJ 18-2012" assert candidates[0]["match_score"] == 0.7 def test_short_generic_title_has_reduced_score(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("验收规程") assert candidates[0]["code"] == "JGJ 18-2012" assert 0.75 <= candidates[0]["match_score"] < 0.9 def test_relation_text_is_not_searchable(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) candidates = matcher.search("OLD 100-2000") assert candidates == [] def test_unknown_query_returns_empty_list(standards_csv): matcher = StandardMatcher.from_csv(standards_csv) assert matcher.search("本地规范库中不存在的规范") == [] def test_gb18030_csv_is_supported(tmp_path, standard_rows): path = write_csv(tmp_path / "standards-gb18030.csv", standard_rows, encoding="gb18030") matcher = StandardMatcher.from_csv(path) assert matcher.search("混凝土结构设计规范")[0]["code"] == "GB 50010-2010" def test_record_without_title_is_still_searchable_by_code(tmp_path): path = write_csv(tmp_path / "code-only.csv", [row("GB 99999-2026", "")]) matcher = StandardMatcher.from_csv(path) candidates = matcher.search("GB 99999-2026") assert candidates[0]["code"] == "GB 99999-2026" assert candidates[0]["title"] == ""