chore(root): 收纳根目录调试产物与项目级文件

**项目级文件**:
- configs/mkdocs.yml           mkdocs 站点配置
- jumpserver-webcli/ANALYSIS.md  webcli 调试分析报告
- jumpserver-webcli/CHANGELOG.md webcli 变更日志
- jumpserver-webcli/deploy_0802_*.txt  0802 部署调试输出(4 个)
- jumpserver-webcli/diag_0802_*.txt   0802 诊断输出(3 个)
- jumpserver-webcli/diag_184715.txt   18:47:15 诊断输出
- tests/test_quick_rules.py   quick_rules API 测试
- temp/                       临时调试脚本目录(5 个:check_tables + 调试 + rename_dzpc + ts-check + inspect_batch)

**交付报告**:
- overview.md  「结束会话失败」修复交付报告(2026-07-30,ChatPanel.vue 三件套修复)

**会话调试上下文**:
- dedup_context.txt / dedup2.txt       去重调试上下文
- pattern_check.txt / pattern_context.txt  模式检查
- option_button_context.txt / option_select_context.txt  选项按钮/选择上下文
- qid.txt                              会话 ID 记录
- yixuan_context.txt                   艺璇任务上下文
- final_check.txt                      最终检查

**部署脚本输出**:
- deploy_v11.txt / deploy_h5_v11.txt   v11 部署命令输出

