Arag / tests /unit /test_phase4.py
AuthorBot
Add email list sync UI, Phase 4 user fields, and startup migrations.
59ba43e
Raw
History Blame Contribute Delete
4.16 kB
"""Author RAG β€” Email Integrations Admin Page Tests."""
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from fastapi.testclient import TestClient
# ── ConvertKit sync ────────────────────────────────────────────────────────
@pytest.mark.asyncio
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)
@pytest.mark.asyncio
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 ──────────────────────────────────────────────────────────────────
@pytest.mark.asyncio
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 ────────────────────────────────────────────────────────
@pytest.mark.asyncio
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