Files
weibo_signin/backend/tests/test_api_tasks.py
2026-03-09 14:05:00 +08:00

227 lines
6.6 KiB
Python

"""
Tests for API_Service task management endpoints.
"""
import pytest
import pytest_asyncio
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession
from shared.models import User, Account, Task
from auth_service.app.utils.security import create_access_token
from shared.crypto import encrypt_cookie, derive_key
@pytest_asyncio.fixture
async def test_user(db_session: AsyncSession) -> User:
"""Create a test user."""
user = User(
username="testuser",
email="test@example.com",
hashed_password="hashed_password",
)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
@pytest_asyncio.fixture
async def test_account(db_session: AsyncSession, test_user: User) -> Account:
"""Create a test account."""
key = derive_key("test-cookie-key")
ciphertext, iv = encrypt_cookie("test_cookie_data", key)
account = Account(
user_id=test_user.id,
weibo_user_id="123456",
remark="Test Account",
encrypted_cookies=ciphertext,
iv=iv,
status="pending",
)
db_session.add(account)
await db_session.commit()
await db_session.refresh(account)
return account
@pytest_asyncio.fixture
async def auth_headers(test_user: User) -> dict:
"""Generate JWT auth headers for test user."""
token = create_access_token({"sub": test_user.id})
return {"Authorization": f"Bearer {token}"}
@pytest.mark.asyncio
async def test_create_task_valid_cron(
db_session: AsyncSession,
test_user: User,
test_account: Account,
auth_headers: dict,
):
"""Test creating a task with valid cron expression."""
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
f"/api/v1/accounts/{test_account.id}/tasks",
json={"cron_expression": "0 9 * * *"},
headers=auth_headers,
)
assert response.status_code == 201
data = response.json()
assert data["success"] is True
assert data["data"]["cron_expression"] == "0 9 * * *"
assert data["data"]["is_enabled"] is True
assert data["data"]["account_id"] == test_account.id
@pytest.mark.asyncio
async def test_create_task_invalid_cron(
db_session: AsyncSession,
test_user: User,
test_account: Account,
auth_headers: dict,
):
"""Test creating a task with invalid cron expression."""
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
f"/api/v1/accounts/{test_account.id}/tasks",
json={"cron_expression": "invalid cron"},
headers=auth_headers,
)
assert response.status_code == 400
data = response.json()
assert data["success"] is False
@pytest.mark.asyncio
async def test_list_tasks(
db_session: AsyncSession,
test_user: User,
test_account: Account,
auth_headers: dict,
):
"""Test listing tasks for an account."""
# Create two tasks
task1 = Task(account_id=test_account.id, cron_expression="0 9 * * *", is_enabled=True)
task2 = Task(account_id=test_account.id, cron_expression="0 18 * * *", is_enabled=False)
db_session.add_all([task1, task2])
await db_session.commit()
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.get(
f"/api/v1/accounts/{test_account.id}/tasks",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert len(data["data"]) == 2
@pytest.mark.asyncio
async def test_update_task(
db_session: AsyncSession,
test_user: User,
test_account: Account,
auth_headers: dict,
):
"""Test updating a task (enable/disable)."""
task = Task(account_id=test_account.id, cron_expression="0 9 * * *", is_enabled=True)
db_session.add(task)
await db_session.commit()
await db_session.refresh(task)
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.put(
f"/api/v1/tasks/{task.id}",
json={"is_enabled": False},
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["is_enabled"] is False
@pytest.mark.asyncio
async def test_delete_task(
db_session: AsyncSession,
test_user: User,
test_account: Account,
auth_headers: dict,
):
"""Test deleting a task."""
task = Task(account_id=test_account.id, cron_expression="0 9 * * *", is_enabled=True)
db_session.add(task)
await db_session.commit()
await db_session.refresh(task)
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.delete(
f"/api/v1/tasks/{task.id}",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
# Verify task is deleted
from sqlalchemy import select
result = await db_session.execute(select(Task).where(Task.id == task.id))
deleted_task = result.scalar_one_or_none()
assert deleted_task is None
@pytest.mark.asyncio
async def test_access_other_user_task_forbidden(
db_session: AsyncSession,
test_account: Account,
):
"""Test that users cannot access tasks from other users' accounts."""
# Create another user
other_user = User(
username="otheruser",
email="other@example.com",
hashed_password="hashed_password",
)
db_session.add(other_user)
await db_session.commit()
await db_session.refresh(other_user)
# Create a task for test_account
task = Task(account_id=test_account.id, cron_expression="0 9 * * *", is_enabled=True)
db_session.add(task)
await db_session.commit()
await db_session.refresh(task)
# Try to access with other_user's token
other_token = create_access_token({"sub": other_user.id})
other_headers = {"Authorization": f"Bearer {other_token}"}
from api_service.app.main import app
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.put(
f"/api/v1/tasks/{task.id}",
json={"is_enabled": False},
headers=other_headers,
)
assert response.status_code == 403