Implemented proposals from comprehensive QA review: 1. extend-csrf-protection - Add POST to CSRF protected methods in frontend - Global CSRF middleware for all state-changing operations - Update tests with CSRF token fixtures 2. tighten-cors-websocket-security - Replace wildcard CORS with explicit method/header lists - Disable query parameter auth in production (code 4002) - Add per-user WebSocket connection limit (max 5, code 4005) 3. shorten-jwt-expiry - Reduce JWT expiry from 7 days to 60 minutes - Add refresh token support with 7-day expiry - Implement token rotation on refresh - Frontend auto-refresh when token near expiry (<5 min) 4. fix-frontend-quality - Add React.lazy() code splitting for all pages - Fix useCallback dependency arrays (Dashboard, Comments) - Add localStorage data validation in AuthContext - Complete i18n for AttachmentUpload component 5. enhance-backend-validation - Add SecurityAuditMiddleware for access denied logging - Add ErrorSanitizerMiddleware for production error messages - Protect /health/detailed with admin authentication - Add input length validation (comment 5000, desc 10000) All 521 backend tests passing. Frontend builds successfully. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
118 lines
3.6 KiB
Python
118 lines
3.6 KiB
Python
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from app.main import app
|
|
from app.models import User, Space
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
# Mock user for testing
|
|
def get_mock_user():
|
|
user = MagicMock(spec=User)
|
|
user.id = "test-user-id"
|
|
user.email = "test@example.com"
|
|
user.name = "Test User"
|
|
user.is_active = True
|
|
user.is_system_admin = False
|
|
user.department_id = "dept-1"
|
|
return user
|
|
|
|
|
|
def get_mock_admin_user():
|
|
user = get_mock_user()
|
|
user.is_system_admin = True
|
|
return user
|
|
|
|
|
|
class TestSpacesAPI:
|
|
"""Test Spaces API endpoints."""
|
|
|
|
@patch("app.api.spaces.router.get_current_user")
|
|
@patch("app.api.spaces.router.get_db")
|
|
def test_list_spaces_empty(self, mock_db, mock_get_user):
|
|
"""Test listing spaces when none exist."""
|
|
mock_user = get_mock_user()
|
|
mock_get_user.return_value = mock_user
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.filter.return_value.all.return_value = []
|
|
mock_db.return_value = mock_session
|
|
|
|
# Skip actual auth for unit test
|
|
with patch("app.middleware.auth.get_current_user", return_value=mock_user):
|
|
response = client.get(
|
|
"/api/spaces",
|
|
headers={"Authorization": "Bearer test-token"}
|
|
)
|
|
|
|
# This will fail auth in real scenario, but tests the route exists
|
|
assert response.status_code in [200, 401]
|
|
|
|
@patch("app.api.spaces.router.get_current_user")
|
|
def test_create_space_requires_auth(self, mock_get_user):
|
|
"""Test that creating a space requires authentication."""
|
|
response = client.post(
|
|
"/api/spaces",
|
|
json={"name": "Test Space", "description": "Test"}
|
|
)
|
|
assert response.status_code == 401 # 401 for unauthenticated, 403 for unauthorized
|
|
|
|
def test_space_routes_exist(self):
|
|
"""Test that all space routes are registered."""
|
|
routes = [route.path for route in app.routes if hasattr(route, 'path')]
|
|
assert "/api/spaces" in routes
|
|
assert "/api/spaces/{space_id}" in routes
|
|
|
|
|
|
class TestSpaceModel:
|
|
"""Test Space model."""
|
|
|
|
def test_space_creation(self):
|
|
"""Test Space model can be instantiated."""
|
|
space = Space(
|
|
id="test-id",
|
|
name="Test Space",
|
|
description="A test space",
|
|
owner_id="owner-id",
|
|
is_active=True,
|
|
)
|
|
assert space.name == "Test Space"
|
|
assert space.is_active == True
|
|
|
|
|
|
class TestSpacePermissions:
|
|
"""Test space permission logic."""
|
|
|
|
def test_admin_has_access(self):
|
|
"""Test that admin users have access to all spaces."""
|
|
from app.middleware.auth import check_space_access, check_space_edit_access
|
|
|
|
admin = get_mock_admin_user()
|
|
space = MagicMock()
|
|
space.owner_id = "other-user"
|
|
|
|
assert check_space_access(admin, space) == True
|
|
assert check_space_edit_access(admin, space) == True
|
|
|
|
def test_owner_can_edit(self):
|
|
"""Test that space owner can edit."""
|
|
from app.middleware.auth import check_space_edit_access
|
|
|
|
user = get_mock_user()
|
|
space = MagicMock()
|
|
space.owner_id = user.id
|
|
|
|
assert check_space_edit_access(user, space) == True
|
|
|
|
def test_non_owner_cannot_edit(self):
|
|
"""Test that non-owner cannot edit."""
|
|
from app.middleware.auth import check_space_edit_access
|
|
|
|
user = get_mock_user()
|
|
space = MagicMock()
|
|
space.owner_id = "other-user-id"
|
|
|
|
assert check_space_edit_access(user, space) == False
|