Improve sparse and structured catalog matching
This commit is contained in:
1 parent
2982161374
commit
42f8f07207
8 files changed
+739
-20
No files matched your search
@@ -184,6 +184,55 @@ def test_product_type_survives_typos(matcher: CatalogMatcher, query: str) -> Non
|
||||
assert all(candidate.sku.startswith("INS-") for candidate in result.candidates)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("query", "skus"),
|
||||
[
|
||||
("удар", ("INS-0008", "INS-0009", "INS-0022")),
|
||||
("ударная", ("INS-0008", "INS-0009", "INS-0022")),
|
||||
("удар мощный", ("INS-0008", "INS-0009", "INS-0022")),
|
||||
(
|
||||
"удар мощно 900 хромированый анус долбить",
|
||||
("INS-0009", "INS-0016", "INS-0023"),
|
||||
),
|
||||
(
|
||||
"дрель удар мощно 900 хромированый анус долбить",
|
||||
("INS-0009", "INS-0023", "INS-0002"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_trigram_fallback_finds_product_type_from_sparse_or_noisy_text(
|
||||
matcher: CatalogMatcher, query: str, skus: tuple[str, ...]
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == "ambiguous"
|
||||
assert tuple(candidate.sku for candidate in result.candidates) == skus
|
||||
items_by_sku = {item.sku: item for item in matcher.items}
|
||||
assert {
|
||||
items_by_sku[candidate.sku].product_type for candidate in result.candidates
|
||||
} == {"дрель"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[
|
||||
"анус",
|
||||
"долбить",
|
||||
"какой адрес",
|
||||
"аквариум 125 мм",
|
||||
"<script>alert(1)</script>",
|
||||
"qwerty asdf zxcv",
|
||||
],
|
||||
)
|
||||
def test_trigram_fallback_rejects_weak_or_accidental_similarity(
|
||||
matcher: CatalogMatcher, query: str
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == "not_found"
|
||||
assert result.candidates == []
|
||||
|
||||
|
||||
def test_bare_size_is_parsed_without_unit(matcher: CatalogMatcher) -> None:
|
||||
assert matcher.match("бита ph2 50").candidates[0].sku == "BIT-0005"
|
||||
assert matcher.match("диск пильный 190 48 зубьев").candidates[0].sku == "DSK-0034"
|
||||
@@ -195,3 +244,110 @@ def test_unknown_size_does_not_fall_back_to_nearest_product(
|
||||
result = matcher.match("ушм 150")
|
||||
assert result.status == "not_found"
|
||||
assert result.candidates == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("query", "sku"),
|
||||
[
|
||||
("165 мм 24 зуба", "DSK-0029"),
|
||||
("165 мм 36 зубов", "DSK-0030"),
|
||||
("165 мм 48 зубьев", "DSK-0031"),
|
||||
("190 мм 24 зуба", "DSK-0032"),
|
||||
("190 мм 36 зубов", "DSK-0033"),
|
||||
("190 мм 48 зубьев", "DSK-0034"),
|
||||
("210 мм 24 зуба", "DSK-0035"),
|
||||
("210 мм 36 зубов", "DSK-0036"),
|
||||
("210 мм 48 зубьев", "DSK-0037"),
|
||||
],
|
||||
)
|
||||
def test_tooth_count_infers_saw_blade_without_product_name(
|
||||
matcher: CatalogMatcher, query: str, sku: str
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == "matched"
|
||||
assert [candidate.sku for candidate in result.candidates] == [sku]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("query", "skus"),
|
||||
[
|
||||
("24 зуба", ("DSK-0029", "DSK-0032", "DSK-0035")),
|
||||
("36 зубов", ("DSK-0030", "DSK-0033", "DSK-0036")),
|
||||
("48 зубьев", ("DSK-0031", "DSK-0034", "DSK-0037")),
|
||||
],
|
||||
)
|
||||
def test_bare_tooth_count_returns_all_matching_saw_blade_sizes(
|
||||
matcher: CatalogMatcher, query: str, skus: tuple[str, ...]
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == "ambiguous"
|
||||
assert tuple(candidate.sku for candidate in result.candidates) == skus
|
||||
|
||||
|
||||
def test_unknown_tooth_count_does_not_return_nearest_saw_blade(
|
||||
matcher: CatalogMatcher,
|
||||
) -> None:
|
||||
result = matcher.match("210 мм 25 зубьев")
|
||||
|
||||
assert result.status == "not_found"
|
||||
assert result.candidates == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query",
|
||||
[
|
||||
"пильный диск 190x48t",
|
||||
"190x48T",
|
||||
"190x48z",
|
||||
"190 48t",
|
||||
"190/48T",
|
||||
],
|
||||
)
|
||||
def test_compact_saw_notation_parses_diameter_and_teeth(
|
||||
matcher: CatalogMatcher, query: str
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == "matched"
|
||||
assert [candidate.sku for candidate in result.candidates] == ["DSK-0034"]
|
||||
|
||||
|
||||
def test_bare_t_suffix_infers_saw_blades(matcher: CatalogMatcher) -> None:
|
||||
result = matcher.match("24T")
|
||||
|
||||
assert result.status == "ambiguous"
|
||||
assert [candidate.sku for candidate in result.candidates] == [
|
||||
"DSK-0029",
|
||||
"DSK-0032",
|
||||
"DSK-0035",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("query", "status", "skus"),
|
||||
[
|
||||
("125 мм", "ambiguous", ("INS-0013", "DSK-0025", "RAS-0054")),
|
||||
("12 В", "ambiguous", ("INS-0003", "INS-0010", "INS-0024")),
|
||||
("900 Вт", "ambiguous", ("INS-0009", "INS-0013")),
|
||||
("M10", "ambiguous", ("KRP-0030", "KRP-0020", "KRP-0009")),
|
||||
("P120", "ambiguous", ("DSK-0024", "RAS-0050", "RAS-0057")),
|
||||
("SDS-plus", "ambiguous", ("BIT-0073", "INS-0012")),
|
||||
("PH2", "ambiguous", ("BIT-0004", "RIN-0010")),
|
||||
("20x20x2", "matched", ("TRB-0003",)),
|
||||
("ToolKraft 125 мм", "matched", ("INS-0027",)),
|
||||
("99999 мм", "not_found", ()),
|
||||
("аквариум 125 мм", "not_found", ()),
|
||||
],
|
||||
)
|
||||
def test_attribute_only_queries_use_exact_catalog_values(
|
||||
matcher: CatalogMatcher,
|
||||
query: str,
|
||||
status: str,
|
||||
skus: tuple[str, ...],
|
||||
) -> None:
|
||||
result = matcher.match(query)
|
||||
|
||||
assert result.status == status
|
||||
assert tuple(candidate.sku for candidate in result.candidates) == skus
|
||||
Reference in new issue
Block a user