feat: Meeting Assistant MVP - Complete implementation
Enterprise Meeting Knowledge Management System with: Backend (FastAPI): - Authentication proxy with JWT (pj-auth-api integration) - MySQL database with 4 tables (users, meetings, conclusions, actions) - Meeting CRUD with system code generation (C-YYYYMMDD-XX, A-YYYYMMDD-XX) - Dify LLM integration for AI summarization - Excel export with openpyxl - 20 unit tests (all passing) Client (Electron): - Login page with company auth - Meeting list with create/delete - Meeting detail with real-time transcription - Editable transcript textarea (single block, easy editing) - AI summarization with conclusions/action items - 5-second segment recording (efficient for long meetings) Sidecar (Python): - faster-whisper medium model with int8 quantization - ONNX Runtime VAD (lightweight, ~20MB vs PyTorch ~2GB) - Chinese punctuation processing - OpenCC for Traditional Chinese conversion - Anti-hallucination parameters - Auto-cleanup of temp audio files OpenSpec: - add-meeting-assistant-mvp (47 tasks, archived) - add-realtime-transcription (29 tasks, archived) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
138
backend/tests/test_auth.py
Normal file
138
backend/tests/test_auth.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Unit tests for authentication functionality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock, AsyncMock
|
||||
from fastapi.testclient import TestClient
|
||||
from jose import jwt
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestAdminRoleDetection:
|
||||
"""Tests for admin role detection."""
|
||||
|
||||
def test_admin_email_gets_admin_role(self):
|
||||
"""Test that admin email is correctly identified."""
|
||||
from app.config import settings
|
||||
|
||||
admin_email = settings.ADMIN_EMAIL
|
||||
test_email = "regular@example.com"
|
||||
|
||||
# Admin email should be set (either from env or default)
|
||||
assert admin_email is not None
|
||||
assert len(admin_email) > 0
|
||||
assert test_email != admin_email
|
||||
|
||||
@patch("app.routers.auth.settings")
|
||||
def test_create_token_includes_role(self, mock_settings):
|
||||
"""Test that created tokens include the role."""
|
||||
mock_settings.JWT_SECRET = "test-secret"
|
||||
mock_settings.ADMIN_EMAIL = "admin@test.com"
|
||||
|
||||
from app.routers.auth import create_token
|
||||
|
||||
# Test admin token
|
||||
admin_token = create_token("admin@test.com", "admin")
|
||||
admin_payload = jwt.decode(admin_token, "test-secret", algorithms=["HS256"])
|
||||
assert admin_payload["role"] == "admin"
|
||||
|
||||
# Test user token
|
||||
user_token = create_token("user@test.com", "user")
|
||||
user_payload = jwt.decode(user_token, "test-secret", algorithms=["HS256"])
|
||||
assert user_payload["role"] == "user"
|
||||
|
||||
|
||||
class TestTokenValidation:
|
||||
"""Tests for JWT token validation."""
|
||||
|
||||
@patch("app.routers.auth.settings")
|
||||
def test_decode_valid_token(self, mock_settings):
|
||||
"""Test decoding a valid token."""
|
||||
mock_settings.JWT_SECRET = "test-secret"
|
||||
|
||||
from app.routers.auth import create_token, decode_token
|
||||
|
||||
token = create_token("test@example.com", "user")
|
||||
payload = decode_token(token)
|
||||
|
||||
assert payload.email == "test@example.com"
|
||||
assert payload.role == "user"
|
||||
|
||||
@patch("app.routers.auth.settings")
|
||||
def test_decode_invalid_token_raises_error(self, mock_settings):
|
||||
"""Test that invalid tokens raise an error."""
|
||||
mock_settings.JWT_SECRET = "test-secret"
|
||||
|
||||
from app.routers.auth import decode_token
|
||||
from fastapi import HTTPException
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
decode_token("invalid-token")
|
||||
|
||||
assert exc_info.value.status_code == 401
|
||||
|
||||
|
||||
class TestLoginEndpoint:
|
||||
"""Tests for the login endpoint."""
|
||||
|
||||
@pytest.fixture
|
||||
def client(self):
|
||||
"""Create test client."""
|
||||
from app.main import app
|
||||
|
||||
# Skip lifespan for tests
|
||||
app.router.lifespan_context = None
|
||||
return TestClient(app, raise_server_exceptions=False)
|
||||
|
||||
@patch("app.routers.auth.httpx.AsyncClient")
|
||||
@patch("app.routers.auth.settings")
|
||||
async def test_login_success(self, mock_settings, mock_client_class):
|
||||
"""Test successful login."""
|
||||
mock_settings.AUTH_API_URL = "https://auth.test.com/login"
|
||||
mock_settings.ADMIN_EMAIL = "admin@test.com"
|
||||
mock_settings.JWT_SECRET = "test-secret"
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"token": "external-token"}
|
||||
|
||||
mock_client = AsyncMock()
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_client.__aenter__.return_value = mock_client
|
||||
mock_client.__aexit__.return_value = None
|
||||
mock_client_class.return_value = mock_client
|
||||
|
||||
from app.routers.auth import login
|
||||
from app.models import LoginRequest
|
||||
|
||||
result = await login(LoginRequest(email="user@test.com", password="password"))
|
||||
|
||||
assert result.email == "user@test.com"
|
||||
assert result.role == "user"
|
||||
assert result.token is not None
|
||||
|
||||
@patch("app.routers.auth.httpx.AsyncClient")
|
||||
@patch("app.routers.auth.settings")
|
||||
async def test_login_admin_gets_admin_role(self, mock_settings, mock_client_class):
|
||||
"""Test that admin email gets admin role."""
|
||||
mock_settings.AUTH_API_URL = "https://auth.test.com/login"
|
||||
mock_settings.ADMIN_EMAIL = "admin@test.com"
|
||||
mock_settings.JWT_SECRET = "test-secret"
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
|
||||
mock_client = AsyncMock()
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_client.__aenter__.return_value = mock_client
|
||||
mock_client.__aexit__.return_value = None
|
||||
mock_client_class.return_value = mock_client
|
||||
|
||||
from app.routers.auth import login
|
||||
from app.models import LoginRequest
|
||||
|
||||
result = await login(LoginRequest(email="admin@test.com", password="password"))
|
||||
|
||||
assert result.role == "admin"
|
||||
Reference in New Issue
Block a user