Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
对话链路: 4 缝测试 (Tauri→Kernel / Kernel→LLM / LLM→UI / 流式生命周期) Hands链路: 3 缝测试 (工具路由 / 执行回调 / 通用工具) 记忆链路: 3 缝测试 (FTS5存储 / 模式检索 / 去重) 冒烟测试: 3 Rust + 8 TypeScript 全量 PASS - Kernel::boot_with_driver() 测试辅助方法 - 全量 cargo test 0 回归 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
94 lines
3.6 KiB
Rust
94 lines
3.6 KiB
Rust
//! Hands smoke test — full lifecycle: trigger tool_call → hand execute → result
|
|
//!
|
|
//! Uses MockLlmDriver with stream chunks to simulate a real tool call flow.
|
|
|
|
use std::sync::Arc;
|
|
use zclaw_kernel::{Kernel, KernelConfig};
|
|
use zclaw_runtime::stream::StreamChunk;
|
|
use zclaw_runtime::test_util::MockLlmDriver;
|
|
use zclaw_runtime::{LoopEvent, LlmDriver};
|
|
use zclaw_types::AgentConfig;
|
|
|
|
#[tokio::test]
|
|
async fn smoke_hands_full_lifecycle() {
|
|
// Simulate: LLM calls hand_quiz → quiz hand executes → LLM summarizes
|
|
let mock = MockLlmDriver::new()
|
|
.with_stream_chunks(vec![
|
|
StreamChunk::TextDelta { delta: "正在生成测验...".to_string() },
|
|
StreamChunk::ToolUseStart {
|
|
id: "call_1".to_string(),
|
|
name: "hand_quiz".to_string(),
|
|
},
|
|
StreamChunk::ToolUseEnd {
|
|
id: "call_1".to_string(),
|
|
input: serde_json::json!({ "topic": "历史", "count": 2 }),
|
|
},
|
|
StreamChunk::Complete {
|
|
input_tokens: 15,
|
|
output_tokens: 10,
|
|
stop_reason: "tool_use".to_string(),
|
|
},
|
|
])
|
|
// After hand_quiz returns, LLM generates final response
|
|
.with_stream_chunks(vec![
|
|
StreamChunk::TextDelta { delta: "测验已生成!".to_string() },
|
|
StreamChunk::Complete {
|
|
input_tokens: 20,
|
|
output_tokens: 5,
|
|
stop_reason: "end_turn".to_string(),
|
|
},
|
|
]);
|
|
|
|
let config = KernelConfig::default();
|
|
let kernel = Kernel::boot_with_driver(config, Arc::new(mock) as Arc<dyn LlmDriver>)
|
|
.await
|
|
.expect("kernel boot");
|
|
|
|
let agent = AgentConfig::new("smoke-agent");
|
|
let id = agent.id;
|
|
kernel.spawn_agent(agent).await.expect("spawn agent");
|
|
|
|
let mut rx = kernel
|
|
.send_message_stream(&id, "生成一个历史测验".to_string())
|
|
.await
|
|
.expect("stream");
|
|
|
|
let mut saw_tool_start = false;
|
|
let mut saw_tool_end = false;
|
|
let mut saw_delta_before_tool = false;
|
|
let mut saw_delta_after_tool = false;
|
|
let mut phase = "before_tool";
|
|
let mut got_complete = false;
|
|
|
|
while let Some(event) = rx.recv().await {
|
|
match event {
|
|
LoopEvent::Delta(_) if phase == "before_tool" => saw_delta_before_tool = true,
|
|
LoopEvent::Delta(_) if phase == "after_tool" => saw_delta_after_tool = true,
|
|
LoopEvent::ToolStart { name, .. } => {
|
|
assert_eq!(name, "hand_quiz", "should be hand_quiz");
|
|
saw_tool_start = true;
|
|
}
|
|
LoopEvent::ToolEnd { name, output } => {
|
|
assert!(name.starts_with("hand_"), "should be hand tool");
|
|
assert!(output.is_object() || output.is_string(), "hand should produce output");
|
|
saw_tool_end = true;
|
|
phase = "after_tool";
|
|
}
|
|
LoopEvent::Complete(result) => {
|
|
assert!(result.output_tokens > 0, "should have output tokens");
|
|
assert!(result.iterations >= 2, "should take at least 2 iterations");
|
|
got_complete = true;
|
|
break;
|
|
}
|
|
LoopEvent::Error(msg) => panic!("unexpected error: {}", msg),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
assert!(saw_delta_before_tool, "should see delta before tool execution");
|
|
assert!(saw_tool_start, "should see hand_quiz ToolStart");
|
|
assert!(saw_tool_end, "should see hand_quiz ToolEnd");
|
|
assert!(saw_delta_after_tool, "should see delta after tool execution");
|
|
assert!(got_complete, "should receive complete event");
|
|
}
|