Render space pages from Forgejo markdown (CSOC-WP-0004-T04)
Fetch raw files from Forgejo, sanitize markdown to HTML, and show them on space detail. Ship a public demo fixture path and seed_demo_space command.
This commit is contained in:
parent
86b54243b8
commit
1cedd8f219
13 changed files with 606 additions and 26 deletions
111
tests/test_content.py
Normal file
111
tests/test_content.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
|
||||
from coulomb_social.apps.members.models import Member
|
||||
from coulomb_social.apps.spaces.content import clear_content_cache, load_space_page, render_markdown
|
||||
from coulomb_social.apps.spaces.forgejo import ContentFetchError, FetchedFile
|
||||
from coulomb_social.apps.spaces.models import Space
|
||||
|
||||
|
||||
def test_render_markdown_strips_script():
|
||||
html = render_markdown("# Hi\n\n<script>alert(1)</script>\n\n**bold**")
|
||||
assert "<script>" not in html
|
||||
assert "bold" in html
|
||||
assert "Hi" in html
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_load_space_page_unbound():
|
||||
space = Space(tenant_id="t", slug="x", title="X")
|
||||
page = load_space_page(space)
|
||||
assert page.error
|
||||
assert "not bound" in page.error.lower()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_load_space_page_fetches_and_renders():
|
||||
clear_content_cache()
|
||||
space = Space(
|
||||
tenant_id="t",
|
||||
slug="demo",
|
||||
title="Demo",
|
||||
forgejo_owner="coulomb",
|
||||
forgejo_repo="coulomb-social",
|
||||
default_branch="main",
|
||||
content_root="docs/space-fixtures/demo/pages",
|
||||
)
|
||||
fake = FetchedFile(
|
||||
path="docs/space-fixtures/demo/pages/index.md",
|
||||
text="# Hello demo\n\nParagraph.\n",
|
||||
source="forgejo-raw",
|
||||
)
|
||||
with patch(
|
||||
"coulomb_social.apps.spaces.content.fetch_raw_file", return_value=fake
|
||||
) as mocked:
|
||||
page = load_space_page(space, "index")
|
||||
mocked.assert_called_once()
|
||||
assert page.error is None
|
||||
assert page.title == "Hello demo"
|
||||
assert "Paragraph" in page.html
|
||||
assert page.source == "forgejo-raw"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_load_space_page_fail_closed():
|
||||
clear_content_cache()
|
||||
space = Space(
|
||||
tenant_id="t",
|
||||
slug="demo",
|
||||
title="Demo",
|
||||
forgejo_owner="o",
|
||||
forgejo_repo="r",
|
||||
content_root="pages",
|
||||
)
|
||||
with patch(
|
||||
"coulomb_social.apps.spaces.content.fetch_raw_file",
|
||||
side_effect=ContentFetchError("File not found: pages/index.md", status_code=404),
|
||||
):
|
||||
page = load_space_page(space)
|
||||
assert page.error
|
||||
assert "not found" in page.error.lower()
|
||||
assert page.html == ""
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_space_detail_renders_content(client, settings):
|
||||
clear_content_cache()
|
||||
settings.DEBUG = True
|
||||
settings.OIDC_ENABLED = False
|
||||
client.post(
|
||||
reverse("identity:dev_login"),
|
||||
{
|
||||
"subject": "reader",
|
||||
"issuer": "https://local.dev/issuer",
|
||||
"name": "Reader",
|
||||
"tenant": "tenant:coulomb",
|
||||
},
|
||||
)
|
||||
member = Member.objects.get(subject="reader")
|
||||
Space.objects.create(
|
||||
tenant_id="tenant:coulomb",
|
||||
slug="demo",
|
||||
title="Demo",
|
||||
created_by=member,
|
||||
forgejo_owner="coulomb",
|
||||
forgejo_repo="coulomb-social",
|
||||
content_root="docs/space-fixtures/demo/pages",
|
||||
)
|
||||
fake = FetchedFile(
|
||||
path="docs/space-fixtures/demo/pages/index.md",
|
||||
text="# From Forgejo\n\nVisible body.\n",
|
||||
source="forgejo-raw",
|
||||
)
|
||||
with patch(
|
||||
"coulomb_social.apps.spaces.content.fetch_raw_file", return_value=fake
|
||||
):
|
||||
r = client.get(reverse("spaces:detail", kwargs={"slug": "demo"}))
|
||||
assert r.status_code == 200
|
||||
assert b"From Forgejo" in r.content
|
||||
assert b"Visible body" in r.content
|
||||
Loading…
Add table
Add a link
Reference in a new issue