201 lines
6.6 KiB
Python
201 lines
6.6 KiB
Python
"""Standalone-тесты boltgen — ПСЕВДОНЕЗАВИСИМЫЕ от cadquery.
|
||
|
||
Проверяют логику спецификаций (standard.py) и схему каталога/product_params,
|
||
не импортируя model.py (который тянет cadquery). Отдельно — проверка сгенерированных
|
||
STEP-файлов лежит в test_geometry.py, который падает «skip» при отсутствии cadquery.
|
||
|
||
Нужны для того, чтобы `pytest` проходился на хостах без cadquery (тесты спецификаций),
|
||
а тяжёлый экспорт модели — только когда cadquery доступен.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import math
|
||
import os
|
||
import sys
|
||
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||
|
||
import pytest # noqa: F401
|
||
|
||
from boltgen.standard import ( # noqa: E402
|
||
BOLT_ALLOWED_LENGTHS,
|
||
BOLT_SIZES,
|
||
NUT_SIZES,
|
||
WASHER_SIZES,
|
||
BoltSpec,
|
||
NutSpec,
|
||
WasherSpec,
|
||
bolt_designation,
|
||
get_bolt,
|
||
get_nut,
|
||
get_washer,
|
||
iter_bolts,
|
||
iter_nuts,
|
||
iter_washers,
|
||
model_filename,
|
||
product_params,
|
||
washer_designation,
|
||
)
|
||
|
||
|
||
# =====Стандартные размерные данные ======
|
||
|
||
|
||
def test_size_tables_present_and_consistent():
|
||
for kind in (BOLT_SIZES, NUT_SIZES, WASHER_SIZES):
|
||
assert len(kind) >= 7
|
||
# диаметры совпадают между таблицами болт/гайка/шайба по размеру
|
||
for s in BOLT_SIZES:
|
||
assert BOLT_SIZES[s]["d"] == NUT_SIZES[s]["d"]
|
||
assert s in WASHER_SIZES
|
||
|
||
|
||
def test_bolt_geometry_monotonic():
|
||
# размер под ключ и высота головки растут с размером
|
||
sizes = sorted(BOLT_SIZES, key=lambda s: int(s[1:]))
|
||
prev_s = prev_k = 0.0
|
||
for s in sizes:
|
||
cur_s = BOLT_SIZES[s]["S"]
|
||
cur_k = BOLT_SIZES[s]["k"]
|
||
assert cur_s > prev_s or cur_s == prev_s
|
||
assert cur_k >= prev_k
|
||
prev_s, prev_k = cur_s, cur_k
|
||
|
||
|
||
def test_nut_washer_geometry_reasonable():
|
||
for s, n in NUT_SIZES.items():
|
||
# высота гайки меньше размера под ключ
|
||
assert n["k"] < n["S"]
|
||
for s, w in WASHER_SIZES.items():
|
||
# внутренний диаметр < внешнего, толщина положительная
|
||
assert w["d"] < w["D"]
|
||
assert w["S"] > 0
|
||
|
||
|
||
def test_m6_short_lengths():
|
||
# M6 по ГОСТ 7805-70 только до 60
|
||
lengths = [spec.length for spec in iter_bolts() if spec.size == "M6"]
|
||
assert lengths == sorted(lengths)
|
||
assert all(l <= 60 for l in lengths)
|
||
assert all(l in BOLT_ALLOWED_LENGTHS for l in lengths)
|
||
|
||
|
||
# =====Спецификации и валидация ======
|
||
|
||
|
||
def test_get_bolt_roundtrip():
|
||
spec = get_bolt("M12", 45)
|
||
assert isinstance(spec, BoltSpec)
|
||
assert spec.size == "M12"
|
||
assert spec.length == 45
|
||
|
||
|
||
def test_get_bolt_invalid_size():
|
||
with pytest.raises(ValueError):
|
||
get_bolt("M99", 40)
|
||
|
||
|
||
def test_get_bolt_invalid_length():
|
||
with pytest.raises(ValueError):
|
||
get_bolt("M10", 33) # 33 не в BOLT_ALLOWED_LENGTHS
|
||
|
||
|
||
def test_get_nut_washer_invalid():
|
||
with pytest.raises(ValueError):
|
||
get_nut("M99")
|
||
with pytest.raises(ValueError):
|
||
get_washer("M99")
|
||
|
||
|
||
# =====Итераторы (полные семейства) ======
|
||
|
||
|
||
def test_iter_counts():
|
||
bolt_count = sum(1 for _ in iter_bolts())
|
||
nut_count = sum(1 for _ in iter_nuts())
|
||
washer_count = sum(1 for _ in iter_washers())
|
||
# M6×7 + M8,M10,M12,M16,M20,M24,M27,M30 × 20 = 7 + 160 = 167 болтов + 9 гаек + 9 шайб
|
||
assert bolt_count == 167
|
||
assert nut_count == 9
|
||
assert washer_count == 9
|
||
assert bolt_count + nut_count + washer_count == 185
|
||
|
||
|
||
def test_iter_unique_bolt_ids():
|
||
ids = {(b.size, b.length) for b in iter_bolts()}
|
||
assert len(ids) == sum(1 for _ in iter_bolts()) # без дубликатov
|
||
|
||
|
||
# =====Дизигнации и model_filename ======
|
||
|
||
|
||
def test_bolt_designation_format():
|
||
# bolt_designation/nut_designation/washer_designation — базовое наименование;
|
||
# составное обозначение по ГОСТ 1759.0-87 (класс, стандарты, покрытие) собирает _*_params.
|
||
assert bolt_designation("M12", 45) == "Болт М12×45"
|
||
assert bolt_designation("M10", 20) == "Болт М10×20"
|
||
|
||
|
||
def test_washer_designation_format():
|
||
# шайба по номиналу резьбы: M12 -> 13
|
||
assert washer_designation("M12") == "Шайба 13"
|
||
|
||
|
||
def test_model_filename_types():
|
||
assert model_filename(get_bolt("M12", 45)) == "bolt_M12_45.step"
|
||
assert model_filename(get_nut("M12")) == "nut_M12.step"
|
||
assert model_filename(get_washer("M12")) == "washer_M12.step"
|
||
|
||
|
||
# =====product_params / схема каталога ======
|
||
|
||
|
||
def test_bolt_product_params():
|
||
p = product_params(get_bolt("M12", 45))
|
||
assert p["type"] == "bolt"
|
||
assert p["category"] == "Крепёж"
|
||
assert p["standard"] == "ГОСТ 7805-70"
|
||
assert p["material"] == "Сталь 8.8"
|
||
assert p["attributes"]["диаметр"] == "12"
|
||
assert p["attributes"]["длина"] == 45
|
||
assert p["attributes"]["размер_под_ключ"] == 18.0
|
||
|
||
|
||
def test_nut_product_params():
|
||
p = product_params(get_nut("M12"))
|
||
assert p["type"] == "nut"
|
||
assert p["standard"] == "ГОСТ 5915-70"
|
||
assert p["material"] == "Сталь 8"
|
||
|
||
|
||
def test_washer_product_params():
|
||
p = product_params(get_washer("M12"))
|
||
assert p["type"] == "washer"
|
||
assert p["standard"] == "ГОСТ 11371-78"
|
||
assert p["attributes"]["диаметр_наружный"] == 24.0
|
||
|
||
|
||
def test_all_bolt_params_have_required_keys():
|
||
required = {"designation", "type", "category", "standard", "material", "attributes"}
|
||
for spec in iter_bolts():
|
||
p = product_params(spec)
|
||
assert required.issubset(p.keys())
|
||
assert p["designation"].startswith("Болт ")
|
||
|
||
|
||
# =====Критические гостовские допуски ======
|
||
|
||
|
||
def test_bolt_key_size_upper_bound():
|
||
# размер под ключ не превышает диаметр + ~25 (защита от дурака в данных)
|
||
for s, b in BOLT_SIZES.items():
|
||
assert b["S"] <= float(s[1:]) + 25
|
||
|
||
|
||
def test_washer_hole_vs_outer_clearance_ratio():
|
||
# внутреннее отверстие заметно меньше внешнего (плоская шайба)
|
||
for s, w in WASHER_SIZES.items():
|
||
ratio = w["d"] / w["D"]
|
||
assert 0.3 < ratio < 0.75
|