"""Unit tests for lexical document validator.""" from __future__ import annotations import pytest from blog.bp.blog.ghost.lexical_validator import ( validate_lexical, ALLOWED_NODE_TYPES, ) class TestValidateLexical: def test_valid_empty_doc(self): ok, reason = validate_lexical({"root": {"type": "root", "children": []}}) assert ok is True assert reason is None def test_non_dict_input(self): ok, reason = validate_lexical("not a dict") assert ok is False assert "JSON object" in reason def test_list_input(self): ok, reason = validate_lexical([]) assert ok is False def test_missing_root(self): ok, reason = validate_lexical({"foo": "bar"}) assert ok is False assert "'root'" in reason def test_root_not_dict(self): ok, reason = validate_lexical({"root": "string"}) assert ok is False def test_valid_paragraph(self): doc = {"root": {"type": "root", "children": [ {"type": "paragraph", "children": [ {"type": "text", "text": "hello"} ]} ]}} ok, _ = validate_lexical(doc) assert ok is True def test_disallowed_type(self): doc = {"root": {"type": "root", "children": [ {"type": "script"} ]}} ok, reason = validate_lexical(doc) assert ok is False assert "Disallowed node type: script" in reason def test_nested_disallowed_type(self): doc = {"root": {"type": "root", "children": [ {"type": "paragraph", "children": [ {"type": "list", "children": [ {"type": "evil-widget"} ]} ]} ]}} ok, reason = validate_lexical(doc) assert ok is False assert "evil-widget" in reason def test_node_without_type_allowed(self): """Nodes with type=None are allowed by _walk.""" doc = {"root": {"type": "root", "children": [ {"children": []} # no "type" key ]}} ok, _ = validate_lexical(doc) assert ok is True def test_all_allowed_types(self): """Every type in the allowlist should pass.""" for node_type in ALLOWED_NODE_TYPES: doc = {"root": {"type": "root", "children": [ {"type": node_type, "children": []} ]}} ok, reason = validate_lexical(doc) assert ok is True, f"{node_type} should be allowed but got: {reason}" def test_allowed_types_count(self): """Sanity: at least 30 types in the allowlist.""" assert len(ALLOWED_NODE_TYPES) >= 30