# retoor import types import devplacepy.seo as seo import devplacepy.attachments as attach def _seo_request(): return types.SimpleNamespace( base_url="https://x.test/", url=types.SimpleNamespace(path="/posts/abc"), query_params={}, ) def test_truncate_no_overflow(): assert len(seo.truncate("x" * 200)) <= 160 assert seo.truncate("short text") == "short text" assert seo.truncate("") == "" def test_software_application_url_is_per_project(): schema = seo.software_application_schema( {"uid": "p1", "slug": "p1-foo", "title": "P"}, "https://x.test" ) assert schema["url"] == "https://x.test/projects/p1-foo" def test_schema_types(): assert seo.organization_schema("https://x.test")["@type"] == "Organization" assert ( seo.news_article_schema( {"uid": "n1", "title": "T", "synced_at": "2026-01-01"}, "https://x.test" )["@type"] == "NewsArticle" ) assert ( seo.software_source_code_schema( {"uid": "g1", "title": "G", "language": "python"}, "https://x.test" )["@type"] == "SoftwareSourceCode" ) def test_news_article_publisher_is_organization(): schema = seo.news_article_schema( {"uid": "n1", "title": "T", "synced_at": "2026-01-01"}, "https://x.test" ) assert schema["publisher"]["@type"] == "Organization" def test_site_url_precedence(monkeypatch): import devplacepy.database as database monkeypatch.setattr(seo, "SITE_URL", "https://constant.test") monkeypatch.setattr( database, "get_setting", lambda *a, **k: "https://setting.test" ) assert seo.site_url(None) == "https://setting.test" monkeypatch.setattr(database, "get_setting", lambda *a, **k: "") assert seo.site_url(None) == "https://constant.test" monkeypatch.setattr(seo, "SITE_URL", "") request = types.SimpleNamespace(base_url="https://req.test/") assert seo.site_url(request) == "https://req.test" def test_site_url_falls_back_to_request(monkeypatch): monkeypatch.setattr(seo, "SITE_URL", "") request = types.SimpleNamespace(base_url="http://fallback.test/") assert seo.site_url(request) == "http://fallback.test" def test_upload_allowlist_excludes_dangerous_types(): assert ".svg" not in attach.ALLOWED_UPLOAD_TYPES assert ".html" not in attach.ALLOWED_UPLOAD_TYPES assert ".png" in attach.ALLOWED_UPLOAD_TYPES assert ".svg" not in attach.POST_IMAGE_EXTENSIONS def test_detect_mime_neutralizes_dangerous_types(): assert attach._detect_mime(b"", "x.svg") == "application/octet-stream" assert attach._detect_mime(b"", "x.html") == "application/octet-stream" assert attach._detect_mime(b"", "x.png") == "image/png" def test_base_seo_context_emits_keywords_key(): ctx = seo.base_seo_context( _seo_request(), title="A Post", description="Body text", keywords="python, sqlite", ) assert "meta_keywords" in ctx assert ctx["meta_keywords"] == "python, sqlite" def test_base_seo_context_default_keywords_empty_without_target(): ctx = seo.base_seo_context( _seo_request(), title="A Post", description="Body text" ) assert ctx["meta_keywords"] == "" def test_base_seo_context_description_strips_markdown(monkeypatch): monkeypatch.setattr(seo, "_ready_seo_metadata", lambda target: None) ctx = seo.base_seo_context( _seo_request(), title="A Post", description="## Heading\n\nThis is **bold** body with `code`.", ) assert "##" not in ctx["meta_description"] assert "**" not in ctx["meta_description"] assert "`" not in ctx["meta_description"] assert "bold" in ctx["meta_description"] def test_base_seo_context_plain_default_fills_fields_for_target(monkeypatch): monkeypatch.setattr(seo, "_ready_seo_metadata", lambda target: None) ctx = seo.base_seo_context( _seo_request(), title="Async Python Database Tooling", description="A **guide** to async python database tooling with sqlite.", seo_target=("post", "abc"), ) assert ctx["meta_keywords"] assert "python" in ctx["meta_keywords"] assert ctx["meta_description"] assert "**" not in ctx["meta_description"] def test_base_seo_context_consumes_ready_metadata(monkeypatch): monkeypatch.setattr( seo, "_ready_seo_metadata", lambda target: { "seo_title": "Generated Title", "seo_description": "Generated clean description", "seo_keywords": "generated, metadata", }, ) ctx = seo.base_seo_context( _seo_request(), title="Raw **markdown** title", description="raw markdown body", seo_target=("post", "abc"), ) assert "Generated Title" in ctx["page_title"] assert ctx["meta_description"] == "Generated clean description" assert ctx["meta_keywords"] == "generated, metadata" def _comment_item(uid, content, children=None, author="alice"): return { "comment": {"uid": uid, "content": content, "created_at": "2026-01-01T00:00:00+00:00"}, "author": {"username": author}, "children": children or [], } def test_comment_schema_shapes_a_comment_type(): item = _comment_item("c1", "Nice post, thanks!") schema = seo.comment_schema(item, "https://x.test") assert schema["@type"] == "Comment" assert schema["text"] == "Nice post, thanks!" assert schema["author"] == { "@type": "Person", "name": "alice", "url": "https://x.test/profile/alice", } assert schema["datePublished"] == "2026-01-01T00:00:00+00:00" def test_comment_schema_handles_a_missing_author(): item = _comment_item("c1", "text", author=None) item["author"] = None schema = seo.comment_schema(item, "https://x.test") assert schema["author"] == {"@type": "Person", "name": "Unknown", "url": ""} def test_comment_schema_list_flattens_replies_depth_first(): reply = _comment_item("c2", "a reply") top = _comment_item("c1", "a top-level comment", children=[reply]) flat = seo.comment_schema_list([top], "https://x.test") assert [c["text"] for c in flat] == ["a top-level comment", "a reply"] def test_comment_schema_list_respects_the_limit(): tree = [_comment_item(f"c{i}", f"comment {i}") for i in range(30)] flat = seo.comment_schema_list(tree, "https://x.test", limit=5) assert len(flat) == 5 def test_discussion_forum_posting_omits_comment_key_without_comments(): schema = seo.discussion_forum_posting( {"uid": "p1", "title": "T", "content": "body", "created_at": "2026-01-01"}, {"username": "alice"}, 0, 0, "https://x.test", ) assert "comment" not in schema def test_discussion_forum_posting_embeds_nested_comments(): top = _comment_item("c1", "a top-level comment") schema = seo.discussion_forum_posting( {"uid": "p1", "title": "T", "content": "body", "created_at": "2026-01-01"}, {"username": "alice"}, 1, 0, "https://x.test", comments=seo.comment_schema_list([top], "https://x.test"), ) assert schema["comment"][0]["@type"] == "Comment" assert schema["comment"][0]["text"] == "a top-level comment"