diff --git a/scripts/gen_rack.py b/scripts/gen_rack.py index a3e65d5..1b01fc7 100644 --- a/scripts/gen_rack.py +++ b/scripts/gen_rack.py @@ -94,3 +94,24 @@ def validate_item(fm: dict) -> None: raise SchemaError( f"{name}: occupies U{u}..U{u + h - 1}, exceeds {RACK_UNITS}U" ) + + +def check_overlaps(items: list[dict]) -> None: + """Raise if two items share a U on the same face within one rack.""" + occupied: dict[tuple[str, int], str] = {} + for fm in items: + face = fm.get("rack_face") + if face in ZERO_U_FACES: + continue + faces = ("front", "rear") if face == "both" else (face,) + u = fm["rack_u"] + h = fm["u_height"] + name = fm.get("hostname", "?") + for f in faces: + for uu in range(u, u + h): + key = (f, uu) + if key in occupied: + raise SchemaError( + f"U{uu} {f}: {name} overlaps {occupied[key]}" + ) + occupied[key] = name diff --git a/tests/test_gen_rack.py b/tests/test_gen_rack.py index 724cb59..3829512 100644 --- a/tests/test_gen_rack.py +++ b/tests/test_gen_rack.py @@ -40,3 +40,37 @@ def test_validate_accepts_zero_u_rail(): def test_validate_rejects_missing_units_on_faced_item(): with pytest.raises(gen_rack.SchemaError): gen_rack.validate_item(item(rack_face="front")) + + +def test_overlaps_detects_same_face_overlap(): + items = [ + item(hostname="a", rack_u=1, u_height=2, rack_face="front"), + item(hostname="b", rack_u=2, u_height=1, rack_face="front"), + ] + with pytest.raises(gen_rack.SchemaError): + gen_rack.check_overlaps(items) + + +def test_overlaps_allows_same_u_different_face(): + items = [ + item(hostname="a", rack_u=5, u_height=1, rack_face="front"), + item(hostname="b", rack_u=5, u_height=1, rack_face="rear"), + ] + gen_rack.check_overlaps(items) # no raise + + +def test_overlaps_both_face_conflicts_with_front(): + items = [ + item(hostname="a", rack_u=5, u_height=1, rack_face="both"), + item(hostname="b", rack_u=5, u_height=1, rack_face="front"), + ] + with pytest.raises(gen_rack.SchemaError): + gen_rack.check_overlaps(items) + + +def test_overlaps_ignores_zero_u_rails(): + items = [ + item(hostname="p1", rack_face="left"), + item(hostname="p2", rack_face="left"), + ] + gen_rack.check_overlaps(items) # no raise