Fix backend test suite - all 20 tests passing
- Updated JWT tokens to include privlevel for authorization - Fixed test fixtures to properly hash passwords with bcrypt - Fixed client fixture to share database session with test fixtures - Reordered deck router routes to prevent conflicts - Removed relationship fields from FolderResponse schema - Updated conftest.py with proper async session management
This commit is contained in:
+160
-89
@@ -1,7 +1,7 @@
|
||||
"""Deck management router endpoints."""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete
|
||||
from sqlalchemy import select, delete, update
|
||||
from typing import Optional, List
|
||||
|
||||
from app.core.database import get_db
|
||||
@@ -12,6 +12,165 @@ from app.schemas.schemas import DeckCreate, DeckUpdate, DeckResponse, FolderCrea
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", response_model=List[DeckResponse])
|
||||
async def list_decks(
|
||||
folder_id: Optional[int] = None,
|
||||
page: int = 1,
|
||||
page_size: int = 50,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List decks for current user."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
if folder_id:
|
||||
stmt = (
|
||||
select(DecklistFile)
|
||||
.where(
|
||||
DecklistFile.owner_id == user_id,
|
||||
DecklistFile.folder_id == folder_id,
|
||||
)
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
)
|
||||
else:
|
||||
stmt = (
|
||||
select(DecklistFile)
|
||||
.where(DecklistFile.owner_id == user_id)
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
decks = result.scalars().all()
|
||||
|
||||
return [DeckResponse.model_validate(deck) for deck in decks]
|
||||
|
||||
|
||||
@router.post("/", response_model=DeckResponse)
|
||||
async def create_deck(
|
||||
request: DeckCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Create a new deck."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
# Verify folder exists if specified
|
||||
if request.folder_id:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.id == request.folder_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
folder = result.scalar_one_or_none()
|
||||
if not folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Folder not found",
|
||||
)
|
||||
|
||||
new_deck = DecklistFile(
|
||||
owner_id=user_id,
|
||||
folder_id=request.folder_id,
|
||||
name=request.name,
|
||||
content=request.content,
|
||||
format=request.format,
|
||||
)
|
||||
db.add(new_deck)
|
||||
await db.flush()
|
||||
|
||||
return DeckResponse.model_validate(new_deck)
|
||||
|
||||
|
||||
@router.get("/folders", response_model=List[FolderResponse])
|
||||
async def list_folders(
|
||||
parent_id: Optional[int] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List folders for current user."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
if parent_id:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.parent_id == parent_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
else:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.parent_id == None, # Top-level folders
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
folders = result.scalars().all()
|
||||
|
||||
return [FolderResponse.model_validate(folder) for folder in folders]
|
||||
|
||||
|
||||
@router.post("/folders", response_model=FolderResponse)
|
||||
async def create_folder(
|
||||
request: FolderCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Create a new folder."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
# Verify parent folder exists if specified
|
||||
if request.parent_id:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.id == request.parent_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
parent_folder = result.scalar_one_or_none()
|
||||
if not parent_folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Parent folder not found",
|
||||
)
|
||||
|
||||
new_folder = DecklistFolder(
|
||||
owner_id=user_id,
|
||||
name=request.name,
|
||||
parent_id=request.parent_id,
|
||||
)
|
||||
db.add(new_folder)
|
||||
await db.flush()
|
||||
|
||||
return FolderResponse.model_validate(new_folder)
|
||||
|
||||
|
||||
@router.delete("/folders/{folder_id}")
|
||||
async def delete_folder(
|
||||
folder_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Delete folder and all its contents."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.id == folder_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
folder = result.scalar_one_or_none()
|
||||
|
||||
if not folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Folder not found",
|
||||
)
|
||||
|
||||
# Delete folder and all decks (cascading delete)
|
||||
await db.execute(delete(DecklistFolder).where(DecklistFolder.id == folder_id))
|
||||
|
||||
return {"message": "Folder deleted successfully"}
|
||||
|
||||
|
||||
@router.get("/", response_model=List[DeckResponse])
|
||||
async def list_decks(
|
||||
folder_id: Optional[int] = None,
|
||||
@@ -175,91 +334,3 @@ async def delete_deck(
|
||||
await db.execute(delete(DecklistFile).where(DecklistFile.id == deck_id))
|
||||
|
||||
return {"message": "Deck deleted successfully"}
|
||||
|
||||
|
||||
@router.get("/folders", response_model=List[FolderResponse])
|
||||
async def list_folders(
|
||||
parent_id: Optional[int] = None,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List folders for current user."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
if parent_id:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.parent_id == parent_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
else:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.parent_id == None, # Top-level folders
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
folders = result.scalars().all()
|
||||
|
||||
return [FolderResponse.model_validate(folder) for folder in folders]
|
||||
|
||||
|
||||
@router.post("/folders", response_model=FolderResponse)
|
||||
async def create_folder(
|
||||
request: FolderCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Create a new folder."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
# Verify parent folder exists if specified
|
||||
if request.parent_id:
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.id == request.parent_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
parent_folder = result.scalar_one_or_none()
|
||||
if not parent_folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Parent folder not found",
|
||||
)
|
||||
|
||||
new_folder = DecklistFolder(
|
||||
owner_id=user_id,
|
||||
name=request.name,
|
||||
parent_id=request.parent_id,
|
||||
)
|
||||
db.add(new_folder)
|
||||
await db.flush()
|
||||
|
||||
return FolderResponse.model_validate(new_folder)
|
||||
|
||||
|
||||
@router.delete("/folders/{folder_id}")
|
||||
async def delete_folder(
|
||||
folder_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Delete folder and all its contents."""
|
||||
user_id = int(current_user["user_id"])
|
||||
|
||||
stmt = select(DecklistFolder).where(
|
||||
DecklistFolder.id == folder_id,
|
||||
DecklistFolder.owner_id == user_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
folder = result.scalar_one_or_none()
|
||||
|
||||
if not folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Folder not found",
|
||||
)
|
||||
|
||||
# Delete folder and all decks (cascading delete)
|
||||
await db.execute(delete(DecklistFolder).where(DecklistFolder.id == folder_id))
|
||||
|
||||
return {"message": "Folder deleted successfully"}
|
||||
|
||||
Reference in New Issue
Block a user