Files
smart-support/backend/tests/conftest.py
Yaojia Wang 0e78e5b06b feat: complete phase 5 -- error hardening, frontend, Docker, demo, docs
Backend:
- ConversationTracker: Protocol + PostgresConversationTracker for lifecycle tracking
- Error handler: ErrorCategory enum, classify_error(), with_retry() exponential backoff
- Wire PostgresAnalyticsRecorder + ConversationTracker into ws_handler
- Rate limiting (10 msg/10s per thread), edge case hardening
- Health endpoint GET /api/health, version 0.5.0
- Demo seed data script + sample OpenAPI spec

Frontend (all new):
- React Router with NavBar (Chat / Replay / Dashboard / Review)
- ReplayListPage + ReplayPage with ReplayTimeline component
- DashboardPage with MetricCard, range selector, zero-state
- ReviewPage for OpenAPI classification review
- ErrorBanner for WebSocket disconnect handling
- API client (api.ts) with typed fetch wrappers

Infrastructure:
- Frontend Dockerfile (multi-stage node -> nginx)
- nginx.conf with SPA routing + API/WS proxy
- docker-compose.yml with frontend service + healthchecks
- .env.example files (root + backend)

Documentation:
- README.md with quick start and architecture
- Agent configuration guide
- OpenAPI import guide
- Deployment guide
- Demo script

48 new tests, 449 total passing, 92.87% coverage
2026-03-31 21:20:06 +02:00

80 lines
2.1 KiB
Python

"""Shared test fixtures and marker registration."""
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
import yaml
from app.config import Settings
from app.registry import AgentRegistry
from app.session_manager import SessionManager
if TYPE_CHECKING:
from pathlib import Path
@pytest.fixture(autouse=True)
def clear_rate_limit_state() -> None:
"""Clear module-level rate limit state between tests to prevent leakage."""
import app.ws_handler as ws_handler
ws_handler._thread_timestamps.clear()
yield
ws_handler._thread_timestamps.clear()
@pytest.fixture
def test_settings() -> Settings:
return Settings(
database_url="postgresql://test:test@localhost:5432/test_db",
llm_provider="anthropic",
llm_model="claude-sonnet-4-6",
anthropic_api_key="test-key",
)
@pytest.fixture
def sample_yaml_path(tmp_path: Path) -> Path:
data = {
"agents": [
{
"name": "test_reader",
"description": "A test read agent",
"permission": "read",
"tools": ["get_order_status"],
},
{
"name": "test_writer",
"description": "A test write agent",
"permission": "write",
"personality": {
"tone": "formal",
"greeting": "Greetings.",
"escalation_message": "Escalating now.",
},
"tools": ["cancel_order"],
},
{
"name": "test_fallback",
"description": "A fallback agent",
"permission": "read",
"tools": ["fallback_respond"],
},
]
}
path = tmp_path / "test_agents.yaml"
path.write_text(yaml.dump(data), encoding="utf-8")
return path
@pytest.fixture
def sample_registry(sample_yaml_path: Path) -> AgentRegistry:
return AgentRegistry.load(sample_yaml_path)
@pytest.fixture
def session_manager() -> SessionManager:
return SessionManager(session_ttl_seconds=60)