Spaces:
Running
Running
| """Author RAG β Email Integrations Admin Page Tests.""" | |
| import pytest | |
| from unittest.mock import AsyncMock, patch, MagicMock | |
| from fastapi.testclient import TestClient | |
| # ββ ConvertKit sync ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def test_sync_subscriber_convertkit_success(): | |
| from app.api.email_integrations import sync_subscriber, _configs | |
| _configs["test-author"] = { | |
| "provider": "convertkit", | |
| "api_key": "ck_test_key_1234", | |
| "list_id": "12345", | |
| "sync_on_buy_intent": True, | |
| "sync_on_link_click": False, | |
| } | |
| with patch("httpx.AsyncClient") as MockClient: | |
| instance = AsyncMock() | |
| MockClient.return_value.__aenter__ = AsyncMock(return_value=instance) | |
| MockClient.return_value.__aexit__ = AsyncMock(return_value=False) | |
| instance.post = AsyncMock(return_value=MagicMock(status_code=200)) | |
| await sync_subscriber("test-author", "reader@example.com", "Jane") | |
| instance.post.assert_called_once() | |
| call_kwargs = instance.post.call_args | |
| assert "subscribe" in str(call_kwargs) | |
| async def test_sync_subscriber_no_config(): | |
| """Should silently no-op if author has no email integration configured.""" | |
| from app.api.email_integrations import sync_subscriber, _configs | |
| _configs.pop("no-config-author", None) | |
| # Should not raise | |
| await sync_subscriber("no-config-author", "reader@example.com") | |
| # ββ GDPR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def test_gdpr_deletion_request_idempotent(): | |
| from app.api.gdpr import _deletion_requests | |
| _deletion_requests["dup-author"] = { | |
| "author_id": "dup-author", | |
| "scheduled_at": "2024-01-01T00:00:00Z", | |
| "deletion_date": "2024-01-31T00:00:00Z", | |
| "status": "pending", | |
| } | |
| # Re-requesting should return already_requested | |
| assert _deletion_requests["dup-author"]["status"] == "pending" | |
| def test_gdpr_delete_status_none(): | |
| from app.api.gdpr import _deletion_requests | |
| _deletion_requests.pop("clean-author", None) | |
| assert "clean-author" not in _deletion_requests | |
| # ββ Agency ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_agency_member_role_validation(): | |
| from app.models.agency import AgencyMember | |
| m = AgencyMember.__new__(AgencyMember) | |
| m.role = "author" | |
| assert m.role in ("author", "admin") | |
| # ββ Rate Limiter ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_rate_limit_rule_login(): | |
| from app.middleware.rate_limit import _get_rule | |
| limit, window = _get_rule("/api/auth/login") | |
| assert limit == 10 | |
| assert window == 900 | |
| def test_rate_limit_rule_forgot(): | |
| from app.middleware.rate_limit import _get_rule | |
| limit, window = _get_rule("/api/auth/forgot-password") | |
| assert limit == 3 | |
| assert window == 3600 | |
| def test_rate_limit_rule_default(): | |
| from app.middleware.rate_limit import _get_rule | |
| limit, window = _get_rule("/api/admin/some-author/books") | |
| assert limit == 300 | |
| assert window == 60 | |
| # ββ Stripe Connect ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def test_connect_status_no_account(mock_db, mock_author): | |
| mock_author.stripe_connect_account_id = None | |
| from app.api.stripe_connect import connect_status | |
| result = await connect_status( | |
| slug=mock_author.id, | |
| db=mock_db, | |
| author=mock_author, | |
| ) | |
| assert result["connected"] is False | |