"""Domain-model tests: properties, catalogue, relations, ports, serialization.""" from pipeline_editor.model.properties import ( Property, PropertyBag, PropertyGroup, PropertyType, ) from pipeline_editor.model.catalogue import default_registry as default_catalogues from pipeline_editor.model.relations import default_registry as default_relations from pipeline_editor.model.port import Port, PortSide, PortDirection from pipeline_editor.model.node_library import default_library def test_property_coercion_and_bounds(): p = Property("d", "Diameter", PropertyType.FLOAT, "50", minimum=0, maximum=100) assert p.value == 50.0 and isinstance(p.value, float) assert p.set_value(150) is True assert p.value == 100.0 # clamped to max assert p.set_value(100) is False # unchanged assert p.set_value(-5) is True and p.value == 0.0 # clamped to min def test_property_on_change_callback(): seen = [] p = Property("t", "Title", PropertyType.STRING, "a") p.on_change = lambda prop: seen.append(prop.value) p.set_value("b") assert seen == ["b"] def test_property_bag_lookup_and_roundtrip(): bag = PropertyBag([ PropertyGroup("Presentation", [Property("title", "Title", PropertyType.STRING, "N1")]), PropertyGroup("Physics", [Property("len", "Length", PropertyType.FLOAT, 12.5, unit="m")]), ]) assert bag.value("title") == "N1" assert bag.set_value("len", 20) is True restored = PropertyBag.from_dict(bag.to_dict()) assert restored.value("len") == 20.0 assert restored.group("Physics").get("len").unit == "m" def test_all_property_types_roundtrip(): bag = PropertyBag([PropertyGroup("G", [ Property("s", "S", PropertyType.STRING, "x"), Property("i", "I", PropertyType.INT, 3), Property("f", "F", PropertyType.FLOAT, 1.5), Property("b", "B", PropertyType.BOOL, True), Property("e", "E", PropertyType.ENUM, "a", options=["a", "b"]), Property("c", "C", PropertyType.COLOR, "#ff0000"), Property("ci", "CI", PropertyType.CATALOGUE_ITEM, "DN50", catalogue="pipe_classes"), Property("cis", "CIS", PropertyType.CATALOGUE_ITEMS, ["DN50", "DN100"], catalogue="pipe_classes"), Property("vl", "VL", PropertyType.VALUE_LIST, [1, 2, 3]), ])]) r = PropertyBag.from_dict(bag.to_dict()) assert r.value("i") == 3 and r.value("b") is True assert r.value("cis") == ["DN50", "DN100"] assert r.value("vl") == [1, 2, 3] def test_catalogue_resolution(): reg = default_catalogues() item = reg.resolve("pipe_classes", "DN100") assert item is not None and item.attr("diameter_mm") == 100 assert reg.resolve("pipe_classes", "NOPE") is None def test_relation_compatibility_and_color_scheme(): reg = default_relations() assert reg.can_connect("water", "water") is True assert reg.can_connect("water", "hot_water") is True # hot_water compatible with water assert reg.can_connect("water", "power") is False # default color, then switch scheme assert reg.color("water") == "#1e88e5" reg.set_active_scheme("high_contrast") assert reg.color("water") == "#0000ff" def test_port_geometry(): p = Port("out", "water", PortSide.RIGHT, 0.5, direction=PortDirection.OUTPUT) assert p.local_point(80, 40) == (80, 20) assert p.normal() == (1.0, 0.0) top = Port("t", "water", PortSide.TOP, 0.25) assert top.local_point(100, 100) == (25, 0) def test_library_instantiation(): lib = default_library() tmpl = lib.get("pump") node = tmpl.instantiate("n1", 10, 20) assert node.title == "Pump" assert node.properties.group("Physics").get("head").value == 30.0 assert {p.port_id for p in node.ports} == {"in", "out"} # groups exposed for the panel assert "Equipment" in lib.groups()