fix: Implement /api/anime/add endpoint correctly

- Fixed 501 Not Implemented error by replacing non-existent AddSeries method
- Added Serie import from src.core.entities.series
- Implemented proper series creation using Serie class following CLI pattern
- Added input validation for empty link and name fields
- Series are now correctly added to series_app.List using add() method
- Call refresh_series_list() to update cache after adding

Tests:
- Added test for unauthorized access (401)
- Added test for successful addition with authentication (200)
- Added test for empty name validation (400)
- Added test for empty link validation (400)
- Updated FakeSeriesApp mock to support add() and refresh_series_list()

All tests passing.
This commit is contained in:
Lukas 2025-10-28 19:36:16 +01:00
parent 02764f7e6f
commit adfbdf56d0
2 changed files with 110 additions and 15 deletions

View File

@ -3,6 +3,7 @@ from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from src.core.entities.series import Serie
from src.server.utils.dependencies import ( from src.server.utils.dependencies import (
get_optional_series_app, get_optional_series_app,
get_series_app, get_series_app,
@ -540,24 +541,50 @@ async def add_series(
HTTPException: If adding the series fails HTTPException: If adding the series fails
""" """
try: try:
if not hasattr(series_app, "AddSeries"): # Validate inputs
if not request.link or not request.link.strip():
raise HTTPException( raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, status_code=status.HTTP_400_BAD_REQUEST,
detail="Add series functionality not available", detail="Series link cannot be empty",
) )
result = series_app.AddSeries(request.link, request.name) if not request.name or not request.name.strip():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Series name cannot be empty",
)
# Check if series_app has the List attribute
if not hasattr(series_app, "List"):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="Series list functionality not available",
)
# Create a new Serie object
# Following the pattern from CLI:
# Serie(key, name, site, folder, episodeDict)
# The key and folder are both the link in this case
# episodeDict is empty {} for a new series
serie = Serie(
key=request.link.strip(),
name=request.name.strip(),
site="aniworld.to",
folder=request.name.strip(),
episodeDict={}
)
# Add the series to the list
series_app.List.add(serie)
# Refresh the series list to update the cache
if hasattr(series_app, "refresh_series_list"):
series_app.refresh_series_list()
if result:
return { return {
"status": "success", "status": "success",
"message": f"Successfully added series: {request.name}" "message": f"Successfully added series: {request.name}"
} }
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Failed to add series - series may already exist",
)
except HTTPException: except HTTPException:
raise raise
except Exception as exc: except Exception as exc:

View File

@ -43,6 +43,16 @@ class FakeSeriesApp:
"""Trigger rescan with callback.""" """Trigger rescan with callback."""
callback() callback()
def add(self, serie):
"""Add a serie to the list."""
# Check if already exists
if not any(s.key == serie.key for s in self._items):
self._items.append(serie)
def refresh_series_list(self):
"""Refresh series list."""
pass
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def reset_auth_state(): def reset_auth_state():
@ -144,3 +154,61 @@ async def test_get_anime_detail_endpoint_unauthorized():
response = await client.get("/api/v1/anime/1") response = await client.get("/api/v1/anime/1")
# Should work or require auth # Should work or require auth
assert response.status_code in (200, 401, 404, 503) assert response.status_code in (200, 401, 404, 503)
@pytest.mark.asyncio
async def test_add_series_endpoint_unauthorized():
"""Test POST /api/anime/add without authentication."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.post(
"/api/anime/add",
json={"link": "test-link", "name": "Test Anime"}
)
# Should require auth
assert response.status_code == 401
@pytest.mark.asyncio
async def test_add_series_endpoint_authenticated(authenticated_client):
"""Test POST /api/anime/add with authentication."""
response = await authenticated_client.post(
"/api/anime/add",
json={"link": "test-anime-link", "name": "Test New Anime"}
)
# The endpoint should succeed (returns 200 or may fail if series exists)
assert response.status_code in (200, 400)
data = response.json()
if response.status_code == 200:
assert data["status"] == "success"
assert "Test New Anime" in data["message"]
@pytest.mark.asyncio
async def test_add_series_endpoint_empty_name(authenticated_client):
"""Test POST /api/anime/add with empty name."""
response = await authenticated_client.post(
"/api/anime/add",
json={"link": "test-link", "name": ""}
)
# Should return 400 for empty name
assert response.status_code == 400
data = response.json()
assert "name" in data["detail"].lower()
@pytest.mark.asyncio
async def test_add_series_endpoint_empty_link(authenticated_client):
"""Test POST /api/anime/add with empty link."""
response = await authenticated_client.post(
"/api/anime/add",
json={"link": "", "name": "Test Anime"}
)
# Should return 400 for empty link
assert response.status_code == 400
data = response.json()
assert "link" in data["detail"].lower()