"""Tests for the DSAR worker job (G1 DSGVO: Art. 15/17). Proves the previously missing "process_dsar" job works end-to-end: - access: collects profile + contacts + audit_log + notifications - deletion: soft-deletes contacts, hard-deletes notifications, anonymizes and deactivates the account, writes a dsar_erasure audit entry """ from __future__ import annotations import uuid import pytest import pytest_asyncio from sqlalchemy import select as sa_select from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from app.core.db import reset_engine_for_testing from app.core.jobs import _dsar_collect_user_data, _dsar_execute_deletion, process_dsar from app.models.audit import AuditLog from app.models.contact import Contact from app.models.notification import Notification from app.models.user import User from tests.conftest import seed_tenant_and_users @pytest_asyncio.fixture async def dsar_session(engine) -> AsyncSession: reset_engine_for_testing(engine) sf = async_sessionmaker(bind=engine, expire_on_commit=False, class_=AsyncSession) async with sf() as session: yield session await session.rollback() def test_process_dsar_registered(): """The job is registered so enqueue_job('process_dsar') can find it.""" from app.core.job_registry import get_job assert get_job("process_dsar") is not None @pytest.mark.asyncio async def test_dsar_access_collects_all_categories(dsar_session: AsyncSession): """Art. 15: collect_user_data returns all promised categories.""" seed = await seed_tenant_and_users(dsar_session) data = await _dsar_collect_user_data( dsar_session, str(seed["tenant_a"].id), str(seed["admin_a"].id), ) assert data["legal_basis"] for category in ("profile", "contacts", "audit_log", "notifications"): assert category in data["data"], f"missing category {category}" @pytest.mark.asyncio async def test_dsar_deletion_anonymizes_and_soft_deletes(dsar_session: AsyncSession): """Art. 17: contacts soft-deleted, notifications gone, user anonymized.""" seed = await seed_tenant_and_users(dsar_session) uid = seed["admin_a"].id tid = seed["tenant_a"].id # Fixture: one contact + one notification owned by admin_a contact = Contact( id=uuid.uuid4(), tenant_id=tid, owner_id=uid, type="person", displayname="To Erase", email_1="erase@test.local", firstname="Era", surname="SeMe", discount_crew=0, discount_transport=0, discount_rental=0, discount_sale=0, discount_subrent=0, discount_total=0, ) notification = Notification( id=uuid.uuid4(), tenant_id=tid, owner_id=uid, user_id=uid, type="test", title="to erase", ) dsar_session.add_all([contact, notification]) await dsar_session.commit() counts = await _dsar_execute_deletion(dsar_session, str(tid), str(uid)) await dsar_session.commit() assert counts["contacts_soft_deleted"] == 1 assert counts["notifications_deleted"] == 1 assert counts["user_anonymized"] == 1 # Verify persisted state (fresh query, bypassing identity map) dsar_session.expunge_all() erased_contact = ( await dsar_session.execute(sa_select(Contact).where(Contact.id == contact.id)) ).scalar_one() assert erased_contact.deleted_at is not None remaining_notifications = ( await dsar_session.execute( sa_select(Notification).where(Notification.owner_id == uid) ) ).scalars().all() assert len(remaining_notifications) == 0 erased_user = ( await dsar_session.execute(sa_select(User).where(User.id == uid)) ).scalar_one() assert erased_user.is_active is False assert erased_user.name == "[gelöscht gemäß DSGVO Art. 17]" assert "anonymized.invalid" in erased_user.email # Audit entry written audits = ( await dsar_session.execute( sa_select(AuditLog).where(AuditLog.action == "dsar_erasure") ) ).scalars().all() assert len(audits) >= 1 @pytest.mark.asyncio async def test_process_dsar_dispatches_by_type(dsar_session: AsyncSession): """process_dsar dispatches by request_type without raising.""" seed = await seed_tenant_and_users(dsar_session) result = await process_dsar( {}, user_id=str(seed["admin_a"].id), tenant_id=str(seed["tenant_a"].id), request_type="access", ) assert result["status"] == "completed" assert "categories" in result result = await process_dsar( {}, user_id=str(seed["editor_a"].id), tenant_id=str(seed["tenant_a"].id), request_type="unknown_type_xyz", ) assert result["status"] == "unknown_type"