fix(mcp): 修复前端空输入重置为不超时,增加超时路径单元测试

P3: 清空输入框时回退到默认值 300 而非 0;新增两个 tokio 测试覆盖有超时和无超时(error pass-through)路径
This commit is contained in:
oudecheng 2026-08-11 23:07:00 +08:00
parent 3e97ed903c
commit 77a0eac2c8
2 changed files with 49 additions and 1 deletions

View File

@ -281,4 +281,49 @@ mod tests {
// Empty stays empty // Empty stays empty
assert_eq!(sanitize_tool_name(""), ""); assert_eq!(sanitize_tool_name(""), "");
} }
#[tokio::test]
async fn test_execute_returns_error_when_server_not_connected() {
// McpClientManager with no connected servers — call should fail immediately
// with "not connected" error, not hang or timeout.
let manager = Arc::new(McpClientManager::new());
let schema: serde_json::Map<String, serde_json::Value> =
serde_json::json!({"type": "object"})
.as_object()
.unwrap()
.clone();
let tool_info = Tool::new("echo", "Echo tool", schema);
let wrapper = McpToolWrapper::new(manager, "ghost".to_string(), tool_info, 300);
let result = wrapper.execute(serde_json::json!({})).await;
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(
msg.contains("not connected"),
"expected 'not connected' error, got: {msg}"
);
}
#[tokio::test]
async fn test_timeout_zero_does_not_wrap() {
// With timeout_secs = 0, the wrapper should pass through directly
// without tokio::time::timeout. The call still fails because no server
// is connected, but the error path is the non-timeout branch.
let manager = Arc::new(McpClientManager::new());
let schema: serde_json::Map<String, serde_json::Value> =
serde_json::json!({"type": "object"})
.as_object()
.unwrap()
.clone();
let tool_info = Tool::new("echo", "Echo tool", schema);
let wrapper = McpToolWrapper::new(manager, "ghost".to_string(), tool_info, 0);
let result = wrapper.execute(serde_json::json!({})).await;
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(
msg.contains("not connected"),
"expected 'not connected' error, got: {msg}"
);
}
} }

View File

@ -63,7 +63,10 @@ export function McpTab({ config, update, setToast }: Props) {
<input <input
type="number" type="number"
value={config.mcp_tool_timeout_secs ?? 300} value={config.mcp_tool_timeout_secs ?? 300}
onChange={(e) => update('mcp_tool_timeout_secs', Number(e.target.value))} onChange={(e) => {
const v = e.target.value;
update('mcp_tool_timeout_secs', v === '' ? 300 : Number(v));
}}
className={inputCls} className={inputCls}
min={0} min={0}
step={30} step={30}