# 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_project_schema_type_mapping(): assert seo.project_schema_type("game") == "VideoGame" assert seo.project_schema_type("game_asset") == "CreativeWork" assert seo.project_schema_type("website") == "WebApplication" assert seo.project_schema_type("software") == "SoftwareApplication" assert seo.project_schema_type("mobile_app") == "SoftwareApplication" assert seo.project_schema_type(None) == "SoftwareApplication" assert seo.project_schema_type("unknown") == "SoftwareApplication" def test_software_application_schema_is_type_aware(): game = seo.software_application_schema( {"uid": "p1", "slug": "p1-g", "title": "G", "project_type": "game", "platforms": "PC, Web"}, "https://x.test", ) assert game["@type"] == "VideoGame" assert game["gamePlatform"] == ["PC", "Web"] assert "applicationCategory" not in game web = seo.software_application_schema( {"uid": "p2", "slug": "p2-w", "title": "W", "project_type": "website"}, "https://x.test", ) assert web["@type"] == "WebApplication" assert web["browserRequirements"] == "Requires JavaScript" assert web["applicationCategory"] == "DeveloperApplication" def test_software_application_schema_rating_and_image(): project = {"uid": "p1", "slug": "p1-s", "title": "S", "project_type": "software"} plain = seo.software_application_schema(project, "https://x.test") assert "aggregateRating" not in plain assert "image" not in plain rich = seo.software_application_schema( project, "https://x.test", image_url="https://x.test/img.png", star_count=7, comment_count=3, ) assert rich["aggregateRating"]["ratingCount"] == "7" assert rich["image"] == "https://x.test/img.png" assert rich["interactionStatistic"]["userInteractionCount"] == 3 def test_project_devlog_schema_builds_blog_graph(): project = {"uid": "p1", "slug": "p1-s", "title": "Nebula"} assert seo.project_devlog_schema(project, [], "https://x.test") is None posts = [ { "post": { "uid": "a1", "slug": "a1-first", "title": "First update", "content": "Progress!", "created_at": "2026-01-01T00:00:00+00:00", }, "author": {"username": "dev"}, "comment_count": 2, } ] schema = seo.project_devlog_schema(project, posts, "https://x.test") assert schema["@type"] == "Blog" assert schema["url"] == "https://x.test/projects/p1-s#devlog" entry = schema["blogPost"][0] assert entry["@type"] == "BlogPosting" assert entry["headline"] == "First update" assert entry["url"] == "https://x.test/posts/a1-first" assert entry["author"]["name"] == "dev" assert entry["commentCount"] == 2 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"