"""Tests für AbonnementRepository (#136, ADR 0008).""" from __future__ import annotations import asyncio from app.repositories import ( AbonnementRepository, InMemoryAbonnementRepository, SqliteAbonnementRepository, ) def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) class TestProtocolConformance: def test_in_memory_implements_protocol(self): repo = InMemoryAbonnementRepository() assert isinstance(repo, AbonnementRepository) def test_sqlite_implements_protocol(self): repo = SqliteAbonnementRepository() assert isinstance(repo, AbonnementRepository) class TestCreateAndList: def test_create_returns_id(self): repo = InMemoryAbonnementRepository() sid = _run(repo.create("u1", "a@b.de")) assert sid == 1 def test_create_increments_id(self): repo = InMemoryAbonnementRepository() s1 = _run(repo.create("u1", "a@b.de")) s2 = _run(repo.create("u1", "c@d.de")) assert s2 == s1 + 1 def test_list_by_user_filters(self): repo = InMemoryAbonnementRepository() _run(repo.create("u1", "a@b.de")) _run(repo.create("u2", "c@d.de")) rows = _run(repo.list_by_user("u1")) assert len(rows) == 1 assert rows[0]["email"] == "a@b.de" def test_list_all_returns_every_sub(self): repo = InMemoryAbonnementRepository() _run(repo.create("u1", "a@b.de")) _run(repo.create("u2", "c@d.de")) assert len(_run(repo.list_all())) == 2 class TestListDue: def test_list_due_returns_unsent(self): repo = InMemoryAbonnementRepository() _run(repo.create("u1", "a@b.de")) due = _run(repo.list_due()) assert len(due) == 1 def test_mark_sent_removes_from_due(self): repo = InMemoryAbonnementRepository() sid = _run(repo.create("u1", "a@b.de")) _run(repo.mark_sent(sid)) assert _run(repo.list_due()) == [] def test_list_due_filters_by_frequency(self): repo = InMemoryAbonnementRepository() _run(repo.create("u1", "a@b.de", frequency="daily")) _run(repo.create("u2", "c@d.de", frequency="weekly")) daily = _run(repo.list_due("daily")) weekly = _run(repo.list_due("weekly")) assert len(daily) == 1 and daily[0]["email"] == "a@b.de" assert len(weekly) == 1 and weekly[0]["email"] == "c@d.de" class TestDelete: def test_delete_checks_ownership(self): repo = InMemoryAbonnementRepository() sid = _run(repo.create("u1", "a@b.de")) # Fremder User kann nicht löschen assert _run(repo.delete("u2", sid)) is False # Eigentümer kann löschen assert _run(repo.delete("u1", sid)) is True def test_delete_by_id_skips_ownership_check(self): repo = InMemoryAbonnementRepository() sid = _run(repo.create("u1", "a@b.de")) # delete_by_id ist für Unsubscribe-Links (Token-gesichert), nicht für # den Self-Service; kein User-Check assert _run(repo.delete_by_id(sid)) is True def test_delete_by_id_missing_returns_false(self): repo = InMemoryAbonnementRepository() assert _run(repo.delete_by_id(999)) is False # ───────────────────────────────────────────────────────────────────────────── # SqliteAbonnementRepository — Delegation an database.* (#134 Coverage-Backfill) # ───────────────────────────────────────────────────────────────────────────── class TestSqliteAbonnementRepositoryDelegation: """Die Sqlite-Variante ist nur ein duenner Wrapper um Module-Funktionen in app.database. Test prueft dass jede Methode korrekt delegiert, ohne echte DB-Calls (Module-Funktionen werden gemockt).""" def test_create_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository repo = SqliteAbonnementRepository() with patch("app.repositories.abonnement_repository.database.create_subscription", new=AsyncMock(return_value=42)) as m: result = _run(repo.create("u1", "a@b.de", "NRW", "CDU", "weekly")) assert result == 42 m.assert_called_once_with("u1", "a@b.de", "NRW", "CDU", "weekly") def test_list_by_user_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository repo = SqliteAbonnementRepository() fake = [{"id": 1, "email": "x@y"}] with patch("app.repositories.abonnement_repository.database.list_subscriptions", new=AsyncMock(return_value=fake)) as m: assert _run(repo.list_by_user("u1")) == fake m.assert_called_once_with("u1") def test_list_all_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository with patch("app.repositories.abonnement_repository.database.list_all_subscriptions", new=AsyncMock(return_value=[])) as m: assert _run(SqliteAbonnementRepository().list_all()) == [] m.assert_called_once_with() def test_list_due_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository with patch("app.repositories.abonnement_repository.database.get_all_subscriptions_due", new=AsyncMock(return_value=[])) as m: _run(SqliteAbonnementRepository().list_due("weekly")) m.assert_called_once_with("weekly") def test_delete_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository with patch("app.repositories.abonnement_repository.database.delete_subscription", new=AsyncMock(return_value=True)) as m: _run(SqliteAbonnementRepository().delete("u1", 5)) m.assert_called_once_with("u1", 5) def test_delete_by_id_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository with patch("app.repositories.abonnement_repository.database.delete_subscription_by_id", new=AsyncMock(return_value=False)) as m: _run(SqliteAbonnementRepository().delete_by_id(99)) m.assert_called_once_with(99) def test_mark_sent_delegates(self): from unittest.mock import AsyncMock, patch from app.repositories.abonnement_repository import SqliteAbonnementRepository with patch("app.repositories.abonnement_repository.database.mark_subscription_sent", new=AsyncMock(return_value=None)) as m: _run(SqliteAbonnementRepository().mark_sent(7)) m.assert_called_once_with(7) def test_get_abonnement_repository_returns_singleton(): from app.repositories.abonnement_repository import get_abonnement_repository a = get_abonnement_repository() b = get_abonnement_repository() assert a is b