198 lines
6.9 KiB
Python
198 lines
6.9 KiB
Python
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.matcher import CatalogMatcher
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("message", "sku"),
|
||
[
|
||
("дрель ударная prowerk pw-750 в наличии?", "INS-0008"),
|
||
("кабель шввп 2х0.5 сколько за метр", "KAB-0017"),
|
||
("диск пильный 190 на 48 зубьев", "DSK-0034"),
|
||
("уровень 1000 мм есть?", "RIN-0024"),
|
||
("гайки м10 нужны", "KRP-0020"),
|
||
("лента фум 12 мм", "RAS-0060"),
|
||
("перчатки нитриловые есть?", "PER-0002"),
|
||
("проф труба 20х20 стенка полтора", "TRB-0002"),
|
||
("гкл 9.5 сколько лист", "GKL-0001"),
|
||
("хомуты пластиковые 4.8х400", "RAS-0014"),
|
||
("сдс бур 6 на 110", "BIT-0074"),
|
||
("наждачка р120 листами", "RAS-0050"),
|
||
("круг зачистной на 125", "DSK-0018"),
|
||
("бита ph2 50 мм", "BIT-0005"),
|
||
("саморезы по дереву 3.5х45 пачка 200", "SAM-0010"),
|
||
("труба профильная 20х20х2", "TRB-0003"),
|
||
("ввгнг лс 3х1.5", "KAB-0010"),
|
||
("диск отрезной по металлу 115х1.2", "DSK-0002"),
|
||
("бур sds 10х210", "BIT-0080"),
|
||
("дюбель-гвоздь 6х60", "KRP-0048"),
|
||
],
|
||
)
|
||
def test_unique_queries_are_matched(
|
||
matcher: CatalogMatcher, message: str, sku: str
|
||
) -> None:
|
||
result = matcher.match(message)
|
||
assert result.status == "matched"
|
||
assert [candidate.sku for candidate in result.candidates] == [sku]
|
||
assert 0.0 <= result.candidates[0].confidence <= 1.0
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"message",
|
||
[
|
||
"саморезы по металлу 4.2х70",
|
||
"труба профильная 35х35",
|
||
"кабель ввгнг 4х2.5",
|
||
"ушм на 150",
|
||
"бита t50",
|
||
"шпилька м16 метровая",
|
||
"бур 10х220",
|
||
"бита ph2 70 мм",
|
||
],
|
||
)
|
||
def test_explicit_attribute_conflicts_are_not_found(
|
||
matcher: CatalogMatcher, message: str
|
||
) -> None:
|
||
result = matcher.match(message)
|
||
assert result.status == "not_found"
|
||
assert result.candidates == []
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"message",
|
||
[
|
||
"нужен кабель",
|
||
"дайте дюбелей",
|
||
"сверло нужно",
|
||
"какие есть диски",
|
||
"перфоратор посоветуйте",
|
||
"шуруповерт как у макиты, только дешевле",
|
||
"шурик на 12в недорогой",
|
||
"болгарка на 230 какая есть",
|
||
"гкл 12.5",
|
||
],
|
||
)
|
||
def test_under_specified_product_queries_are_ambiguous(
|
||
matcher: CatalogMatcher, message: str
|
||
) -> None:
|
||
result = matcher.match(message)
|
||
assert result.status == "ambiguous"
|
||
assert 2 <= len(result.candidates) <= 3
|
||
|
||
|
||
def test_unspecified_packaging_remains_ambiguous(matcher: CatalogMatcher) -> None:
|
||
result = matcher.match("здравствуйте, есть саморезы гкл 3.5х25?")
|
||
assert result.status == "ambiguous"
|
||
assert {candidate.sku for candidate in result.candidates} == {
|
||
"SAM-0061",
|
||
"SAM-0062",
|
||
"SAM-0063",
|
||
}
|
||
|
||
|
||
def test_package_word_filters_out_kilogram_item(matcher: CatalogMatcher) -> None:
|
||
result = matcher.match("саморезы по дереву 4.2 на 75, пачку")
|
||
assert result.status == "ambiguous"
|
||
assert {candidate.sku for candidate in result.candidates} == {
|
||
"SAM-0022",
|
||
"SAM-0023",
|
||
}
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"message",
|
||
[
|
||
"здравствуйте, вы до скольки работаете?",
|
||
"можно оплатить картой при получении?",
|
||
"где находится ваш магазин",
|
||
"статус заказа 4512 подскажите",
|
||
"спасибо, заказ получил, все отлично",
|
||
"",
|
||
],
|
||
)
|
||
def test_non_product_messages_are_not_found(
|
||
matcher: CatalogMatcher, message: str
|
||
) -> None:
|
||
result = matcher.match(message)
|
||
assert result.status == "not_found"
|
||
assert result.candidates == []
|
||
|
||
|
||
def test_product_evidence_has_priority_over_service_words(matcher: CatalogMatcher) -> None:
|
||
result = matcher.match("статус заказа 4512 и еще нужен бур 8х160")
|
||
assert result.status == "matched"
|
||
assert result.candidates[0].sku == "BIT-0077"
|
||
|
||
|
||
@pytest.mark.parametrize("brand", ["makita", "макита", "макиты", "макиту"])
|
||
def test_makita_transliterations_share_the_same_brand_alias(
|
||
matcher: CatalogMatcher, brand: str
|
||
) -> None:
|
||
result = matcher.match(f"шуруповерт как у {brand}, только дешевле")
|
||
|
||
assert result.status == "ambiguous"
|
||
assert [candidate.sku for candidate in result.candidates] == [
|
||
"INS-0017",
|
||
"INS-0010",
|
||
"INS-0003",
|
||
]
|
||
|
||
|
||
def test_cheap_preference_orders_candidates_by_ascending_price(
|
||
matcher: CatalogMatcher,
|
||
) -> None:
|
||
result = matcher.match("шуруповерт как у макиты, только дешевле")
|
||
prices_by_sku = {item.sku: item.price for item in matcher.items}
|
||
candidate_prices = [
|
||
prices_by_sku[candidate.sku] for candidate in result.candidates
|
||
]
|
||
|
||
assert candidate_prices == sorted(candidate_prices)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"brand", ["ToolKraft", "toolkraft", "тулкрафт", "тул крафт"]
|
||
)
|
||
def test_brand_only_toolkraft_query_returns_catalog_items(
|
||
matcher: CatalogMatcher, brand: str
|
||
) -> None:
|
||
result = matcher.match(brand)
|
||
|
||
assert result.status == "ambiguous"
|
||
assert [candidate.sku for candidate in result.candidates] == [
|
||
"INS-0022",
|
||
"INS-0023",
|
||
"INS-0024",
|
||
]
|
||
|
||
|
||
def test_unknown_toolkraft_model_does_not_fall_back_to_brand_results(
|
||
matcher: CatalogMatcher,
|
||
) -> None:
|
||
result = matcher.match("ToolKraft TK-999")
|
||
|
||
assert result.status == "not_found"
|
||
assert result.candidates == []
|
||
|
||
|
||
@pytest.mark.parametrize("query", ["шурпуоверт на 12в", "шураыввавёрт на 12в"])
|
||
def test_product_type_survives_typos(matcher: CatalogMatcher, query: str) -> None:
|
||
result = matcher.match(query)
|
||
assert result.status == "ambiguous"
|
||
assert all(candidate.sku.startswith("INS-") for candidate in 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"
|
||
|
||
|
||
def test_unknown_size_does_not_fall_back_to_nearest_product(
|
||
matcher: CatalogMatcher,
|
||
) -> None:
|
||
result = matcher.match("ушм 150")
|
||
assert result.status == "not_found"
|
||
assert result.candidates == []
|