Files
erp-ass/backend/tests/test_requirement_service.py
dazhuang acd73431ae feat: implement ERP AI Assistant Phase 1
Backend (FastAPI + SQLAlchemy + Claude API + RAG):
- Config management with Pydantic v2
- Database engine with connection pooling and SQL injection prevention
- AI engine with Claude API integration (support custom base URL)
- RAG engine with ChromaDB and sentence-transformers
- Requirement analysis service
- Config generation service
- Executor engine with SQL validation
- REST API endpoints: /analyze, /generate, /execute

Frontend (Vue 3 + Element Plus + Pinia):
- Complete 3-step workflow: analyze → generate → execute
- Step indicator with progress visualization
- Analysis result display with field table
- SQL preview with monospace font
- Execute confirmation dialog with safety warning
- Execution result display
- State management with Pinia
- API service integration

Security:
- SQL injection prevention with parameterized queries
- Dangerous SQL operation blocking
- Database password URL encoding
- Transaction auto-rollback
- Pydantic config validation

Features:
- Natural language requirement analysis
- Automated SQL configuration generation
- Safe execution with human review
- LAN access support
- Custom Claude API endpoint support

Documentation:
- README with quick start guide
- Quick start guide
- LAN access configuration
- Dependency fixes guide
- Claude API configuration
- Git operation guide
- Implementation report

Dependencies fixed:
- numpy<2.0.0 for chromadb compatibility
- sentence-transformers==2.7.0 for huggingface_hub compatibility

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 14:23:20 +00:00

116 lines
4.4 KiB
Python

"""Tests for Requirement Service.
This module tests the RequirementService class for requirement analysis.
"""
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
from app.services.requirement_service import RequirementService
@pytest.mark.asyncio
async def test_analyze_requirement():
"""Test requirement analysis with mocked dependencies."""
# Create service with mocked engines
with patch('app.services.requirement_service.ClaudeEngine') as MockClaudeEngine, \
patch('app.services.requirement_service.RAGEngine') as MockRAGEngine, \
patch('app.services.requirement_service.DatabaseEngine') as MockDBEngine:
# Setup mocks
mock_ai_engine = MagicMock()
mock_ai_engine.call_claude = AsyncMock(return_value='{"功能名称": "销售订单管理", "功能类型": "列表页面"}')
mock_ai_engine.parse_json_response = MagicMock(return_value={
"功能名称": "销售订单管理",
"功能类型": "列表页面"
})
MockClaudeEngine.return_value = mock_ai_engine
mock_rag_engine = MagicMock()
mock_rag_engine.search = MagicMock(return_value=[
{"content": "Sample knowledge", "metadata": {"source": "docs"}}
])
MockRAGEngine.return_value = mock_rag_engine
mock_db_engine = MagicMock()
mock_db_engine.execute_sql = MagicMock(return_value=[("SYS_FORM",), ("SYS_MENU",)])
MockDBEngine.return_value = mock_db_engine
# Create service and test
service = RequirementService()
result = await service.analyze(
user_input="创建一个销售订单管理页面",
session_id="test-session"
)
assert result is not None
assert "功能名称" in result
assert result["功能名称"] == "销售订单管理"
@pytest.mark.asyncio
async def test_analyze_requirement_without_session_id():
"""Test that session_id is auto-generated if not provided."""
with patch('app.services.requirement_service.ClaudeEngine') as MockClaudeEngine, \
patch('app.services.requirement_service.RAGEngine') as MockRAGEngine, \
patch('app.services.requirement_service.DatabaseEngine') as MockDBEngine:
# Setup mocks
mock_ai_engine = MagicMock()
mock_ai_engine.call_claude = AsyncMock(return_value='{"功能名称": "测试功能"}')
mock_ai_engine.parse_json_response = MagicMock(return_value={"功能名称": "测试功能"})
MockClaudeEngine.return_value = mock_ai_engine
mock_rag_engine = MagicMock()
mock_rag_engine.search = MagicMock(return_value=[])
MockRAGEngine.return_value = mock_rag_engine
mock_db_engine = MagicMock()
mock_db_engine.execute_sql = MagicMock(return_value=[])
MockDBEngine.return_value = mock_db_engine
# Test without session_id
service = RequirementService()
result = await service.analyze(user_input="测试输入")
assert result is not None
assert "功能名称" in result
@pytest.mark.asyncio
async def test_get_existing_tables_success():
"""Test successful retrieval of existing tables."""
with patch('app.services.requirement_service.ClaudeEngine'), \
patch('app.services.requirement_service.RAGEngine'), \
patch('app.services.requirement_service.DatabaseEngine') as MockDBEngine:
mock_db_engine = MagicMock()
mock_db_engine.execute_sql = MagicMock(return_value=[
("SYS_FORM",),
("SYS_MENU",),
("SYS_USER",)
])
MockDBEngine.return_value = mock_db_engine
service = RequirementService()
tables = service._get_existing_tables("测试")
assert "SYS_FORM" in tables
assert "SYS_MENU" in tables
assert "SYS_USER" in tables
@pytest.mark.asyncio
async def test_get_existing_tables_failure():
"""Test handling of database query failure."""
with patch('app.services.requirement_service.ClaudeEngine'), \
patch('app.services.requirement_service.RAGEngine'), \
patch('app.services.requirement_service.DatabaseEngine') as MockDBEngine:
mock_db_engine = MagicMock()
mock_db_engine.execute_sql = MagicMock(side_effect=Exception("DB Error"))
MockDBEngine.return_value = mock_db_engine
service = RequirementService()
tables = service._get_existing_tables("测试")
assert "无法获取现有表信息" in tables