import types
import devplacepy.seo as seo
import devplacepy.attachments as attach
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_prefers_env(monkeypatch):
monkeypatch.setattr(seo, "SITE_URL", "https://configured.test")
assert seo.site_url(None) == "https://configured.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"