- Backend: FastAPI + SQLAlchemy + Celery (Python 3.11+) - Frontend: Vue 3 + TypeScript + Pinia + Tailwind - Admin Frontend: separate Vue 3 app for management - Docker Compose: 9 services orchestration - Specs: design prototypes, memory system PRD, product roadmap Cleanup performed: - Removed temporary debug scripts from backend root - Removed deprecated admin_app.py (embedded UI) - Removed duplicate docs from admin-frontend - Updated .gitignore for Vite cache and egg-info
144 lines
4.5 KiB
Python
144 lines
4.5 KiB
Python
"""Reading event API tests."""
|
|
|
|
import pytest
|
|
from httpx import ASGITransport, AsyncClient
|
|
from sqlalchemy import select
|
|
|
|
from app.db.database import get_db
|
|
from app.db.models import MemoryItem
|
|
from app.main import app
|
|
|
|
pytestmark = pytest.mark.asyncio
|
|
|
|
|
|
async def _create_profile(client: AsyncClient) -> str:
|
|
response = await client.post(
|
|
"/api/profiles",
|
|
json={"name": "小明", "gender": "male"},
|
|
)
|
|
assert response.status_code == 201
|
|
return response.json()["id"]
|
|
|
|
|
|
async def test_create_reading_event_updates_stats_and_memory(
|
|
db_session,
|
|
test_user,
|
|
auth_token,
|
|
test_story,
|
|
):
|
|
async def override_get_db():
|
|
yield db_session
|
|
|
|
app.dependency_overrides[get_db] = override_get_db
|
|
transport = ASGITransport(app=app)
|
|
|
|
try:
|
|
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
client.cookies.set("access_token", auth_token)
|
|
profile_id = await _create_profile(client)
|
|
|
|
response = await client.post(
|
|
"/api/reading-events",
|
|
json={
|
|
"child_profile_id": profile_id,
|
|
"story_id": test_story.id,
|
|
"event_type": "completed",
|
|
"reading_time": 120,
|
|
},
|
|
)
|
|
assert response.status_code == 201
|
|
data = response.json()
|
|
assert data["child_profile_id"] == profile_id
|
|
assert data["story_id"] == test_story.id
|
|
assert data["event_type"] == "completed"
|
|
|
|
response = await client.get(f"/api/profiles/{profile_id}")
|
|
assert response.status_code == 200
|
|
profile = response.json()
|
|
assert profile["stories_count"] == 1
|
|
assert profile["total_reading_time"] == 120
|
|
|
|
result = await db_session.execute(
|
|
select(MemoryItem).where(MemoryItem.child_profile_id == profile_id)
|
|
)
|
|
items = result.scalars().all()
|
|
assert len(items) == 1
|
|
assert items[0].type == "recent_story"
|
|
assert items[0].value["story_id"] == test_story.id
|
|
|
|
response = await client.post(
|
|
"/api/reading-events",
|
|
json={
|
|
"child_profile_id": profile_id,
|
|
"story_id": test_story.id,
|
|
"event_type": "skipped",
|
|
"reading_time": 0,
|
|
},
|
|
)
|
|
assert response.status_code == 201
|
|
|
|
result = await db_session.execute(
|
|
select(MemoryItem).where(MemoryItem.child_profile_id == profile_id)
|
|
)
|
|
assert len(result.scalars().all()) == 1
|
|
|
|
response = await client.post(
|
|
"/api/reading-events",
|
|
json={
|
|
"child_profile_id": profile_id,
|
|
"story_id": test_story.id,
|
|
"event_type": "completed",
|
|
"reading_time": 0,
|
|
},
|
|
)
|
|
assert response.status_code == 201
|
|
|
|
response = await client.get(f"/api/profiles/{profile_id}")
|
|
assert response.status_code == 200
|
|
profile = response.json()
|
|
assert profile["stories_count"] == 1
|
|
assert profile["total_reading_time"] == 120
|
|
finally:
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
async def test_reading_event_validation_errors(
|
|
db_session,
|
|
test_user,
|
|
auth_token,
|
|
):
|
|
async def override_get_db():
|
|
yield db_session
|
|
|
|
app.dependency_overrides[get_db] = override_get_db
|
|
transport = ASGITransport(app=app)
|
|
|
|
try:
|
|
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
|
client.cookies.set("access_token", auth_token)
|
|
|
|
response = await client.post(
|
|
"/api/reading-events",
|
|
json={
|
|
"child_profile_id": "not-exist",
|
|
"event_type": "started",
|
|
"reading_time": 0,
|
|
},
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
profile_id = await _create_profile(client)
|
|
|
|
response = await client.post(
|
|
"/api/reading-events",
|
|
json={
|
|
"child_profile_id": profile_id,
|
|
"story_id": 999999,
|
|
"event_type": "completed",
|
|
"reading_time": 0,
|
|
},
|
|
)
|
|
assert response.status_code == 404
|
|
finally:
|
|
app.dependency_overrides.clear()
|