Frontend Features: - Add ProtectedRoute component with token expiry validation - Create AdminDashboardPage with system statistics and user management - Create AuditLogsPage with filtering and pagination - Add admin-only navigation (Shield icon) for ymirliu@panjit.com.tw - Add admin API methods to apiV2 service - Add admin type definitions (SystemStats, AuditLog, etc.) Token Management: - Auto-redirect to login on token expiry - Check authentication on route change - Show loading state during auth check - Admin privilege verification Backend Testing: - Add pytest configuration (pytest.ini) - Create test fixtures (conftest.py) - Add unit tests for auth, tasks, and admin endpoints - Add integration tests for complete workflows - Test user isolation and admin access control Documentation: - Add TESTING.md with comprehensive testing guide - Include test running instructions - Document fixtures and best practices Routes: - /admin - Admin dashboard (admin only) - /admin/audit-logs - Audit logs viewer (admin only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
"""
|
|
Unit tests for task management endpoints
|
|
"""
|
|
|
|
import pytest
|
|
from app.models.task import Task
|
|
|
|
|
|
class TestTasks:
|
|
"""Test task management endpoints"""
|
|
|
|
def test_create_task(self, client, auth_token):
|
|
"""Test task creation"""
|
|
response = client.post(
|
|
'/api/v2/tasks/',
|
|
headers={'Authorization': f'Bearer {auth_token}'},
|
|
json={
|
|
'filename': 'test.pdf',
|
|
'file_type': 'application/pdf'
|
|
}
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
data = response.json()
|
|
assert 'task_id' in data
|
|
assert data['filename'] == 'test.pdf'
|
|
assert data['status'] == 'pending'
|
|
|
|
def test_list_tasks(self, client, auth_token, test_task):
|
|
"""Test listing user tasks"""
|
|
response = client.get(
|
|
'/api/v2/tasks/',
|
|
headers={'Authorization': f'Bearer {auth_token}'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert 'tasks' in data
|
|
assert 'total' in data
|
|
assert len(data['tasks']) > 0
|
|
|
|
def test_get_task(self, client, auth_token, test_task):
|
|
"""Test get single task"""
|
|
response = client.get(
|
|
f'/api/v2/tasks/{test_task.task_id}',
|
|
headers={'Authorization': f'Bearer {auth_token}'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data['task_id'] == test_task.task_id
|
|
|
|
def test_get_task_stats(self, client, auth_token, test_task):
|
|
"""Test get task statistics"""
|
|
response = client.get(
|
|
'/api/v2/tasks/stats',
|
|
headers={'Authorization': f'Bearer {auth_token}'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert 'total' in data
|
|
assert 'pending' in data
|
|
assert 'processing' in data
|
|
assert 'completed' in data
|
|
assert 'failed' in data
|
|
|
|
def test_delete_task(self, client, auth_token, test_task):
|
|
"""Test task deletion"""
|
|
response = client.delete(
|
|
f'/api/v2/tasks/{test_task.task_id}',
|
|
headers={'Authorization': f'Bearer {auth_token}'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
def test_user_isolation(self, client, db, test_user):
|
|
"""Test that users can only access their own tasks"""
|
|
# Create another user
|
|
from app.models.user import User
|
|
other_user = User(email="other@example.com", display_name="Other User")
|
|
db.add(other_user)
|
|
db.commit()
|
|
|
|
# Create task for other user
|
|
other_task = Task(
|
|
user_id=other_user.id,
|
|
task_id="other-task-123",
|
|
filename="other.pdf",
|
|
status="pending"
|
|
)
|
|
db.add(other_task)
|
|
db.commit()
|
|
|
|
# Create token for test_user
|
|
from app.core.security import create_access_token
|
|
token = create_access_token({"sub": str(test_user.id)})
|
|
|
|
# Try to access other user's task
|
|
response = client.get(
|
|
f'/api/v2/tasks/{other_task.task_id}',
|
|
headers={'Authorization': f'Bearer {token}'}
|
|
)
|
|
|
|
assert response.status_code == 404 # Task not found (user isolation)
|