Add Space metadata model and provisional Forgejo content ADR
Introduce tenant-scoped Space/SpaceMembership with list and detail views. Document markdown-in-Forgejo as content SoR (ADR-0002). Run migrations on container start so app.coulomb.social picks up the new tables.
This commit is contained in:
parent
bb6bc6093a
commit
be32432d39
22 changed files with 697 additions and 12 deletions
111
tests/test_spaces.py
Normal file
111
tests/test_spaces.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import pytest
|
||||
from django.urls import reverse
|
||||
|
||||
from coulomb_social.apps.members.models import Member, User
|
||||
from coulomb_social.apps.spaces.models import Space, SpaceMembership
|
||||
from coulomb_social.apps.spaces.services import spaces_for_member
|
||||
|
||||
|
||||
def _login(client, settings, *, subject: str, tenant: str, name: str):
|
||||
settings.DEBUG = True
|
||||
settings.OIDC_ENABLED = False
|
||||
settings.DEFAULT_TENANT_ID = tenant
|
||||
return client.post(
|
||||
reverse("identity:dev_login"),
|
||||
{
|
||||
"subject": subject,
|
||||
"issuer": "https://local.dev/issuer",
|
||||
"name": name,
|
||||
"tenant": tenant,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_spaces_list_empty_for_tenant(client, settings):
|
||||
_login(client, settings, subject="u1", tenant="tenant:a", name="User One")
|
||||
r = client.get(reverse("core:app_home"))
|
||||
assert r.status_code == 200
|
||||
assert b"No spaces yet" in r.content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_spaces_list_shows_same_tenant_only(client, settings):
|
||||
_login(client, settings, subject="u1", tenant="tenant:a", name="User One")
|
||||
member = Member.objects.get(subject="u1")
|
||||
Space.objects.create(
|
||||
tenant_id="tenant:a",
|
||||
slug="alpha",
|
||||
title="Alpha Space",
|
||||
created_by=member,
|
||||
)
|
||||
Space.objects.create(
|
||||
tenant_id="tenant:b",
|
||||
slug="beta",
|
||||
title="Beta Space",
|
||||
)
|
||||
|
||||
r = client.get(reverse("core:app_home"))
|
||||
assert r.status_code == 200
|
||||
assert b"Alpha Space" in r.content
|
||||
assert b"Beta Space" not in r.content
|
||||
assert b"No spaces yet" not in r.content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_space_detail_tenant_isolation(client, settings):
|
||||
_login(client, settings, subject="u1", tenant="tenant:a", name="User One")
|
||||
Space.objects.create(tenant_id="tenant:b", slug="secret", title="Secret")
|
||||
r = client.get(reverse("spaces:detail", kwargs={"slug": "secret"}))
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_space_detail_ok(client, settings):
|
||||
_login(client, settings, subject="u1", tenant="tenant:a", name="User One")
|
||||
member = Member.objects.get(subject="u1")
|
||||
Space.objects.create(
|
||||
tenant_id="tenant:a",
|
||||
slug="lab",
|
||||
title="Lab",
|
||||
description="Research lab",
|
||||
created_by=member,
|
||||
forgejo_owner="coulomb",
|
||||
forgejo_repo="space-lab",
|
||||
)
|
||||
r = client.get(reverse("spaces:detail", kwargs={"slug": "lab"}))
|
||||
assert r.status_code == 200
|
||||
assert b"Lab" in r.content
|
||||
assert b"coulomb/space-lab" in r.content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_membership_narrows_visibility(client, settings):
|
||||
_login(client, settings, subject="u1", tenant="tenant:a", name="User One")
|
||||
member = Member.objects.get(subject="u1")
|
||||
other_user = User.objects.create_user(username="other")
|
||||
other = Member.objects.create(
|
||||
tenant_id="tenant:a",
|
||||
user=other_user,
|
||||
user_engine_user_id="usr_other",
|
||||
issuer="https://local.dev/issuer",
|
||||
subject="other-sub",
|
||||
display_name="Other",
|
||||
)
|
||||
open_space = Space.objects.create(
|
||||
tenant_id="tenant:a", slug="open", title="Open", created_by=other
|
||||
)
|
||||
closed = Space.objects.create(
|
||||
tenant_id="tenant:a", slug="closed", title="Closed", created_by=other
|
||||
)
|
||||
# Any membership in tenant switches mode to membership-scoped
|
||||
SpaceMembership.objects.create(
|
||||
space=closed, member=other, role=SpaceMembership.Role.OWNER
|
||||
)
|
||||
SpaceMembership.objects.create(
|
||||
space=open_space, member=member, role=SpaceMembership.Role.MEMBER
|
||||
)
|
||||
|
||||
visible = list(spaces_for_member(member).values_list("slug", flat=True))
|
||||
assert "open" in visible
|
||||
assert "closed" not in visible
|
||||
Loading…
Add table
Add a link
Reference in a new issue