Backend (Phase 1-6): - Pydantic schemas for request/response validation - Service layer (SSH Key, Server, Repo, Sync) - API routes with authentication - FastAPI main application with lifespan management - ORM models (SshKey, Server, Repo, SyncLog) Frontend (Phase 7): - Vue 3 + Element Plus + Pinia + Vue Router - API client with Axios and interceptors - State management stores - All page components (Dashboard, Servers, Repos, SyncLogs, SshKeys, Settings) Deployment (Phase 8): - README with quick start guide - Startup script (start.sh) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
3.7 KiB
Python
130 lines
3.7 KiB
Python
import sys
|
|
import pytest
|
|
from pathlib import Path
|
|
from typing import Generator
|
|
|
|
# Add backend to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker, Session
|
|
|
|
# NOTE: This import will fail until models are created in Task 2.1
|
|
# This is expected behavior - the models module doesn't exist yet
|
|
from app.models import Base
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def db_path(tmp_path):
|
|
"""临时数据库路径."""
|
|
return tmp_path / "test.db"
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def db_engine(db_path):
|
|
"""临时数据库引擎."""
|
|
engine = create_engine(f"sqlite:///{db_path}", connect_args={"check_same_thread": False})
|
|
Base.metadata.create_all(engine)
|
|
yield engine
|
|
engine.dispose()
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def db_session(db_engine):
|
|
"""临时数据库会话."""
|
|
SessionLocal = sessionmaker(bind=db_engine, autocommit=False, autoflush=False)
|
|
session = SessionLocal()
|
|
yield session
|
|
session.close()
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def test_encrypt_key():
|
|
"""测试加密密钥."""
|
|
import base64
|
|
return base64.b64encode(b'test-key-32-bytes-long-123456789').decode()
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def test_env_vars(db_path, test_encrypt_key, monkeypatch):
|
|
"""设置测试环境变量."""
|
|
# Clear global settings to ensure fresh config
|
|
import app.config
|
|
app.config._settings = None
|
|
|
|
monkeypatch.setenv("GM_ENCRYPT_KEY", test_encrypt_key)
|
|
monkeypatch.setenv("GM_API_TOKEN", "test-token")
|
|
monkeypatch.setenv("GM_DATA_DIR", str(db_path.parent))
|
|
return {
|
|
"GM_ENCRYPT_KEY": test_encrypt_key,
|
|
"GM_API_TOKEN": "test-token",
|
|
}
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def client(db_session: Session, test_env_vars: dict, monkeypatch):
|
|
"""
|
|
FastAPI test client fixture.
|
|
|
|
Provides a test client for the FastAPI application with:
|
|
- In-memory database session
|
|
- Test environment variables
|
|
- Disabled lifespan (for faster tests)
|
|
|
|
Args:
|
|
db_session: Database session fixture
|
|
test_env_vars: Test environment variables fixture
|
|
monkeypatch: Pytest monkeypatch fixture
|
|
|
|
Yields:
|
|
FastAPI test client
|
|
|
|
Example:
|
|
def test_create_ssh_key(client):
|
|
response = client.post(
|
|
"/api/ssh-keys",
|
|
json={"name": "test-key", "private_key": "..."},
|
|
headers={"Authorization": "Bearer test-token"}
|
|
)
|
|
assert response.status_code == 201
|
|
"""
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
# Mock the lifespan context manager
|
|
async def mock_lifespan(app): # noqa: ARG001 - Unused app parameter
|
|
# Database is already initialized by db_session fixture
|
|
yield
|
|
|
|
# Import after setting env vars
|
|
import app.database
|
|
import app.config
|
|
|
|
# Initialize database with test session
|
|
app.database._engine = db_session.bind
|
|
app.database._session_factory = sessionmaker(bind=db_session.bind, autocommit=False, autoflush=False)
|
|
|
|
# Create app with mocked lifespan
|
|
from app.main import create_app
|
|
test_app = create_app(lifespan_handler=mock_lifespan)
|
|
|
|
# Override get_db_session dependency to use test session
|
|
from app.api import deps
|
|
|
|
def override_get_db_session():
|
|
try:
|
|
yield db_session
|
|
finally:
|
|
pass
|
|
|
|
test_app.dependency_overrides[deps.get_db_session] = override_get_db_session
|
|
|
|
with TestClient(test_app) as test_client:
|
|
yield test_client
|
|
|
|
# Clean up
|
|
test_app.dependency_overrides = {}
|
|
app.database._engine = None
|
|
app.database._session_factory = None
|
|
app.config._settings = None
|