Files
test-search-engine/tests/test_catalog_self_retrieval.py
T

186 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from decimal import Decimal
from pathlib import Path
from typing import Callable
import pytest
from app.catalog import CatalogItem, RawCatalogItem, load_raw_catalog
from app.matcher import CatalogMatcher
CATALOG_ITEMS = load_raw_catalog(
Path(__file__).resolve().parents[1] / "catalog_excel.csv"
)
@pytest.mark.parametrize("item", CATALOG_ITEMS, ids=lambda item: item.sku)
def test_full_catalog_name_retrieves_its_own_sku(
matcher: CatalogMatcher, item: RawCatalogItem
) -> None:
result = matcher.match(item.name)
assert item.sku in {candidate.sku for candidate in result.candidates}
def test_every_canonical_product_type_returns_only_its_own_items(
matcher: CatalogMatcher,
) -> None:
items_by_sku = {item.sku: item for item in matcher.items}
product_types = {item.product_type for item in matcher.items}
for product_type in product_types:
result = matcher.match(product_type)
assert result.candidates, product_type
assert all(
items_by_sku[candidate.sku].product_type == product_type
for candidate in result.candidates
), product_type
def test_every_catalog_model_code_returns_only_matching_items(
matcher: CatalogMatcher,
) -> None:
items_by_sku = {item.sku: item for item in matcher.items}
model_codes = {
model
for item in matcher.items
for model in item.attributes.model_codes
}
for model_code in model_codes:
result = matcher.match(model_code)
assert result.candidates, model_code
assert all(
model_code in items_by_sku[candidate.sku].attributes.model_codes
for candidate in result.candidates
), model_code
def test_every_catalog_brand_returns_only_its_own_items(
matcher: CatalogMatcher,
) -> None:
items_by_sku = {item.sku: item for item in matcher.items}
brands = {
item.attributes.brand
for item in matcher.items
if item.attributes.brand is not None
}
for brand in brands:
result = matcher.match(brand)
assert result.candidates, brand
assert all(
items_by_sku[candidate.sku].attributes.brand == brand
for candidate in result.candidates
), brand
def _number_text(value: Decimal) -> str:
return format(value, "f")
def _assert_query_returns_only(
matcher: CatalogMatcher,
query: str,
predicate: Callable[[CatalogItem], bool],
) -> None:
items_by_sku = {item.sku: item for item in matcher.items}
result = matcher.match(query)
assert result.candidates, query
assert all(
predicate(items_by_sku[candidate.sku]) for candidate in result.candidates
), query
def test_every_catalog_millimetre_value_is_searchable_without_product_name(
matcher: CatalogMatcher,
) -> None:
values = {
value
for item in matcher.items
for value in (
*item.attributes.mm_values,
*((item.attributes.dimensions[0],) if item.attributes.dimensions else ()),
*((item.attributes.thread_length_mm,) if item.attributes.thread_length_mm else ()),
)
}
for value in values:
_assert_query_returns_only(
matcher,
f"{_number_text(value)} мм",
lambda item, expected=value: (
expected in item.attributes.mm_values
or bool(
item.attributes.dimensions
and item.attributes.dimensions[0] == expected
)
or item.attributes.thread_length_mm == expected
),
)
def test_every_catalog_dimension_is_searchable_without_product_name(
matcher: CatalogMatcher,
) -> None:
dimensions = {
item.attributes.dimensions
for item in matcher.items
if item.attributes.dimensions
}
for dimension in dimensions:
query = "x".join(_number_text(value) for value in dimension)
_assert_query_returns_only(
matcher,
query,
lambda item, expected=dimension: (
len(item.attributes.dimensions) >= len(expected)
and item.attributes.dimensions[: len(expected)] == expected
),
)
@pytest.mark.parametrize(
("field", "suffix"),
[
("voltage_v", " В"),
("power_w", " Вт"),
("thread_size", ""),
("grit", ""),
("profile", ""),
("sds_type", ""),
("teeth", " зубьев"),
],
)
def test_every_scalar_catalog_attribute_is_searchable_without_product_name(
matcher: CatalogMatcher,
field: str,
suffix: str,
) -> None:
values = {
getattr(item.attributes, field)
for item in matcher.items
if getattr(item.attributes, field) is not None
}
for value in values:
if field == "thread_size":
query = f"M{_number_text(value)}"
elif field == "grit":
query = f"P{value}"
else:
value_text = _number_text(value) if isinstance(value, Decimal) else str(value)
query = f"{value_text}{suffix}"
_assert_query_returns_only(
matcher,
query,
lambda item, expected=value: getattr(item.attributes, field) == expected,
)