Files
fastengen/tests/test_boltgen.py
OpenClaw Coder e407d59b13
CI / quality (push) Canceled after 0s
CI / test (3.10) (push) Canceled after 0s
CI / test (3.11) (push) Canceled after 0s
CI / test (3.12) (push) Canceled after 0s
CI / build-check (push) Canceled after 0s
chore(P4): ruff lint, coverage/CI, README overhaul
- ruff profile in pyproject (E/W/F/I/B/RUF); clean src/ + tests/
- remove dead code: threaded_len/s/d/coat/first_rec_for_size, self.cat in gui
- 59 public re-exports added to __all__ (sorted)
- renamed l->ln (E741), zip(strict=True), split long lines, [*list, x] (RUF005)
- CI: quality job (ruff), test runs tests/ -k 'not gen_all' w/ coverage
- README rewritten (commands, API modules, tests/lint/coverage, CI)
- docs/tasks.md: P4 recorded
- 136 passed, 3 deselected (regen); ruff: all checks passed
2026-11-11 16:30:07 +10:00

229 lines
7.3 KiB
Python
Raw Permalink 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.
"""Тесты для fastengen — генератор крепёжа с STEP-библиотекой и каталогом."""
from __future__ import annotations
import os
import sys
from contextlib import contextmanager
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
import pytest
from fastengen import PortableCatalog, get_bolt, get_nut, get_washer
from fastengen.standard import (
BOLT_ALLOWED_LENGTHS,
BOLT_SIZES,
NUT_SIZES,
WASHER_SIZES,
)
# =====Стандарты ======
def test_bolt_sizes_present():
assert "M6" in BOLT_SIZES and "M24" in BOLT_SIZES
assert BOLT_SIZES["M10"]["d"] == 10.0
def test_nut_washer_sizes():
assert len(NUT_SIZES) == 9
assert len(WASHER_SIZES) == 9
def test_invalid_bolt_size():
with pytest.raises(ValueError):
get_bolt("M99", 40)
def test_invalid_length():
with pytest.raises(ValueError):
get_bolt("M10", 33) # 33不在 BOLT_ALLOWED_LENGTHS
def test_valid_lengths():
assert all(ln in BOLT_ALLOWED_LENGTHS for ln in (20, 25, 120))
# =====Генерация STEP ======
@pytest.mark.parametrize("size,length", [("M6", 20), ("M10", 40), ("M16", 80), ("M24", 120)])
def test_generate_bolt_step(size, length, tmp_path):
spec = get_bolt(size, length)
out = str(tmp_path / f"bolt_{size}_{length}.step")
PortableCatalog(str(tmp_path / "cat.json"), models_dir=str(tmp_path)).add_bolt(spec)
assert os.path.exists(out)
assert os.path.getsize(out) > 1000 # не пустой STEP
@pytest.mark.parametrize("size", list(NUT_SIZES))
def test_generate_nut_step(size, tmp_path):
spec = get_nut(size)
out = str(tmp_path / f"nut_{size}.step")
PortableCatalog(str(tmp_path / "cat.json"), models_dir=str(tmp_path)).add_nut(spec)
assert os.path.exists(out)
assert os.path.getsize(out) > 1000
@pytest.mark.parametrize("size", list(WASHER_SIZES))
def test_generate_washer_step(size, tmp_path):
spec = get_washer(size)
out = str(tmp_path / f"washer_{size}.step")
PortableCatalog(str(tmp_path / "cat.json"), models_dir=str(tmp_path)).add_washer(spec)
assert os.path.exists(out)
assert os.path.getsize(out) > 1000
# =====Каталог ======
def test_catalog_roundtrip(tmp_path):
cat_path = str(tmp_path / "catalog.json")
models_dir = str(tmp_path / "out")
cat = PortableCatalog(cat_path, models_dir=models_dir)
# добавляем
bolt = cat.add_bolt(get_bolt("M12", 45))
cat.add_nut(get_nut("M12"))
cat.add_washer(get_washer("M12"))
cat.save()
# размер каталога
assert len(cat) == 3
assert "bolt_M12_45" in cat
assert cat["bolt_M12_45"].designation == (
"Болт М12×45 8.8 ГОСТ 1759.0-87 ГОСТ 7805-70 Zn (225) ГОСТ 1759.2-76"
)
assert bolt.model_file.endswith(".step")
assert bolt.attributes["диаметр"] == "12"
# загрузка заново
cat2 = PortableCatalog(cat_path, models_dir=models_dir)
assert len(cat2) == 3
assert cat2["nut_M12"].type == "nut"
assert cat2["washer_M12"].standard == "ГОСТ 11371-78"
def test_gen_all(tmp_path):
cat_path = str(tmp_path / "catalog.json")
models_dir = str(tmp_path / "out")
cat = PortableCatalog(cat_path, models_dir=models_dir)
recs = cat.add_all()
# 167 болтов + 9 гаек + 9 шайб = 185
assert len(cat) == 167 + 9 + 9
assert len(recs) == 185
assert cat_path.endswith("catalog.json")
# все модели существуют
for r in cat:
assert os.path.exists(os.path.join(models_dir, r.model_file))
# =====CLI ======
def test_cli_bolt_gen(tmp_path):
from fastengen.cli import main
with chdir(tmp_path):
assert main(["bolt-gen", "M10", "40", "--dir", "out"]) == 0
assert os.path.exists(str(tmp_path / "out" / "bolt_M10_40.step"))
assert os.path.exists(str(tmp_path / "catalog.json"))
def test_cli_gen_all(tmp_path):
from fastengen.cli import main
with chdir(tmp_path):
assert main(["gen-all", "--dir", "out"]) == 0
cat_path = tmp_path / "catalog.json"
assert cat_path.exists()
def test_cli_show_and_list(tmp_path):
from fastengen.cli import main
with chdir(tmp_path):
main(["gen-all", "--dir", "out"])
assert main(["list"]) == 0
assert main(["show", "bolt_M10_40"]) == 0
def test_cli_single_gen_saves_to_catalog(tmp_path):
"""Одинарные *-gen команды должны сохранять запись в каталог (fix list/show)."""
from fastengen.cli import main
with chdir(tmp_path):
assert main(["nut-gen", "M12", "--dir", "out", "--catalog", "cat.json"]) == 0
assert main(["washer-gen", "M12", "--dir", "out", "--catalog", "cat.json"]) == 0
assert main(["bolt-set-gen", "M10", "40", "--dir", "out", "--catalog", "cat.json"]) == 0
assert main(["washer-l-gen", "M12", "--dir", "out", "--catalog", "cat.json"]) == 0
# каталог теперь не пустой и показывает записи
assert main(["list", "--catalog", "cat.json"]) == 0
assert main(["show", "nut_M12", "--catalog", "cat.json"]) == 0
assert main(["show", "boltset_M10_40", "--catalog", "cat.json"]) == 0
assert main(["show", "washer_M12_large", "--catalog", "cat.json"]) == 0
def test_gen_all_extended(tmp_path):
"""add_all_extended генерирует базовые + все расшиленные семейства без исключений."""
from fastengen.cli import main
with chdir(tmp_path):
assert main(["gen-all-extended", "--dir", "out", "--catalog", "cat.json"]) == 0
# каталог должен содержать записи всех семейств
assert main(["list", "--catalog", "cat.json"]) == 0
def test_add_all_extended_covers_all_types(tmp_path):
"""Каждый новый тип генерирует валидную STEP-модель (по одной на тип)."""
import os
from fastengen import PortableCatalog
from fastengen.standard import (
get_bolt_high,
get_nut_baran,
get_nut_high,
get_nut_specing,
get_screw_potay,
get_self_tacker_gost,
get_stud_long,
get_washer_grever,
get_washer_l,
get_washer_stop,
)
cat = PortableCatalog(str(tmp_path / "cat.json"), models_dir=str(tmp_path / "out"))
cat.add_bolt_high(get_bolt_high("M12", 45))
cat.add_screw_potay(get_screw_potay("M10", 40))
cat.add_nut_high(get_nut_high("M12"))
cat.add_nut_baran(get_nut_baran("M12"))
cat.add_nut_specing(get_nut_specing("M12"))
cat.add_washer_grever(get_washer_grever("M12"))
cat.add_washer_stop(get_washer_stop("M12"))
cat.add_stud_long(get_stud_long("M12", 80))
cat.add_self_tacker_gost(get_self_tacker_gost("4.2", 30))
cat.add_washer_l(get_washer_l("M12"))
for r in cat:
pth = os.path.join(str(tmp_path / "out"), r.model_file)
assert os.path.exists(pth) and os.path.getsize(pth) > 1000, r.model_file
assert len(cat) == 10
def test_cli_error(tmp_path):
from fastengen.cli import main
with chdir(tmp_path):
assert main(["bolt-gen", "M99", "40"]) == 2 # invalid size
@contextmanager
def chdir(d):
cwd = os.getcwd()
os.chdir(d)
try:
yield
finally:
os.chdir(cwd)