Add FastAPI backend with agent system

This commit is contained in:
2026-03-21 10:13:29 +08:00
parent ed6bab59fe
commit 6ffa07adde
82 changed files with 11138 additions and 0 deletions

View File

View File

View File

View File

@@ -0,0 +1,120 @@
import pytest
from unittest.mock import MagicMock, patch
from app.services.tag_service import TagService, TAG_EXTRACTION_PROMPT, TAG_RELATION_PROMPT
class TestTagService:
"""TagService 单元测试"""
def test_parse_tag_path_single_level(self):
"""测试单层标签路径解析"""
service = TagService(db=MagicMock(), llm_client=MagicMock())
short_name, level, parent_path = service.parse_tag_path("Python")
assert short_name == "Python"
assert level == 1
assert parent_path is None
def test_parse_tag_path_nested(self):
"""测试多层标签路径解析"""
service = TagService(db=MagicMock(), llm_client=MagicMock())
short_name, level, parent_path = service.parse_tag_path("编程语言/Python/异步")
assert short_name == "异步"
assert level == 3
assert parent_path == "编程语言/Python"
def test_parse_tag_path_strips_slashes(self):
"""测试标签路径斜杠处理"""
service = TagService(db=MagicMock(), llm_client=MagicMock())
short_name, level, parent_path = service.parse_tag_path("/后端/框架/")
assert short_name == "框架"
assert level == 2
assert parent_path == "后端"
def test_parse_tag_path_empty_parts(self):
"""测试空路径部分处理"""
service = TagService(db=MagicMock(), llm_client=MagicMock())
short_name, level, parent_path = service.parse_tag_path("a/b/c/d")
assert short_name == "d"
assert level == 4
assert parent_path == "a/b/c"
@patch('app.services.tag_service.KGNode')
@patch('app.services.tag_service.KGEdge')
def test_get_or_create_tag_node_creates_new(self, mock_edge, mock_node):
"""测试创建新标签节点"""
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = None
service = TagService(db=mock_db, llm_client=MagicMock())
tag_info = {"path": "Python", "description": "Python语言"}
result = service.get_or_create_tag_node(tag_info, "user_123")
assert result is not None
mock_db.add.assert_called_once()
mock_db.flush.assert_called_once()
@patch('app.services.tag_service.KGNode')
def test_get_or_create_tag_node_returns_existing(self, mock_node):
"""测试返回已存在的标签节点"""
mock_db = MagicMock()
mock_existing = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = mock_existing
service = TagService(db=mock_db, llm_client=MagicMock())
tag_info = {"path": "Python", "description": "Python语言"}
result = service.get_or_create_tag_node(tag_info, "user_123")
assert result == mock_existing
mock_db.add.assert_not_called()
def test_ensure_parent_tags_creates_parents(self):
"""测试自动创建父标签"""
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = None
service = TagService(db=mock_db, llm_client=MagicMock())
with patch.object(service, 'get_or_create_tag_node') as mock_create:
mock_create.return_value = MagicMock()
result = service.ensure_parent_tags("a/b/c", "user_123")
assert mock_create.call_count == 2
def test_ensure_parent_tags_single_level(self):
"""测试单层标签不创建父标签"""
mock_db = MagicMock()
service = TagService(db=mock_db, llm_client=MagicMock())
with patch.object(service, 'get_or_create_tag_node') as mock_create:
mock_create.return_value = MagicMock()
result = service.ensure_parent_tags("Python", "user_123")
assert mock_create.call_count == 0
@patch('app.services.tag_service.KGNode')
def test_get_related_content_empty_tags(self, mock_kg_node):
"""测试空标签列表返回空结果"""
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = []
service = TagService(db=mock_db, llm_client=MagicMock())
result = service.get_related_content([], "user_123")
assert result == []
def test_tag_extraction_prompt_format(self):
"""测试标签提取提示词格式"""
assert "层级路径格式" in TAG_EXTRACTION_PROMPT
assert "3-8 个标签" in TAG_EXTRACTION_PROMPT
assert "{content}" in TAG_EXTRACTION_PROMPT
def test_tag_relation_prompt_format(self):
"""测试标签关系提示词格式"""
assert "parent_of" in TAG_RELATION_PROMPT
assert "related_to" in TAG_RELATION_PROMPT
assert "synonym_of" in TAG_RELATION_PROMPT
assert "{tag_paths}" in TAG_RELATION_PROMPT