""" tests/test_node_fields.py CTNode 의 6개 신규 필드(header, footer, prefix, suffix, data_type, value) 테스트 """ import pytest from ctstruct import CTNode, CTTree, NodeType, DataType class TestNewFields: """신규 필드 기본 동작 확인.""" def test_default_values(self): """미지정 시 모두 None / UNSET 이어야 한다.""" node = CTNode(NodeType.PARAGRAPH, "hello") assert node.header is None assert node.footer is None assert node.prefix is None assert node.suffix is None assert node.data_type == DataType.UNSET assert node.value is None def test_set_on_construction(self): """생성자에서 모든 필드를 지정할 수 있어야 한다.""" node = CTNode( node_type = NodeType.PARAGRAPH, content = "본문", header = "== 시작 ==", footer = "== 끝 ==", prefix = "[", suffix = "]", data_type = DataType.STRING, value = "본문", ) assert node.header == "== 시작 ==" assert node.footer == "== 끝 ==" assert node.prefix == "[" assert node.suffix == "]" assert node.data_type == DataType.STRING assert node.value == "본문" def test_set_after_construction(self): """생성 후 직접 속성 할당도 가능해야 한다.""" node = CTNode(NodeType.PARAGRAPH, "숫자") node.header = "머리글" node.footer = "꼬리글" node.prefix = "₩" node.suffix = "원" node.data_type = DataType.INTEGER node.value = 42 assert node.value == 42 assert node.data_type == DataType.INTEGER def test_value_types(self): """value 필드는 어떤 파이썬 타입도 허용해야 한다.""" for val, dtype in [ (123, DataType.INTEGER), (3.14, DataType.FLOAT), (True, DataType.BOOLEAN), (None, DataType.NULL), ([1, 2, 3], DataType.LIST), ({"k": "v"}, DataType.DICT), ("텍스트", DataType.STRING), ]: node = CTNode(NodeType.CUSTOM, data_type=dtype, value=val) assert node.value == val assert node.data_type == dtype class TestFullText: """full_text() — header/prefix/content/suffix/footer 조합 확인.""" def test_content_only(self): node = CTNode(NodeType.PARAGRAPH, "hello") assert node.full_text() == "hello" def test_prefix_suffix(self): node = CTNode(NodeType.PARAGRAPH, "world", prefix="(", suffix=")") assert node.full_text() == "(world)" def test_header_footer(self): node = CTNode(NodeType.PARAGRAPH, "body", header="HEAD\n", footer="\nFOOT") assert node.full_text() == "HEAD\nbody\nFOOT" def test_all_fields(self): node = CTNode( NodeType.PARAGRAPH, content = "내용", header = "H|", footer = "|F", prefix = "→", suffix = "←", ) assert node.full_text() == "H|→내용←|F" def test_none_fields_skipped(self): """None 필드는 빈 문자열로 취급되지 않고 생략되어야 한다.""" node = CTNode(NodeType.PARAGRAPH, content=None, prefix=">>") assert node.full_text() == ">>" class TestSerialization: """to_dict / from_dict 에 신규 필드가 포함되는지 확인.""" def _make_node(self) -> CTNode: return CTNode( NodeType.PARAGRAPH, content = "직렬화 테스트", header = "머리글", footer = "꼬리글", prefix = ">>", suffix = "<<", data_type = DataType.MARKDOWN, value = "**bold**", ) def test_to_dict_contains_fields(self): tree = CTTree() tree.root.append(self._make_node()) d = tree.to_dict() child = d["children"][0] assert child["header"] == "머리글" assert child["footer"] == "꼬리글" assert child["prefix"] == ">>" assert child["suffix"] == "<<" assert child["data_type"] == DataType.MARKDOWN.value assert child["value"] == "**bold**" def test_roundtrip(self): """직렬화 → 역직렬화 후 모든 신규 필드가 보존되어야 한다.""" tree = CTTree() tree.root.append(self._make_node()) restored = CTTree.from_dict(tree.to_dict()) para = restored.find(NodeType.PARAGRAPH)[0] assert para.header == "머리글" assert para.footer == "꼬리글" assert para.prefix == ">>" assert para.suffix == "<<" assert para.data_type == DataType.MARKDOWN assert para.value == "**bold**" def test_unknown_data_type_falls_back_to_custom(self): """알 수 없는 data_type 문자열은 CUSTOM 으로 폴백되어야 한다.""" d = { "id": "test0001", "type": "PARAGRAPH", "content": "?", "attributes": {}, "header": None, "footer": None, "prefix": None, "suffix": None, "data_type": "not_a_real_type", "value": None, "children": [], } tree = CTTree() tree.root = CTTree.from_dict(d).root # ROOT 복원 확인 — 타입은 ROOT가 됨 # 직접 역직렬화 테스트 from ctstruct.types import DataType as DT from ctstruct.node import CTNode as CN from ctstruct.types import NodeType as NT raw_dtype = d.get("data_type", DT.UNSET.value) try: dtype = DT(raw_dtype) except ValueError: dtype = DT.CUSTOM assert dtype == DT.CUSTOM class TestPretty: """pretty() 출력에 신규 필드가 표시되는지 확인.""" def test_pretty_shows_new_fields(self): node = CTNode( NodeType.PARAGRAPH, content = "텍스트", header = "H", prefix = "P", data_type = DataType.STRING, value = "v", ) p = node.pretty() assert "header='H'" in p assert "prefix='P'" in p assert "value='v'" in p def test_dtype_shown_when_set(self): node = CTNode(NodeType.PARAGRAPH, data_type=DataType.INTEGER, value=7) assert "dtype=integer" in node.pretty() def test_dtype_hidden_when_unset(self): node = CTNode(NodeType.PARAGRAPH, "hi") assert "dtype" not in node.pretty()