Fix CI failures: add backend/conftest.py for module resolution and run black formatting
- Add backend/conftest.py that inserts the backend directory into sys.path, fixing ModuleNotFoundError when pytest runs from the backend/ directory (as CI does with `cd backend && pytest tests/`) - Run black formatter on all 28 backend files that needed reformatting - All 53 tests pass with both `pytest tests/` and `python -m pytest tests/` Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/beb47db2-da25-416a-8fb0-c1452a3b22a7
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Mail account management endpoints"""
|
||||
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -9,9 +10,13 @@ from app.core.deps import get_current_active_user
|
||||
from app.core.security import encrypt_credential, decrypt_credential
|
||||
from app.models.database_models import User, MailAccount
|
||||
from app.models.schemas import (
|
||||
MailAccountCreate, MailAccountResponse, MailAccountUpdate,
|
||||
MailAccountTestRequest, MailAccountTestResponse,
|
||||
MailAccountAutoDetectRequest, MailAccountAutoDetectResponse
|
||||
MailAccountCreate,
|
||||
MailAccountResponse,
|
||||
MailAccountUpdate,
|
||||
MailAccountTestRequest,
|
||||
MailAccountTestResponse,
|
||||
MailAccountAutoDetectRequest,
|
||||
MailAccountAutoDetectResponse,
|
||||
)
|
||||
from app.services.mail_processor import MailProcessor, MailServerAutoDetect
|
||||
from app.core.config import settings
|
||||
@@ -19,38 +24,40 @@ from app.core.config import settings
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("", response_model=MailAccountResponse, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"", response_model=MailAccountResponse, status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
async def create_mail_account(
|
||||
account_in: MailAccountCreate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Create a new mail account"""
|
||||
|
||||
|
||||
# Check subscription limits
|
||||
result = await db.execute(
|
||||
select(MailAccount).where(MailAccount.user_id == current_user.id)
|
||||
)
|
||||
existing_accounts = result.scalars().all()
|
||||
|
||||
|
||||
tier_limits = {
|
||||
"free": settings.TIER_FREE_MAX_ACCOUNTS,
|
||||
"basic": settings.TIER_BASIC_MAX_ACCOUNTS,
|
||||
"pro": settings.TIER_PRO_MAX_ACCOUNTS,
|
||||
"enterprise": settings.TIER_ENTERPRISE_MAX_ACCOUNTS,
|
||||
}
|
||||
|
||||
|
||||
max_accounts = tier_limits.get(current_user.subscription_tier.value, 1)
|
||||
|
||||
|
||||
if len(existing_accounts) >= max_accounts:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_402_PAYMENT_REQUIRED,
|
||||
detail=f"Account limit reached. Upgrade your subscription to add more accounts."
|
||||
detail=f"Account limit reached. Upgrade your subscription to add more accounts.",
|
||||
)
|
||||
|
||||
|
||||
# Encrypt password
|
||||
encrypted_password = encrypt_credential(account_in.password)
|
||||
|
||||
|
||||
# Create account
|
||||
account = MailAccount(
|
||||
user_id=current_user.id,
|
||||
@@ -68,20 +75,20 @@ async def create_mail_account(
|
||||
is_enabled=account_in.is_enabled,
|
||||
check_interval_minutes=account_in.check_interval_minutes,
|
||||
max_emails_per_check=account_in.max_emails_per_check,
|
||||
delete_after_forward=account_in.delete_after_forward
|
||||
delete_after_forward=account_in.delete_after_forward,
|
||||
)
|
||||
|
||||
|
||||
db.add(account)
|
||||
await db.commit()
|
||||
await db.refresh(account)
|
||||
|
||||
|
||||
return account
|
||||
|
||||
|
||||
@router.get("", response_model=List[MailAccountResponse])
|
||||
async def list_mail_accounts(
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""List all mail accounts for current user"""
|
||||
result = await db.execute(
|
||||
@@ -97,23 +104,21 @@ async def list_mail_accounts(
|
||||
async def get_mail_account(
|
||||
account_id: int,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Get a specific mail account"""
|
||||
result = await db.execute(
|
||||
select(MailAccount).where(
|
||||
MailAccount.id == account_id,
|
||||
MailAccount.user_id == current_user.id
|
||||
MailAccount.id == account_id, MailAccount.user_id == current_user.id
|
||||
)
|
||||
)
|
||||
account = result.scalar_one_or_none()
|
||||
|
||||
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Mail account not found"
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Mail account not found"
|
||||
)
|
||||
|
||||
|
||||
return account
|
||||
|
||||
|
||||
@@ -122,35 +127,35 @@ async def update_mail_account(
|
||||
account_id: int,
|
||||
account_update: MailAccountUpdate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Update a mail account"""
|
||||
result = await db.execute(
|
||||
select(MailAccount).where(
|
||||
MailAccount.id == account_id,
|
||||
MailAccount.user_id == current_user.id
|
||||
MailAccount.id == account_id, MailAccount.user_id == current_user.id
|
||||
)
|
||||
)
|
||||
account = result.scalar_one_or_none()
|
||||
|
||||
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Mail account not found"
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Mail account not found"
|
||||
)
|
||||
|
||||
|
||||
# Update fields
|
||||
update_data = account_update.dict(exclude_unset=True)
|
||||
|
||||
|
||||
if "password" in update_data:
|
||||
update_data["encrypted_password"] = encrypt_credential(update_data.pop("password"))
|
||||
|
||||
update_data["encrypted_password"] = encrypt_credential(
|
||||
update_data.pop("password")
|
||||
)
|
||||
|
||||
for field, value in update_data.items():
|
||||
setattr(account, field, value)
|
||||
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(account)
|
||||
|
||||
|
||||
return account
|
||||
|
||||
|
||||
@@ -158,23 +163,21 @@ async def update_mail_account(
|
||||
async def delete_mail_account(
|
||||
account_id: int,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Delete a mail account"""
|
||||
result = await db.execute(
|
||||
select(MailAccount).where(
|
||||
MailAccount.id == account_id,
|
||||
MailAccount.user_id == current_user.id
|
||||
MailAccount.id == account_id, MailAccount.user_id == current_user.id
|
||||
)
|
||||
)
|
||||
account = result.scalar_one_or_none()
|
||||
|
||||
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Mail account not found"
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Mail account not found"
|
||||
)
|
||||
|
||||
|
||||
await db.delete(account)
|
||||
await db.commit()
|
||||
|
||||
@@ -185,7 +188,7 @@ async def test_mail_connection(
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
"""Test connection to mail server"""
|
||||
|
||||
|
||||
# Create temporary account for testing
|
||||
temp_account = MailAccount(
|
||||
user_id=current_user.id,
|
||||
@@ -198,16 +201,13 @@ async def test_mail_connection(
|
||||
use_tls=test_request.use_tls,
|
||||
username=test_request.username,
|
||||
encrypted_password="", # Not used for test
|
||||
forward_to="test@test.com"
|
||||
forward_to="test@test.com",
|
||||
)
|
||||
|
||||
|
||||
processor = MailProcessor(temp_account, test_request.password)
|
||||
success, message = await processor.test_connection()
|
||||
|
||||
return MailAccountTestResponse(
|
||||
success=success,
|
||||
message=message
|
||||
)
|
||||
|
||||
return MailAccountTestResponse(success=success, message=message)
|
||||
|
||||
|
||||
@router.post("/auto-detect", response_model=MailAccountAutoDetectResponse)
|
||||
@@ -216,10 +216,9 @@ async def auto_detect_mail_settings(
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
"""Auto-detect mail server settings for an email address"""
|
||||
|
||||
|
||||
suggestions = MailServerAutoDetect.detect(detect_request.email_address)
|
||||
|
||||
|
||||
return MailAccountAutoDetectResponse(
|
||||
success=len(suggestions) > 0,
|
||||
suggestions=suggestions
|
||||
success=len(suggestions) > 0, suggestions=suggestions
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user