coulomb-social/tests/test_content.py
tegwick affed64839 Land CSOC-WP-0006 PageOps: member space/page CRUD, copy, transfer
Thin CONTENT_ROOT content plane (ADR-0003/0004) with space visual field,
product UI for Title/Abstract/Visual, page list sidebar, and tests. Update
capability model and smoke checklist; mark WP-0006 finished.
2026-08-13 12:46:39 +02:00

113 lines
3.4 KiB
Python

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(tmp_path, settings):
settings.CONTENT_ROOT = str(tmp_path / "empty-content")
space = Space(tenant_id="t", slug="x", title="X")
page = load_space_page(space)
assert page.error
err = page.error.lower()
assert "content plane" in err or "not bound" in err or "forgejo" in err
@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