Spaces:
Running
Running
File size: 6,043 Bytes
85fcbc2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | """Tests for product configuration validation."""
import pytest
# Required top-level sections
REQUIRED_SECTIONS = ["product", "design", "costs", "pricing", "return_assumptions", "environmental"]
# Required product fields
REQUIRED_PRODUCT_FIELDS = ["name", "category", "retail_price"]
# Required design fields
REQUIRED_DESIGN_FIELDS = ["lifetime_years", "use_cycle_years", "max_cycles"]
# Required cost fields
REQUIRED_COST_FIELDS = ["manufacturing_unit_cost"]
class TestConfigStructure:
"""Test that configs have required structure."""
def test_besafe_has_required_sections(self, besafe_config):
"""BeSafe config has all required sections."""
for section in REQUIRED_SECTIONS:
assert section in besafe_config, f"Missing section: {section}"
def test_hamax_has_required_sections(self, hamax_config):
"""Hamax config has all required sections."""
for section in REQUIRED_SECTIONS:
assert section in hamax_config, f"Missing section: {section}"
def test_all_configs_have_required_sections(self, all_configs):
"""All product configs have required sections."""
for name, config in all_configs.items():
for section in REQUIRED_SECTIONS:
assert section in config, f"{name}: Missing section: {section}"
class TestProductSection:
"""Test product section validation."""
def test_product_has_required_fields(self, all_configs):
"""Product section has required fields."""
for name, config in all_configs.items():
for field in REQUIRED_PRODUCT_FIELDS:
assert field in config["product"], f"{name}: Missing product.{field}"
def test_retail_price_positive(self, all_configs):
"""Retail price is positive."""
for name, config in all_configs.items():
price = config["product"]["retail_price"]
assert price > 0, f"{name}: retail_price must be positive, got {price}"
class TestDesignSection:
"""Test design section validation."""
def test_design_has_required_fields(self, all_configs):
"""Design section has required fields."""
for name, config in all_configs.items():
for field in REQUIRED_DESIGN_FIELDS:
assert field in config["design"], f"{name}: Missing design.{field}"
def test_lifetime_greater_than_use_cycle(self, all_configs):
"""Design lifetime >= use cycle years."""
for name, config in all_configs.items():
lifetime = config["design"]["lifetime_years"]
use_cycle = config["design"]["use_cycle_years"]
assert lifetime >= use_cycle, f"{name}: lifetime_years ({lifetime}) < use_cycle_years ({use_cycle})"
def test_max_cycles_positive(self, all_configs):
"""Max cycles is positive."""
for name, config in all_configs.items():
cycles = config["design"]["max_cycles"]
assert cycles > 0, f"{name}: max_cycles must be positive, got {cycles}"
class TestCostsSection:
"""Test costs section validation."""
def test_manufacturing_cost_positive(self, all_configs):
"""Manufacturing cost is positive."""
for name, config in all_configs.items():
cost = config["costs"]["manufacturing_unit_cost"]
assert cost > 0, f"{name}: manufacturing_unit_cost must be positive, got {cost}"
def test_refurbishment_costs_exist(self, all_configs):
"""Refurbishment costs exist for grades A, B, C."""
for name, config in all_configs.items():
refurb = config["costs"].get("refurbishment", {})
assert "grade_a" in refurb, f"{name}: Missing refurbishment.grade_a"
assert "grade_b" in refurb, f"{name}: Missing refurbishment.grade_b"
def test_refurb_costs_ascending(self, all_configs):
"""Refurbishment costs: grade_a <= grade_b."""
for name, config in all_configs.items():
refurb = config["costs"]["refurbishment"]
assert refurb["grade_a"] <= refurb["grade_b"], \
f"{name}: grade_a cost ({refurb['grade_a']}) > grade_b ({refurb['grade_b']})"
class TestReturnAssumptions:
"""Test return assumptions validation."""
def test_return_rate_valid_range(self, all_configs):
"""Return rate is between 0 and 1."""
for name, config in all_configs.items():
rate = config["return_assumptions"]["return_rate"]
assert 0 <= rate <= 1, f"{name}: return_rate must be 0-1, got {rate}"
def test_grade_distribution_sums_to_one(self, all_configs):
"""Grade distribution (a + b + c) sums to 1."""
for name, config in all_configs.items():
dist = config["return_assumptions"]["grade_distribution"]
total = dist.get("a", 0) + dist.get("b", 0) + dist.get("c", 0)
assert abs(total - 1.0) < 0.01, f"{name}: grade distribution sums to {total}, not 1.0"
class TestMaterialsBOM:
"""Test materials BOM validation (if present)."""
def test_bom_weights_positive(self, all_configs):
"""BOM component weights are positive."""
for name, config in all_configs.items():
if "materials" in config and "bom" in config["materials"]:
for comp_name, comp in config["materials"]["bom"].items():
weight = comp.get("weight_kg", 0)
assert weight > 0, f"{name}: {comp_name} weight must be positive"
def test_recyclable_materials_have_recovery_value(self, all_configs):
"""Recyclable materials should have recovery value."""
for name, config in all_configs.items():
if "materials" in config and "bom" in config["materials"]:
for comp_name, comp in config["materials"]["bom"].items():
if comp.get("recyclable", False) and "virgin_cost_per_kg" in comp:
assert "recovery_value_per_kg" in comp, \
f"{name}: {comp_name} is recyclable but missing recovery_value_per_kg"
|