docs: Translate ja -> en

This commit is contained in:
2026-01-16 16:58:03 +09:00
parent 6d87da90d1
commit 6c43ac9969
26 changed files with 760 additions and 759 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
//! Anthropic フィクスチャベースの統合テスト
//! Anthropic fixture-based integration tests
mod common;
+1 -1
View File
@@ -1,4 +1,4 @@
//! Gemini フィクスチャベースの統合テスト
//! Gemini fixture-based integration tests
mod common;
+1 -1
View File
@@ -1,4 +1,4 @@
//! Ollama フィクスチャベースの統合テスト
//! Ollama fixture-based integration tests
mod common;
+1 -1
View File
@@ -1,4 +1,4 @@
//! OpenAI フィクスチャベースの統合テスト
//! OpenAI fixture-based integration tests
mod common;
+21 -21
View File
@@ -1,6 +1,6 @@
//! 並列ツール実行のテスト
//! Parallel tool execution tests
//!
//! Workerが複数のツールを並列に実行することを確認する。
//! Verify that Worker executes multiple tools in parallel.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -22,7 +22,7 @@ use common::MockLlmClient;
// Parallel Execution Test Tools
// =============================================================================
/// 一定時間待機してから応答するツール
/// Tool that waits for a specified time before responding
#[derive(Clone)]
struct SlowTool {
name: String,
@@ -43,7 +43,7 @@ impl SlowTool {
self.call_count.load(Ordering::SeqCst)
}
/// ToolDefinition を作成
/// Create ToolDefinition
fn definition(&self) -> ToolDefinition {
let tool = self.clone();
Arc::new(move || {
@@ -71,13 +71,13 @@ impl Tool for SlowTool {
// Tests
// =============================================================================
/// 複数のツールが並列に実行されることを確認
/// Verify that multiple tools are executed in parallel
///
/// 各ツールが100msかかる場合、逐次実行なら300ms以上かかるが、
/// 並列実行なら100ms程度で完了するはず。
/// If each tool takes 100ms, sequential execution would take 300ms+,
/// but parallel execution should complete in about 100ms.
#[tokio::test]
async fn test_parallel_tool_execution() {
// 3つのツール呼び出しを含むイベントシーケンス
// Event sequence containing 3 tool calls
let events = vec![
Event::tool_use_start(0, "call_1", "slow_tool_1"),
Event::tool_input_delta(0, r#"{}"#),
@@ -96,7 +96,7 @@ async fn test_parallel_tool_execution() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// 各ツールは100ms待機
// Each tool waits 100ms
let tool1 = SlowTool::new("slow_tool_1", 100);
let tool2 = SlowTool::new("slow_tool_2", 100);
let tool3 = SlowTool::new("slow_tool_3", 100);
@@ -113,13 +113,13 @@ async fn test_parallel_tool_execution() {
let _result = worker.run("Run all tools").await;
let elapsed = start.elapsed();
// 全ツールが呼び出されたことを確認
// Verify all tools were called
assert_eq!(tool1_clone.call_count(), 1, "Tool 1 should be called once");
assert_eq!(tool2_clone.call_count(), 1, "Tool 2 should be called once");
assert_eq!(tool3_clone.call_count(), 1, "Tool 3 should be called once");
// 並列実行なら200ms以下で完了するはず(逐次なら300ms以上)
// マージン込みで250msをしきい値とする
// Parallel execution should complete in under 200ms (sequential would be 300ms+)
// Using 250ms as threshold with margin
assert!(
elapsed < Duration::from_millis(250),
"Parallel execution should complete in ~100ms, but took {:?}",
@@ -129,7 +129,7 @@ async fn test_parallel_tool_execution() {
println!("Parallel execution completed in {:?}", elapsed);
}
/// Hook: pre_tool_call でスキップされたツールは実行されないことを確認
/// Hook: pre_tool_call - verify that skipped tools are not executed
#[tokio::test]
async fn test_before_tool_call_skip() {
let events = vec![
@@ -156,7 +156,7 @@ async fn test_before_tool_call_skip() {
worker.register_tool(allowed_tool.definition()).unwrap();
worker.register_tool(blocked_tool.definition()).unwrap();
// "blocked_tool" をスキップするHook
// Hook to skip "blocked_tool"
struct BlockingHook;
#[async_trait]
@@ -174,7 +174,7 @@ async fn test_before_tool_call_skip() {
let _result = worker.run("Test hook").await;
// allowed_tool は呼び出されるが、blocked_tool は呼び出されない
// allowed_tool is called, but blocked_tool is not
assert_eq!(
allowed_clone.call_count(),
1,
@@ -187,12 +187,12 @@ async fn test_before_tool_call_skip() {
);
}
/// Hook: post_tool_call で結果が改変されることを確認
/// Hook: post_tool_call - verify that results can be modified
#[tokio::test]
async fn test_post_tool_call_modification() {
// 複数リクエストに対応するレスポンスを準備
// Prepare responses for multiple requests
let client = MockLlmClient::with_responses(vec![
// 1回目のリクエスト: ツール呼び出し
// First request: tool call
vec![
Event::tool_use_start(0, "call_1", "test_tool"),
Event::tool_input_delta(0, r#"{}"#),
@@ -201,7 +201,7 @@ async fn test_post_tool_call_modification() {
status: ResponseStatus::Completed,
}),
],
// 2回目のリクエスト: ツール結果を受けてテキストレスポンス
// Second request: text response after receiving tool result
vec![
Event::text_block_start(0),
Event::text_delta(0, "Done!"),
@@ -235,7 +235,7 @@ async fn test_post_tool_call_modification() {
worker.register_tool(simple_tool_definition()).unwrap();
// 結果を改変するHook
// Hook to modify results
struct ModifyingHook {
modified_content: Arc<std::sync::Mutex<Option<String>>>,
}
@@ -261,7 +261,7 @@ async fn test_post_tool_call_modification() {
assert!(result.is_ok(), "Worker should complete: {:?}", result);
// Hookが呼ばれて内容が改変されたことを確認
// Verify hook was called and content was modified
let content = modified_content.lock().unwrap().clone();
assert!(content.is_some(), "Hook should have been called");
assert!(
+26 -26
View File
@@ -1,6 +1,6 @@
//! WorkerSubscriberのテスト
//! WorkerSubscriber tests
//!
//! WorkerSubscriberを使ってイベントを購読するテスト
//! Tests for subscribing to events using WorkerSubscriber
mod common;
@@ -18,9 +18,9 @@ use llm_worker::timeline::{TextBlockEvent, ToolUseBlockEvent};
// Test Subscriber
// =============================================================================
/// テスト用のシンプルなSubscriber実装
/// Simple Subscriber implementation for testing
struct TestSubscriber {
// 記録用のバッファ
// Recording buffers
text_deltas: Arc<Mutex<Vec<String>>>,
text_completes: Arc<Mutex<Vec<String>>>,
tool_call_completes: Arc<Mutex<Vec<ToolCall>>>,
@@ -60,7 +60,7 @@ impl WorkerSubscriber for TestSubscriber {
}
fn on_tool_use_block(&mut self, _scope: &mut (), _event: &ToolUseBlockEvent) {
// 必要に応じて処理
// Process as needed
}
fn on_tool_call_complete(&mut self, call: &ToolCall) {
@@ -76,7 +76,7 @@ impl WorkerSubscriber for TestSubscriber {
}
fn on_error(&mut self, _event: &ErrorEvent) {
// 必要に応じて処理
// Process as needed
}
fn on_turn_start(&mut self, turn: usize) {
@@ -92,10 +92,10 @@ impl WorkerSubscriber for TestSubscriber {
// Tests
// =============================================================================
/// WorkerSubscriberがテキストブロックイベントを正しく受け取ることを確認
/// Verify that WorkerSubscriber correctly receives text block events
#[tokio::test]
async fn test_subscriber_text_block_events() {
// テキストレスポンスを含むイベントシーケンス
// Event sequence containing text response
let events = vec![
Event::text_block_start(0),
Event::text_delta(0, "Hello, "),
@@ -109,33 +109,33 @@ async fn test_subscriber_text_block_events() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// Subscriberを登録
// Register Subscriber
let subscriber = TestSubscriber::new();
let text_deltas = subscriber.text_deltas.clone();
let text_completes = subscriber.text_completes.clone();
worker.subscribe(subscriber);
// 実行
// Execute
let result = worker.run("Greet me").await;
assert!(result.is_ok(), "Worker should complete: {:?}", result);
// デルタが収集されていることを確認
// Verify deltas were collected
let deltas = text_deltas.lock().unwrap();
assert_eq!(deltas.len(), 2);
assert_eq!(deltas[0], "Hello, ");
assert_eq!(deltas[1], "World!");
// 完了テキストが収集されていることを確認
// Verify complete text was collected
let completes = text_completes.lock().unwrap();
assert_eq!(completes.len(), 1);
assert_eq!(completes[0], "Hello, World!");
}
/// WorkerSubscriberがツール呼び出し完了イベントを正しく受け取ることを確認
/// Verify that WorkerSubscriber correctly receives tool call complete events
#[tokio::test]
async fn test_subscriber_tool_call_complete() {
// ツール呼び出しを含むイベントシーケンス
// Event sequence containing tool call
let events = vec![
Event::tool_use_start(0, "call_123", "get_weather"),
Event::tool_input_delta(0, r#"{"city":"#),
@@ -149,15 +149,15 @@ async fn test_subscriber_tool_call_complete() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// Subscriberを登録
// Register Subscriber
let subscriber = TestSubscriber::new();
let tool_call_completes = subscriber.tool_call_completes.clone();
worker.subscribe(subscriber);
// 実行
// Execute
let _ = worker.run("Weather please").await;
// ツール呼び出し完了が収集されていることを確認
// Verify tool call complete was collected
let completes = tool_call_completes.lock().unwrap();
assert_eq!(completes.len(), 1);
assert_eq!(completes[0].name, "get_weather");
@@ -165,7 +165,7 @@ async fn test_subscriber_tool_call_complete() {
assert_eq!(completes[0].input["city"], "Tokyo");
}
/// WorkerSubscriberがターンイベントを正しく受け取ることを確認
/// Verify that WorkerSubscriber correctly receives turn events
#[tokio::test]
async fn test_subscriber_turn_events() {
let events = vec![
@@ -180,29 +180,29 @@ async fn test_subscriber_turn_events() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// Subscriberを登録
// Register Subscriber
let subscriber = TestSubscriber::new();
let turn_starts = subscriber.turn_starts.clone();
let turn_ends = subscriber.turn_ends.clone();
worker.subscribe(subscriber);
// 実行
// Execute
let result = worker.run("Do something").await;
assert!(result.is_ok());
// ターンイベントが収集されていることを確認
// Verify turn events were collected
let starts = turn_starts.lock().unwrap();
let ends = turn_ends.lock().unwrap();
assert_eq!(starts.len(), 1);
assert_eq!(starts[0], 0); // 最初のターン
assert_eq!(starts[0], 0); // First turn
assert_eq!(ends.len(), 1);
assert_eq!(ends[0], 0);
}
/// WorkerSubscriberがUsageイベントを正しく受け取ることを確認
/// Verify that WorkerSubscriber correctly receives Usage events
#[tokio::test]
async fn test_subscriber_usage_events() {
let events = vec![
@@ -218,15 +218,15 @@ async fn test_subscriber_usage_events() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// Subscriberを登録
// Register Subscriber
let subscriber = TestSubscriber::new();
let usage_events = subscriber.usage_events.clone();
worker.subscribe(subscriber);
// 実行
// Execute
let _ = worker.run("Hello").await;
// Usageイベントが収集されていることを確認
// Verify Usage events were collected
let usages = usage_events.lock().unwrap();
assert_eq!(usages.len(), 1);
assert_eq!(usages[0].input_tokens, Some(100));
+20 -20
View File
@@ -1,11 +1,11 @@
//! ツールマクロのテスト
//! Tool macro tests
//!
//! `#[tool_registry]` `#[tool]` マクロの動作を確認する。
//! Verify the behavior of `#[tool_registry]` and `#[tool]` macros.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
// マクロ展開に必要なインポート
// Imports needed for macro expansion
use schemars;
use serde;
@@ -15,7 +15,7 @@ use llm_worker_macros::tool_registry;
// Test: Basic Tool Generation
// =============================================================================
/// シンプルなコンテキスト構造体
/// Simple context struct
#[derive(Clone)]
struct SimpleContext {
prefix: String,
@@ -23,21 +23,21 @@ struct SimpleContext {
#[tool_registry]
impl SimpleContext {
/// メッセージに挨拶を追加する
/// Add greeting to message
///
/// 指定されたメッセージにプレフィックスを付けて返します。
/// Returns the message with a prefix added.
#[tool]
async fn greet(&self, message: String) -> String {
format!("{}: {}", self.prefix, message)
}
/// 二つの数を足す
/// Add two numbers
#[tool]
async fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
/// 引数なしのツール
/// Tool with no arguments
#[tool]
async fn get_prefix(&self) -> String {
self.prefix.clone()
@@ -50,16 +50,16 @@ async fn test_basic_tool_generation() {
prefix: "Hello".to_string(),
};
// ファクトリメソッドでToolDefinitionを取得
// Get ToolDefinition from factory method
let greet_definition = ctx.greet_definition();
// ファクトリを呼び出してMetaとToolを取得
// Call factory to get Meta and Tool
let (meta, tool) = greet_definition();
// メタ情報の確認
// Verify meta information
assert_eq!(meta.name, "greet");
assert!(
meta.description.contains("メッセージに挨拶を追加する"),
meta.description.contains("Add greeting to message"),
"Description should contain doc comment: {}",
meta.description
);
@@ -73,7 +73,7 @@ async fn test_basic_tool_generation() {
serde_json::to_string_pretty(&meta.input_schema).unwrap()
);
// 実行テスト
// Execution test
let result = tool.execute(r#"{"message": "World"}"#).await;
assert!(result.is_ok(), "Should execute successfully");
let output = result.unwrap();
@@ -107,7 +107,7 @@ async fn test_no_arguments() {
assert_eq!(meta.name, "get_prefix");
// 空のJSONオブジェクトで呼び出し
// Call with empty JSON object
let result = tool.execute(r#"{}"#).await;
assert!(result.is_ok());
let output = result.unwrap();
@@ -126,7 +126,7 @@ async fn test_invalid_arguments() {
let (_, tool) = ctx.greet_definition()();
// 不正なJSON
// Invalid JSON
let result = tool.execute(r#"{"wrong_field": "value"}"#).await;
assert!(result.is_err(), "Should fail with invalid arguments");
}
@@ -149,7 +149,7 @@ impl std::fmt::Display for MyError {
#[tool_registry]
impl FallibleContext {
/// 与えられた値を検証する
/// Validate the given value
#[tool]
async fn validate(&self, value: i32) -> Result<String, MyError> {
if value > 0 {
@@ -198,7 +198,7 @@ struct SyncContext {
#[tool_registry]
impl SyncContext {
/// カウンターをインクリメントして返す (非async)
/// Increment counter and return (non-async)
#[tool]
fn increment(&self) -> usize {
self.counter.fetch_add(1, Ordering::SeqCst) + 1
@@ -213,7 +213,7 @@ async fn test_sync_method() {
let (_, tool) = ctx.increment_definition()();
// 3回実行
// Execute 3 times
let result1 = tool.execute(r#"{}"#).await;
let result2 = tool.execute(r#"{}"#).await;
let result3 = tool.execute(r#"{}"#).await;
@@ -222,7 +222,7 @@ async fn test_sync_method() {
assert!(result2.is_ok());
assert!(result3.is_ok());
// カウンターは3になっているはず
// Counter should be 3
assert_eq!(ctx.counter.load(Ordering::SeqCst), 3);
}
@@ -236,7 +236,7 @@ async fn test_tool_meta_immutability() {
prefix: "Test".to_string(),
};
// 2回取得しても同じメタ情報が得られることを確認
// Verify same meta info is returned on multiple calls
let (meta1, _) = ctx.greet_definition()();
let (meta2, _) = ctx.greet_definition()();
+8 -8
View File
@@ -3,16 +3,16 @@ use llm_worker::{Worker, WorkerError};
#[test]
fn test_openai_top_k_warning() {
// ダミーキーでクライアント作成(validate_configは通信しないため安全)
// Create client with dummy key (validate_config doesn't make network calls, so safe)
let client = OpenAIClient::new("dummy-key", "gpt-4o");
// top_kを設定したWorkerを作成
let worker = Worker::new(client).top_k(50); // OpenAIはtop_k非対応
// Create Worker with top_k set (OpenAI doesn't support top_k)
let worker = Worker::new(client).top_k(50);
// validate()を実行
// Run validate()
let result = worker.validate();
// エラーが返り、ConfigWarningsが含まれていることを確認
// Verify error is returned and ConfigWarnings is included
match result {
Err(WorkerError::ConfigWarnings(warnings)) => {
assert_eq!(warnings.len(), 1);
@@ -28,12 +28,12 @@ fn test_openai_top_k_warning() {
fn test_openai_valid_config() {
let client = OpenAIClient::new("dummy-key", "gpt-4o");
// validな設定(temperatureのみ)
// Valid configuration (temperature only)
let worker = Worker::new(client).temperature(0.7);
// validate()を実行
// Run validate()
let result = worker.validate();
// 成功を確認
// Verify success
assert!(result.is_ok());
}
+36 -36
View File
@@ -1,7 +1,7 @@
//! Workerフィクスチャベースの統合テスト
//! Worker fixture-based integration tests
//!
//! 記録されたAPIレスポンスを使ってWorkerの動作をテストする。
//! APIキー不要でローカルで実行可能。
//! Tests Worker behavior using recorded API responses.
//! Can run locally without API keys.
mod common;
@@ -14,12 +14,12 @@ use common::MockLlmClient;
use llm_worker::Worker;
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta};
/// フィクスチャディレクトリのパス
/// Fixture directory path
fn fixtures_dir() -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/anthropic")
}
/// シンプルなテスト用ツール
/// Simple test tool
#[derive(Clone)]
struct MockWeatherTool {
call_count: Arc<AtomicUsize>,
@@ -61,13 +61,13 @@ impl Tool for MockWeatherTool {
async fn execute(&self, input_json: &str) -> Result<String, ToolError> {
self.call_count.fetch_add(1, Ordering::SeqCst);
// 入力をパース
// Parse input
let input: serde_json::Value = serde_json::from_str(input_json)
.map_err(|e| ToolError::InvalidArgument(e.to_string()))?;
let city = input["city"].as_str().unwrap_or("Unknown");
// モックのレスポンスを返す
// Return mock response
Ok(format!("Weather in {}: Sunny, 22°C", city))
}
}
@@ -76,12 +76,12 @@ impl Tool for MockWeatherTool {
// Basic Fixture Tests
// =============================================================================
/// MockLlmClientがJSONLフィクスチャファイルから正しくイベントをロードできることを確認
/// Verify that MockLlmClient can correctly load events from JSONL fixture files
///
/// 既存のanthropic_*.jsonlファイルを使用し、イベントがパース・ロードされることを検証する。
/// Uses existing anthropic_*.jsonl files to verify events are parsed and loaded.
#[test]
fn test_mock_client_from_fixture() {
// 既存のフィクスチャをロード
// Load existing fixture
let fixture_path = fixtures_dir().join("anthropic_1767624445.jsonl");
if !fixture_path.exists() {
println!("Fixture not found, skipping test");
@@ -93,14 +93,14 @@ fn test_mock_client_from_fixture() {
println!("Loaded {} events from fixture", client.event_count());
}
/// MockLlmClientが直接指定されたイベントリストで正しく動作することを確認
/// Verify that MockLlmClient works correctly with directly specified event lists
///
/// fixtureファイルを使わず、プログラムでイベントを構築してクライアントを作成する。
/// Creates a client with programmatically constructed events instead of using fixture files.
#[test]
fn test_mock_client_from_events() {
use llm_worker::llm_client::event::Event;
// 直接イベントを指定
// Specify events directly
let events = vec![
Event::text_block_start(0),
Event::text_delta(0, "Hello!"),
@@ -115,10 +115,10 @@ fn test_mock_client_from_events() {
// Worker Tests with Fixtures
// =============================================================================
/// Workerがシンプルなテキストレスポンスを正しく処理できることを確認
/// Verify that Worker can correctly process simple text responses
///
/// simple_text.jsonlフィクスチャを使用し、ツール呼び出しなしのシナリオをテストする。
/// フィクスチャがない場合はスキップされる。
/// Uses simple_text.jsonl fixture to test scenarios without tool calls.
/// Skipped if fixture is not present.
#[tokio::test]
async fn test_worker_simple_text_response() {
let fixture_path = fixtures_dir().join("simple_text.jsonl");
@@ -131,16 +131,16 @@ async fn test_worker_simple_text_response() {
let client = MockLlmClient::from_fixture(&fixture_path).unwrap();
let mut worker = Worker::new(client);
// シンプルなメッセージを送信
// Send a simple message
let result = worker.run("Hello").await;
assert!(result.is_ok(), "Worker should complete successfully");
}
/// Workerがツール呼び出しを含むレスポンスを正しく処理できることを確認
/// Verify that Worker can correctly process responses containing tool calls
///
/// tool_call.jsonlフィクスチャを使用し、MockWeatherToolが呼び出されることをテストする。
/// max_turns=1に設定し、ツール実行後のループを防止。
/// Uses tool_call.jsonl fixture to test that MockWeatherTool is called.
/// Sets max_turns=1 to prevent loop after tool execution.
#[tokio::test]
async fn test_worker_tool_call() {
let fixture_path = fixtures_dir().join("tool_call.jsonl");
@@ -153,32 +153,32 @@ async fn test_worker_tool_call() {
let client = MockLlmClient::from_fixture(&fixture_path).unwrap();
let mut worker = Worker::new(client);
// ツールを登録
// Register tool
let weather_tool = MockWeatherTool::new();
let tool_for_check = weather_tool.clone();
worker.register_tool(weather_tool.definition()).unwrap();
// メッセージを送信
// Send message
let _result = worker.run("What's the weather in Tokyo?").await;
// ツールが呼び出されたことを確認
// Note: max_turns=1なのでツール結果後のリクエストは送信されない
// Verify tool was called
// Note: max_turns=1 so no request is sent after tool result
let call_count = tool_for_check.get_call_count();
println!("Tool was called {} times", call_count);
// フィクスチャにToolUseが含まれていればツールが呼び出されるはず
// ただしmax_turns=1なので1回で終了
// Tool should be called if fixture contains ToolUse
// But ends after 1 turn due to max_turns=1
}
/// fixtureファイルなしでWorkerが動作することを確認
/// Verify that Worker works without fixture files
///
/// プログラムでイベントシーケンスを構築し、MockLlmClientに渡してテストする。
/// テストの独立性を高め、外部ファイルへの依存を排除したい場合に有用。
/// Constructs event sequence programmatically and passes to MockLlmClient.
/// Useful when test independence is needed and external file dependency should be eliminated.
#[tokio::test]
async fn test_worker_with_programmatic_events() {
use llm_worker::llm_client::event::{Event, ResponseStatus, StatusEvent};
// プログラムでイベントシーケンスを構築
// Construct event sequence programmatically
let events = vec![
Event::text_block_start(0),
Event::text_delta(0, "Hello, "),
@@ -197,16 +197,16 @@ async fn test_worker_with_programmatic_events() {
assert!(result.is_ok(), "Worker should complete successfully");
}
/// ToolCallCollectorがToolUseブロックイベントから正しくToolCallを収集することを確認
/// Verify that ToolCallCollector correctly collects ToolCall from ToolUse block events
///
/// Timelineにイベントをディスパッチし、ToolCallCollector
/// id, name, inputJSON)を正しく抽出できることを検証する。
/// Dispatches events to Timeline and verifies ToolCallCollector
/// correctly extracts id, name, and input (JSON).
#[tokio::test]
async fn test_tool_call_collector_integration() {
use llm_worker::llm_client::event::Event;
use llm_worker::timeline::{Timeline, ToolCallCollector};
// ToolUseブロックを含むイベントシーケンス
// Event sequence containing ToolUse block
let events = vec![
Event::tool_use_start(0, "call_123", "get_weather"),
Event::tool_input_delta(0, r#"{"city":"#),
@@ -218,13 +218,13 @@ async fn test_tool_call_collector_integration() {
let mut timeline = Timeline::new();
timeline.on_tool_use_block(collector.clone());
// イベントをディスパッチ
// Dispatch events
for event in &events {
let timeline_event: llm_worker::timeline::event::Event = event.clone().into();
timeline.dispatch(&timeline_event);
}
// 収集されたToolCallを確認
// Verify collected ToolCall
let calls = collector.take_collected();
assert_eq!(calls.len(), 1, "Should collect one tool call");
assert_eq!(calls[0].name, "get_weather");
+54 -54
View File
@@ -1,7 +1,7 @@
//! Worker状態管理のテスト
//! Worker state management tests
//!
//! Type-stateパターン(Mutable/CacheLocked)による状態遷移と
//! ターン間の状態保持をテストする。
//! Tests for state transitions using the Type-state pattern (Mutable/CacheLocked)
//! and state preservation between turns.
mod common;
@@ -11,10 +11,10 @@ use llm_worker::llm_client::event::{Event, ResponseStatus, StatusEvent};
use llm_worker::{Message, MessageContent};
// =============================================================================
// Mutable状態のテスト
// Mutable State Tests
// =============================================================================
/// Mutable状態でシステムプロンプトを設定できることを確認
/// Verify that system prompt can be set in Mutable state
#[test]
fn test_mutable_set_system_prompt() {
let client = MockLlmClient::new(vec![]);
@@ -29,35 +29,35 @@ fn test_mutable_set_system_prompt() {
);
}
/// Mutable状態で履歴を自由に編集できることを確認
/// Verify that history can be freely edited in Mutable state
#[test]
fn test_mutable_history_manipulation() {
let client = MockLlmClient::new(vec![]);
let mut worker = Worker::new(client);
// 初期状態は空
// Initial state is empty
assert!(worker.history().is_empty());
// 履歴を追加
// Add to history
worker.push_message(Message::user("Hello"));
worker.push_message(Message::assistant("Hi there!"));
assert_eq!(worker.history().len(), 2);
// 履歴への可変アクセス
// Mutable access to history
worker.history_mut().push(Message::user("How are you?"));
assert_eq!(worker.history().len(), 3);
// 履歴をクリア
// Clear history
worker.clear_history();
assert!(worker.history().is_empty());
// 履歴を設定
// Set history
let messages = vec![Message::user("Test"), Message::assistant("Response")];
worker.set_history(messages);
assert_eq!(worker.history().len(), 2);
}
/// ビルダーパターンでWorkerを構築できることを確認
/// Verify that Worker can be constructed using builder pattern
#[test]
fn test_mutable_builder_pattern() {
let client = MockLlmClient::new(vec![]);
@@ -74,7 +74,7 @@ fn test_mutable_builder_pattern() {
assert_eq!(worker.history().len(), 4);
}
/// extend_historyで複数メッセージを追加できることを確認
/// Verify that multiple messages can be added with extend_history
#[test]
fn test_mutable_extend_history() {
let client = MockLlmClient::new(vec![]);
@@ -92,10 +92,10 @@ fn test_mutable_extend_history() {
}
// =============================================================================
// 状態遷移テスト
// State Transition Tests
// =============================================================================
/// lock()でMutable -> CacheLocked状態に遷移することを確認
/// Verify that lock() transitions from Mutable -> CacheLocked state
#[test]
fn test_lock_transition() {
let client = MockLlmClient::new(vec![]);
@@ -105,16 +105,16 @@ fn test_lock_transition() {
worker.push_message(Message::user("Hello"));
worker.push_message(Message::assistant("Hi"));
// ロック
// Lock
let locked_worker = worker.lock();
// CacheLocked状態でも履歴とシステムプロンプトにアクセス可能
// History and system prompt are still accessible in CacheLocked state
assert_eq!(locked_worker.get_system_prompt(), Some("System"));
assert_eq!(locked_worker.history().len(), 2);
assert_eq!(locked_worker.locked_prefix_len(), 2);
}
/// unlock()でCacheLocked -> Mutable状態に遷移することを確認
/// Verify that unlock() transitions from CacheLocked -> Mutable state
#[test]
fn test_unlock_transition() {
let client = MockLlmClient::new(vec![]);
@@ -123,20 +123,20 @@ fn test_unlock_transition() {
worker.push_message(Message::user("Hello"));
let locked_worker = worker.lock();
// アンロック
// Unlock
let mut worker = locked_worker.unlock();
// Mutable状態に戻ったので履歴操作が可能
// History operations are available again in Mutable state
worker.push_message(Message::assistant("Hi"));
worker.clear_history();
assert!(worker.history().is_empty());
}
// =============================================================================
// ターン実行と状態保持のテスト
// Turn Execution and State Preservation Tests
// =============================================================================
/// Mutable状態でターンを実行し、履歴が正しく更新されることを確認
/// Verify that history is correctly updated after running a turn in Mutable state
#[tokio::test]
async fn test_mutable_run_updates_history() {
let events = vec![
@@ -151,33 +151,33 @@ async fn test_mutable_run_updates_history() {
let client = MockLlmClient::new(events);
let mut worker = Worker::new(client);
// 実行
// Execute
let result = worker.run("Hi there").await;
assert!(result.is_ok());
// 履歴が更新されている
// History is updated
let history = worker.history();
assert_eq!(history.len(), 2); // user + assistant
// ユーザーメッセージ
// User message
assert!(matches!(
&history[0].content,
MessageContent::Text(t) if t == "Hi there"
));
// アシスタントメッセージ
// Assistant message
assert!(matches!(
&history[1].content,
MessageContent::Text(t) if t == "Hello, I'm an assistant!"
));
}
/// CacheLocked状態で複数ターンを実行し、履歴が正しく累積することを確認
/// Verify that history accumulates correctly over multiple turns in CacheLocked state
#[tokio::test]
async fn test_locked_multi_turn_history_accumulation() {
// 2回のリクエストに対応するレスポンスを準備
// Prepare responses for 2 requests
let client = MockLlmClient::with_responses(vec![
// 1回目のレスポンス
// First response
vec![
Event::text_block_start(0),
Event::text_delta(0, "Nice to meet you!"),
@@ -186,7 +186,7 @@ async fn test_locked_multi_turn_history_accumulation() {
status: ResponseStatus::Completed,
}),
],
// 2回目のレスポンス
// Second response
vec![
Event::text_block_start(0),
Event::text_delta(0, "I can help with that."),
@@ -199,37 +199,37 @@ async fn test_locked_multi_turn_history_accumulation() {
let worker = Worker::new(client).system_prompt("You are helpful.");
// ロック(システムプロンプト設定後)
// Lock (after setting system prompt)
let mut locked_worker = worker.lock();
assert_eq!(locked_worker.locked_prefix_len(), 0); // メッセージはまだない
assert_eq!(locked_worker.locked_prefix_len(), 0); // No messages yet
// 1ターン目
// Turn 1
let result1 = locked_worker.run("Hello!").await;
assert!(result1.is_ok());
assert_eq!(locked_worker.history().len(), 2); // user + assistant
// 2ターン目
// Turn 2
let result2 = locked_worker.run("Can you help me?").await;
assert!(result2.is_ok());
assert_eq!(locked_worker.history().len(), 4); // 2 * (user + assistant)
// 履歴の内容を確認
// Verify history contents
let history = locked_worker.history();
// 1ターン目のユーザーメッセージ
// Turn 1 user message
assert!(matches!(&history[0].content, MessageContent::Text(t) if t == "Hello!"));
// 1ターン目のアシスタントメッセージ
// Turn 1 assistant message
assert!(matches!(&history[1].content, MessageContent::Text(t) if t == "Nice to meet you!"));
// 2ターン目のユーザーメッセージ
// Turn 2 user message
assert!(matches!(&history[2].content, MessageContent::Text(t) if t == "Can you help me?"));
// 2ターン目のアシスタントメッセージ
// Turn 2 assistant message
assert!(matches!(&history[3].content, MessageContent::Text(t) if t == "I can help with that."));
}
/// locked_prefix_lenがロック時点の履歴長を正しく記録することを確認
/// Verify that locked_prefix_len correctly records history length at lock time
#[tokio::test]
async fn test_locked_prefix_len_tracking() {
let client = MockLlmClient::with_responses(vec![
@@ -253,25 +253,25 @@ async fn test_locked_prefix_len_tracking() {
let mut worker = Worker::new(client);
// 事前にメッセージを追加
// Add messages beforehand
worker.push_message(Message::user("Pre-existing message 1"));
worker.push_message(Message::assistant("Pre-existing response 1"));
assert_eq!(worker.history().len(), 2);
// ロック
// Lock
let mut locked_worker = worker.lock();
assert_eq!(locked_worker.locked_prefix_len(), 2); // ロック時点で2メッセージ
assert_eq!(locked_worker.locked_prefix_len(), 2); // 2 messages at lock time
// ターン実行
// Execute turn
locked_worker.run("New message").await.unwrap();
// 履歴は増えるが、locked_prefix_lenは変わらない
// History grows but locked_prefix_len remains unchanged
assert_eq!(locked_worker.history().len(), 4); // 2 + 2
assert_eq!(locked_worker.locked_prefix_len(), 2); // 変わらない
assert_eq!(locked_worker.locked_prefix_len(), 2); // Unchanged
}
/// ターンカウントが正しくインクリメントされることを確認
/// Verify that turn count is correctly incremented
#[tokio::test]
async fn test_turn_count_increment() {
let client = MockLlmClient::with_responses(vec![
@@ -304,7 +304,7 @@ async fn test_turn_count_increment() {
assert_eq!(worker.turn_count(), 2);
}
/// unlock後に履歴を編集し、再度lockできることを確認
/// Verify that history can be edited after unlock and re-locked
#[tokio::test]
async fn test_unlock_edit_relock() {
let client = MockLlmClient::with_responses(vec![vec![
@@ -320,27 +320,27 @@ async fn test_unlock_edit_relock() {
.with_message(Message::user("Hello"))
.with_message(Message::assistant("Hi"));
// ロック -> アンロック
// Lock -> Unlock
let locked = worker.lock();
assert_eq!(locked.locked_prefix_len(), 2);
let mut unlocked = locked.unlock();
// 履歴を編集
// Edit history
unlocked.clear_history();
unlocked.push_message(Message::user("Fresh start"));
// 再ロック
// Re-lock
let relocked = unlocked.lock();
assert_eq!(relocked.history().len(), 1);
assert_eq!(relocked.locked_prefix_len(), 1);
}
// =============================================================================
// システムプロンプト保持のテスト
// System Prompt Preservation Tests
// =============================================================================
/// CacheLocked状態でもシステムプロンプトが保持されることを確認
/// Verify that system prompt is preserved in CacheLocked state
#[test]
fn test_system_prompt_preserved_in_locked_state() {
let client = MockLlmClient::new(vec![]);
@@ -356,7 +356,7 @@ fn test_system_prompt_preserved_in_locked_state() {
);
}
/// unlock -> lock でシステムプロンプトを変更できることを確認
/// Verify that system prompt can be changed after unlock -> re-lock
#[test]
fn test_system_prompt_change_after_unlock() {
let client = MockLlmClient::new(vec![]);