MakerFLOSS/tests/test_gen_rack.py

277 lines
8.6 KiB
Python
Raw 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.

import pytest
import gen_rack
def item(**kw):
base = {"hostname": "x", "kind": "server", "status": "in-use", "rack": "rack01"}
base.update(kw)
return base
def test_validate_accepts_valid_placement():
gen_rack.validate_item(item(rack_u=12, u_height=2, rack_face="front"))
def test_validate_rejects_u_overflow():
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_item(item(rack_u=47, u_height=3, rack_face="front"))
def test_validate_rejects_u_below_one():
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_item(item(rack_u=0, u_height=1, rack_face="front"))
def test_validate_rejects_bad_face():
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_item(item(rack_u=1, u_height=1, rack_face="sideways"))
def test_validate_rejects_zero_u_with_units():
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_item(item(rack_face="left", rack_u=1, u_height=1))
def test_validate_accepts_zero_u_rail():
gen_rack.validate_item(item(rack_face="left"))
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_validate_rejects_empty_rack():
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_item(item(rack=None, rack_u=1, u_height=1, 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
def test_render_svg_has_two_columns_of_48_slots():
svg = gen_rack.render_svg("rack01", [])
# one faint slot rect per U per column (front + rear)
assert svg.count('fill="#f5f5f5"') == 2 * gen_rack.RACK_UNITS
assert svg.startswith("<svg")
assert svg.rstrip().endswith("</svg>")
def test_render_svg_includes_device_label():
items = [item(hostname="mf00", rack_u=1, u_height=2, rack_face="front")]
svg = gen_rack.render_svg("rack01", items)
assert "mf00" in svg
assert "U1" in svg
def test_render_svg_is_deterministic():
items = [
item(hostname="b", rack_u=3, u_height=1, rack_face="front"),
item(hostname="a", rack_u=1, u_height=1, rack_face="rear"),
]
assert gen_rack.render_svg("rack01", items) == gen_rack.render_svg(
"rack01", list(reversed(items))
)
def test_render_page_has_banner_image_and_table():
items = [item(hostname="mf00", rack_u=1, u_height=2, rack_face="front")]
page = gen_rack.render_page("rack01", items)
assert "do not edit by hand" in page
assert "![Rack rack01 elevation](rack01-elevation.svg)" in page
assert "../../hardware/mf00.md" in page
assert "U1U2" in page
def _write_item(d, name, body):
(d / f"{name}.md").write_text(body, encoding="utf-8")
def test_generate_writes_artifacts(tmp_path):
hw = tmp_path / "hardware"
out = tmp_path / "out"
hw.mkdir()
_write_item(
hw,
"mf00",
"---\nhostname: mf00\nkind: server\nstatus: in-use\n"
"rack: rack01\nrack_u: 1\nu_height: 1\nrack_face: front\n---\n",
)
# a non-rack file must be ignored
_write_item(hw, "cloud", "---\nhostname: cloud\nkind: server\nstatus: in-use\n---\n")
rc = gen_rack.generate(hw, out)
assert rc == 0
assert (out / "rack01.md").exists()
assert (out / "rack01-elevation.svg").exists()
assert "mf00" in (out / "rack01-elevation.svg").read_text()
def test_render_svg_draws_zero_u_rail():
items = [item(hostname="pdu01", kind="pdu", rack_face="left")]
svg = gen_rack.render_svg("rack01", items)
assert "pdu01" in svg
assert "rotate(-90" in svg
def test_render_svg_both_face_draws_in_both_columns():
items = [item(hostname="dev", rack_u=10, u_height=1, rack_face="both")]
svg = gen_rack.render_svg("rack01", items)
assert svg.count("dev (U10)") == 2
def test_generate_writes_one_pair_per_rack(tmp_path):
hw = tmp_path / "hardware"
out = tmp_path / "out"
hw.mkdir()
_write_item(
hw,
"a",
"---\nhostname: a\nkind: server\nstatus: in-use\n"
"rack: rack01\nrack_u: 1\nu_height: 1\nrack_face: front\n---\n",
)
_write_item(
hw,
"b",
"---\nhostname: b\nkind: server\nstatus: in-use\n"
"rack: rack02\nrack_u: 1\nu_height: 1\nrack_face: front\n---\n",
)
rc = gen_rack.generate(hw, out)
assert rc == 0
assert (out / "rack01.md").exists() and (out / "rack02.md").exists()
assert (out / "rack01-elevation.svg").exists()
assert (out / "rack02-elevation.svg").exists()
def test_generate_returns_1_on_overlap(tmp_path):
hw = tmp_path / "hardware"
out = tmp_path / "out"
hw.mkdir()
for n, u in (("a", 1), ("b", 1)):
_write_item(
hw,
n,
f"---\nhostname: {n}\nkind: server\nstatus: in-use\n"
f"rack: rack01\nrack_u: {u}\nu_height: 1\nrack_face: front\n---\n",
)
rc = gen_rack.generate(hw, out)
assert rc == 1
assert not (out / "rack01.md").exists()
def test_validate_power_accepts_valid_feed():
items = [
item(hostname="pdu01", kind="pdu", rack_face="left", outlets=8),
item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=[{"pdu": "pdu01", "outlet": 1}]),
]
gen_rack.validate_power(items)
def test_validate_power_rejects_unknown_pdu():
items = [item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=[{"pdu": "ghost", "outlet": 1}])]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_validate_power_rejects_non_pdu_target():
items = [
item(hostname="sw01", kind="switch", rack_u=1, u_height=1,
rack_face="front"),
item(hostname="mf00", rack_u=2, u_height=1, rack_face="front",
power=[{"pdu": "sw01", "outlet": 1}]),
]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_validate_power_rejects_outlet_over_count():
items = [
item(hostname="pdu01", kind="pdu", rack_face="left", outlets=8),
item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=[{"pdu": "pdu01", "outlet": 9}]),
]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_validate_power_rejects_outlet_zero():
items = [
item(hostname="pdu01", kind="pdu", rack_face="left", outlets=8),
item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=[{"pdu": "pdu01", "outlet": 0}]),
]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_validate_power_rejects_malformed_entry():
items = [
item(hostname="pdu01", kind="pdu", rack_face="left", outlets=8),
item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=["pdu01"]),
]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_validate_power_rejects_pdu_without_outlets():
items = [
item(hostname="pdu01", kind="pdu", rack_face="left"),
item(hostname="mf00", rack_u=1, u_height=1, rack_face="front",
power=[{"pdu": "pdu01", "outlet": 1}]),
]
with pytest.raises(gen_rack.SchemaError):
gen_rack.validate_power(items)
def test_generate_returns_1_on_bad_power_ref(tmp_path):
hw = tmp_path / "hardware"
out = tmp_path / "out"
hw.mkdir()
_write_item(
hw,
"mf00",
"---\nhostname: mf00\nkind: server\nstatus: in-use\n"
"rack: rack01\nrack_u: 1\nu_height: 1\nrack_face: front\n"
"power:\n - { pdu: ghost, outlet: 1 }\n---\n",
)
rc = gen_rack.generate(hw, out)
assert rc == 1
assert not (out / "rack01.md").exists()