import json import pytest from app.flow.messages import DType, MessageSpec, flow_of, qualify def test_port_defaults_to_last_name_segment(): assert MessageSpec(name="temperature").port == "temperature" assert MessageSpec(name="heating.temperature").port == "temperature" assert MessageSpec(name="heating.temperature", port="t").port == "t" def test_int_rejects_bool(): # bool is a subclass of int, but a flag is not a number here. spec = MessageSpec(name="count", dtype=DType.INT) spec.check(3) with pytest.raises(TypeError): spec.check(True) def test_float_accepts_int_but_not_bool(): spec = MessageSpec(name="temp", dtype=DType.FLOAT) spec.check(21) spec.check(21.5) with pytest.raises(TypeError): spec.check(True) with pytest.raises(TypeError): spec.check("21") def test_json_dtype_round_trips(): spec = MessageSpec(name="payload", dtype=DType.JSON) value = {"a": [1, 2], "b": None} spec.check(value) assert json.loads(json.dumps(value)) == value def test_coerce_from_text(): assert MessageSpec(name="a", dtype=DType.FLOAT).coerce("2.5") == 2.5 assert MessageSpec(name="a", dtype=DType.INT).coerce("7") == 7 assert MessageSpec(name="a", dtype=DType.BOOL).coerce("yes") is True assert MessageSpec(name="a", dtype=DType.BOOL).coerce("0") is False def test_spec_serializes(): spec = MessageSpec(name="heating.temp", dtype=DType.FLOAT) assert MessageSpec.model_validate_json(spec.model_dump_json()) == spec def test_qualify_scopes_bare_names_only(): assert qualify("heating", "temp") == "heating.temp" assert qualify("heating", "solar.power") == "solar.power" assert qualify("heating", "") == "" assert flow_of("heating.temp") == "heating"