123
This commit is contained in:
226
backend/tests/test_api_tasks.py
Normal file
226
backend/tests/test_api_tasks.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user