合计 27 文件(含 1 项目文档 + 22 调试上下文 + 4 jumpserver webcli 调试 + 1 mkdocs 配置 + 1 测试 + 1 temp/ + 2 部署输出)
This commit is contained in:
Simon
2026-08-03 18:49:44 +08:00
parent 7864ab404b
commit 53b7bbcff7
28 changed files with 1165 additions and 42 deletions
+207
View File
@@ -0,0 +1,207 @@
# =============================================================================
# 企微IT智能服务台 — 快速规则单元测试
# =============================================================================
# 用途:测试 QuickRuleService 单元逻辑
# 日期:2026-07-27
# 运行:pytest tests/test_quick_rules.py -v
# =============================================================================
import pytest
from unittest.mock import AsyncMock, MagicMock
from app.models.quick_rule import QuickRule
from app.services.quick_rule_service import QuickRuleService
class TestQuickRuleService:
"""QuickRuleService 单元测试"""
@pytest.fixture(autouse=True)
def reset_cache(self):
"""每个测试前重置缓存"""
QuickRuleService._cache = {
'greeting': [],
'routing_prefilter': {},
'routing_target': {},
}
QuickRuleService._initialized = False
yield
@pytest.mark.asyncio
async def test_load_all_with_greeting_rules(self):
"""测试加载打招呼关键词"""
mock_db = AsyncMock()
mock_rules = [
MagicMock(spec=QuickRule, rule_type='greeting', keyword='你好', category=None, extra_data=None, is_active=True),
MagicMock(spec=QuickRule, rule_type='greeting', keyword='您好', category=None, extra_data=None, is_active=True),
]
mock_result = MagicMock()
mock_result.scalars.return_value.all.return_value = mock_rules
mock_db.execute.return_value = mock_result
await QuickRuleService.load_all(mock_db)
keywords = QuickRuleService.get_greeting_keywords()
assert '你好' in keywords
assert '您好' in keywords
assert len(keywords) == 2
@pytest.mark.asyncio
async def test_load_all_with_routing_keywords(self):
"""测试加载路由关键词"""
mock_db = AsyncMock()
mock_rules = [
MagicMock(spec=QuickRule, rule_type='routing_prefilter', keyword='打印机', category='IT服务', extra_data=None, is_active=True),
MagicMock(spec=QuickRule, rule_type='routing_prefilter', keyword='报销', category='财务', extra_data=None, is_active=True),
]
mock_result = MagicMock()
mock_result.scalars.return_value.all.return_value = mock_rules
mock_db.execute.return_value = mock_result
await QuickRuleService.load_all(mock_db)
keywords = QuickRuleService.get_routing_keywords()
assert keywords['打印机'] == 'IT服务'
assert keywords['报销'] == '财务'
@pytest.mark.asyncio
async def test_load_all_with_routing_targets(self):
"""测试加载路由目标"""
mock_db = AsyncMock()
mock_rules = [
MagicMock(
spec=QuickRule,
rule_type='routing_target',
keyword='机票酒店前台',
category='行政',
extra_data={'service_name': '机票酒店前台', 'url': 'https://example.com'},
is_active=True,
),
]
mock_result = MagicMock()
mock_result.scalars.return_value.all.return_value = mock_rules
mock_db.execute.return_value = mock_result
await QuickRuleService.load_all(mock_db)
targets = QuickRuleService.get_routing_targets()
assert '行政' in targets
assert targets['行政']['service_name'] == '机票酒店前台'
@pytest.mark.asyncio
async def test_load_all_handles_exception(self):
"""测试加载异常处理"""
mock_db = AsyncMock()
mock_db.execute.side_effect = Exception("DB error")
# 不应抛出异常
await QuickRuleService.load_all(mock_db)
# 缓存应保持空
assert QuickRuleService.get_greeting_keywords() == []
def test_check_greeting_true(self):
"""测试 greeting 检测为 true"""
QuickRuleService._cache['greeting'] = ['你好', '您好']
assert QuickRuleService.check_greeting('你好啊') is True
assert QuickRuleService.check_greeting('您好请问') is True
def test_check_greeting_false(self):
"""测试 greeting 检测为 false"""
QuickRuleService._cache['greeting'] = ['你好', '您好']
assert QuickRuleService.check_greeting('打印机坏了') is False
def test_check_greeting_empty_text(self):
"""测试空文本"""
assert QuickRuleService.check_greeting('') is False
assert QuickRuleService.check_greeting(None) is False
def test_check_routing_keyword(self):
"""测试路由关键词检测"""
QuickRuleService._cache['routing_prefilter'] = {
'打印机': 'IT服务',
'报销': '财务',
}
assert QuickRuleService.check_routing_keyword('打印机连不上') is True
assert QuickRuleService.check_routing_keyword('电脑坏了') is False
assert QuickRuleService.check_routing_keyword('报销咨询') is True
def test_get_routing_target_found(self):
"""测试获取存在的路由目标"""
QuickRuleService._cache['routing_target'] = {
'行政': {'service_name': '行政窗口', 'url': 'https://example.com'},
}
target = QuickRuleService.get_routing_target('行政')
assert target is not None
assert target['service_name'] == '行政窗口'
def test_get_routing_target_not_found(self):
"""测试获取不存在的路由目标"""
QuickRuleService._cache['routing_target'] = {
'行政': {'service_name': '行政窗口', 'url': 'https://example.com'},
}
target = QuickRuleService.get_routing_target('不存在的')
assert target is None
def test_get_categories(self):
"""测试获取所有业务分类"""
QuickRuleService._cache['routing_target'] = {
'行政': {},
'财务': {},
'人力资源': {},
}
categories = QuickRuleService.get_categories()
assert set(categories) == {'行政', '财务', '人力资源'}
@pytest.mark.asyncio
async def test_refresh_reloads_cache(self):
"""测试 refresh 重新加载缓存"""
mock_db = AsyncMock()
mock_rules = [
MagicMock(spec=QuickRule, rule_type='greeting', keyword='刷新测试', category=None, extra_data=None, is_active=True),
]
mock_result = MagicMock()
mock_result.scalars.return_value.all.return_value = mock_rules
mock_db.execute.return_value = mock_result
await QuickRuleService.refresh(mock_db)
keywords = QuickRuleService.get_greeting_keywords()
assert '刷新测试' in keywords
class TestQuickRuleDataStructures:
"""QuickRule 数据结构测试"""
def test_quick_rule_model_attributes(self):
"""测试 QuickRule 模型属性"""
rule = QuickRule(
rule_type='greeting',
keyword='你好',
priority=10,
is_active=True,
)
assert rule.rule_type == 'greeting'
assert rule.keyword == '你好'
assert rule.priority == 10
assert rule.is_active is True
def test_to_dict_conversion(self):
"""测试 to_dict 转换"""
from datetime import datetime
rule = QuickRule(
id=1,
rule_type='greeting',
keyword='你好',
priority=10,
is_active=True,
)
rule.created_at = datetime.now()
d = rule.to_dict()
assert d['id'] == 1
assert d['rule_type'] == 'greeting'
assert d['keyword'] == '你好'
assert d['priority'] == 10
assert d['is_active'] is True
assert 'created_at' in d