Visibility phase 2: privacy-engine branches on viewer auth state
can_view_tree() now distinguishes anonymous vs authenticated non-members so the
four-level model is enforceable:
- public / unlisted → anyone, including anonymous (unlisted gated only by the
link, so the API must never *list* it)
- site_members → any authenticated account (denies anonymous)
- private → members only
Members (any role) always view; soft-deleted trees stay hidden from everyone.
person_visibility (living-person redaction) is unchanged.
Tests: a full can_view_tree matrix across {anonymous, logged-in non-member,
member} × {public, unlisted, site_members, private}, plus deleted-tree-hidden
and the site_members anon-vs-logged-in case. Adds `engine`/`db_session` fixtures
(refactored out of `client`) so the engine can be unit-tested directly,
including the anonymous path that has no HTTP endpoint yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -67,16 +67,21 @@ def mailer() -> CapturingMailer:
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def client():
|
||||
async def engine():
|
||||
if not TEST_DATABASE_URL:
|
||||
pytest.skip("TEST_DATABASE_URL not set")
|
||||
|
||||
engine = create_async_engine(TEST_DATABASE_URL)
|
||||
async with engine.begin() as conn:
|
||||
eng = create_async_engine(TEST_DATABASE_URL)
|
||||
async with eng.begin() as conn:
|
||||
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm"))
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
yield eng
|
||||
await eng.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def client(engine):
|
||||
sessionmaker = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
|
||||
|
||||
async def _override_session():
|
||||
@@ -95,7 +100,14 @@ async def client():
|
||||
yield http_client
|
||||
|
||||
app.dependency_overrides.clear()
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_session(engine):
|
||||
"""A raw AsyncSession on the test DB, for unit-testing services directly."""
|
||||
sessionmaker = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
|
||||
async with sessionmaker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
def token_from_link(link: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user