170 lines
5.8 KiB
Python
170 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from standard_products.db import (
|
|
Product,
|
|
add_product,
|
|
connect,
|
|
count_products,
|
|
delete_product,
|
|
export_json,
|
|
get_product,
|
|
import_json,
|
|
init_db,
|
|
list_products,
|
|
search_products,
|
|
update_product,
|
|
upsert_product,
|
|
)
|
|
|
|
|
|
@pytest.fixture()
|
|
def connection(tmp_path: Path):
|
|
db_path = init_db(tmp_path / "catalog.db")
|
|
with connect(db_path) as conn:
|
|
yield conn
|
|
|
|
|
|
def make_product(**overrides: object) -> Product:
|
|
data: dict[str, object] = {
|
|
"designation": "Болт M10x50 ГОСТ 7798-70",
|
|
"name": "Болт с шестигранной головкой",
|
|
"standard": "ГОСТ 7798-70",
|
|
"category": "Крепёж",
|
|
"material": "Сталь 20",
|
|
"mass_kg": 0.045,
|
|
"attributes": {"резьба": "M10", "покрытие": "цинк"},
|
|
}
|
|
data.update(overrides)
|
|
return Product(**data) # type: ignore[arg-type]
|
|
|
|
|
|
def test_init_db_creates_file(tmp_path: Path) -> None:
|
|
db_path = init_db(tmp_path / "nested" / "catalog.db")
|
|
assert db_path.exists()
|
|
with connect(db_path) as conn:
|
|
assert count_products(conn) == 0
|
|
|
|
|
|
def test_add_and_get_product(connection) -> None:
|
|
product_id = add_product(connection, make_product())
|
|
assert product_id > 0
|
|
|
|
stored = get_product(connection, "Болт M10x50 ГОСТ 7798-70")
|
|
assert stored is not None
|
|
assert stored.name == "Болт с шестигранной головкой"
|
|
assert stored.attributes == {"резьба": "M10", "покрытие": "цинк"}
|
|
assert stored.mass_kg == pytest.approx(0.045)
|
|
|
|
|
|
def test_duplicate_designation_is_rejected(connection) -> None:
|
|
add_product(connection, make_product())
|
|
with pytest.raises(ValueError, match="уже существует"):
|
|
add_product(connection, make_product(name="Другой болт"))
|
|
|
|
|
|
def test_upsert_updates_existing_row(connection) -> None:
|
|
product_id = add_product(connection, make_product())
|
|
updated_id = upsert_product(connection, make_product(name="Болт обновлённый"))
|
|
|
|
assert updated_id == product_id
|
|
stored = get_product(connection, "Болт M10x50 ГОСТ 7798-70")
|
|
assert stored is not None
|
|
assert stored.name == "Болт обновлённый"
|
|
assert count_products(connection) == 1
|
|
|
|
|
|
def test_validation(connection) -> None:
|
|
with pytest.raises(ValueError, match="designation"):
|
|
add_product(connection, make_product(designation=" "))
|
|
with pytest.raises(ValueError, match="name"):
|
|
add_product(connection, make_product(name=""))
|
|
with pytest.raises(ValueError, match="mass_kg"):
|
|
add_product(connection, make_product(mass_kg=-1))
|
|
|
|
|
|
def test_search_by_query_and_filters(connection) -> None:
|
|
add_product(connection, make_product())
|
|
add_product(
|
|
connection,
|
|
make_product(
|
|
designation="Гайка M10 ГОСТ 5915-70",
|
|
name="Гайка шестигранная",
|
|
standard="ГОСТ 5915-70",
|
|
material="Сталь 10",
|
|
attributes={},
|
|
),
|
|
)
|
|
|
|
by_text = search_products(connection, "гайка")
|
|
assert [p.designation for p in by_text] == ["Гайка M10 ГОСТ 5915-70"]
|
|
|
|
by_attribute = search_products(connection, "цинк")
|
|
assert [p.designation for p in by_attribute] == ["Болт M10x50 ГОСТ 7798-70"]
|
|
|
|
by_standard = search_products(connection, standard="ГОСТ 5915-70")
|
|
assert len(by_standard) == 1
|
|
|
|
by_category = search_products(connection, category="Крепёж", limit=1)
|
|
assert len(by_category) == 1
|
|
|
|
|
|
def test_update_product(connection) -> None:
|
|
add_product(connection, make_product())
|
|
assert update_product(connection, "Болт M10x50 ГОСТ 7798-70", material="Сталь 35")
|
|
|
|
stored = get_product(connection, "Болт M10x50 ГОСТ 7798-70")
|
|
assert stored is not None
|
|
assert stored.material == "Сталь 35"
|
|
assert update_product(connection, "нет такого", name="x") is False
|
|
|
|
|
|
def test_update_rejects_unknown_fields(connection) -> None:
|
|
add_product(connection, make_product())
|
|
with pytest.raises(ValueError, match="Неизвестные поля"):
|
|
update_product(connection, "Болт M10x50 ГОСТ 7798-70", diameter=10)
|
|
|
|
|
|
def test_delete_product(connection) -> None:
|
|
add_product(connection, make_product())
|
|
assert delete_product(connection, "Болт M10x50 ГОСТ 7798-70") is True
|
|
assert delete_product(connection, "Болт M10x50 ГОСТ 7798-70") is False
|
|
assert count_products(connection) == 0
|
|
|
|
|
|
def test_list_with_limit(connection) -> None:
|
|
for index in range(3):
|
|
add_product(connection, make_product(designation=f"Изделие-{index}"))
|
|
assert len(list_products(connection, limit=2)) == 2
|
|
assert len(list_products(connection)) == 3
|
|
|
|
|
|
def test_import_export_roundtrip(connection, tmp_path: Path) -> None:
|
|
add_product(connection, make_product())
|
|
add_product(connection, make_product(designation="Шайба 10 ГОСТ 11371-78", name="Шайба"))
|
|
|
|
exported_path = tmp_path / "export.json"
|
|
assert export_json(connection, exported_path) == 2
|
|
|
|
payload = json.loads(exported_path.read_text(encoding="utf-8"))
|
|
assert len(payload["products"]) == 2
|
|
|
|
init_db(tmp_path / "other.db")
|
|
with connect(tmp_path / "other.db") as other:
|
|
assert import_json(other, exported_path) == 2
|
|
assert count_products(other) == 2
|
|
assert get_product(other, "Шайба 10 ГОСТ 11371-78") is not None
|
|
|
|
|
|
def test_import_accepts_inline_list(connection) -> None:
|
|
imported = import_json(
|
|
connection,
|
|
[{"designation": "Подшипник 6205", "name": "Подшипник шариковый"}],
|
|
)
|
|
assert imported == 1
|
|
assert count_products(connection) == 1
|