"""Unit tests for the Patreon ProseMirror→HTML body converter (#842). Patreon moved the post body from the flat `content` HTML field (now null) to `content_json_string` (a ProseMirror doc). PURE module → no DB, fast lane. """ from __future__ import annotations import json from backend.app.utils.prosemirror import post_body_html, prosemirror_to_html def _doc(*nodes) -> str: return json.dumps({"type": "doc", "content": list(nodes)}) def test_paragraph_text_and_hardbreak(): doc = _doc({ "type": "paragraph", "content": [ {"type": "text", "text": "Hi there"}, {"type": "hardBreak"}, {"type": "text", "text": "line two"}, ], }) assert prosemirror_to_html(doc) == "

Hi there
line two

" def test_marks_bold_italic_link(): doc = _doc({ "type": "paragraph", "content": [ {"type": "text", "marks": [{"type": "bold"}], "text": "B"}, {"type": "text", "marks": [{"type": "italic"}], "text": "I"}, { "type": "text", "marks": [{"type": "link", "attrs": {"href": "https://x.test/a"}}], "text": "L", }, ], }) assert prosemirror_to_html(doc) == ( '

BIL

' ) def test_image_node_emits_img_with_cdn_src(): doc = _doc({ "type": "image", "attrs": {"src": "https://cdn.test/p/post/1/abc/1.gif?token-hash=z", "alt": None}, }) out = prosemirror_to_html(doc) assert out == '' def test_lists_and_heading(): doc = _doc( {"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Status"}]}, {"type": "bulletList", "content": [ {"type": "listItem", "content": [ {"type": "paragraph", "content": [{"type": "text", "text": "one"}]}]}, ]}, ) assert prosemirror_to_html(doc) == ( "

Status

" ) def test_text_is_html_escaped(): doc = _doc({"type": "paragraph", "content": [{"type": "text", "text": "a < b & c > d"}]}) assert prosemirror_to_html(doc) == "

a < b & c > d

" def test_unknown_node_renders_children_not_dropped(): doc = _doc({"type": "weirdBlock", "content": [ {"type": "paragraph", "content": [{"type": "text", "text": "keep me"}]}]}) assert prosemirror_to_html(doc) == "

keep me

" def test_empty_and_unparseable_return_none(): assert prosemirror_to_html(None) is None assert prosemirror_to_html("") is None assert prosemirror_to_html(" ") is None assert prosemirror_to_html("{not json") is None assert prosemirror_to_html(json.dumps({"type": "doc", "content": []})) is None def test_post_body_html_prefers_legacy_content(): attrs = {"content": "

legacy

", "content_json_string": _doc({"type": "paragraph", "content": [{"type": "text", "text": "new"}]})} assert post_body_html(attrs) == "

legacy

" def test_post_body_html_falls_back_to_json_string(): attrs = {"content": None, "content_json_string": _doc({"type": "paragraph", "content": [{"type": "text", "text": "new"}]})} assert post_body_html(attrs) == "

new

" def test_post_body_html_none_when_both_empty(): assert post_body_html({"content": None, "content_json_string": None}) is None assert post_body_html({}) is None assert post_body_html(None) is None