yoi/docs/test-fixtures.md
2026-04-06 02:21:41 +09:00

159 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# テスト Fixture 仕様
## 概要
テスト用 fixture は、実 API のストリーミング応答を JSONL 形式で録画したファイル。
`MockLlmClient::from_fixture()` でロードし、API キー不要・決定的なテスト実行を実現する。
## ファイル形式
```
{メタデータ行: JSON}
{イベント行: JSON}
{イベント行: JSON}
...
```
- **1行目**: メタデータ (`timestamp`, `model`, `description`)
- **2行目以降**: 録画イベント (`elapsed_ms`, `event_type`, `data`)
- `data` フィールドに `Event` の JSON 文字列が入る
## ファイル配置
```
crates/llm-worker/tests/fixtures/
anthropic/
simple_text.jsonl
tool_call.jsonl
long_text.jsonl
openai/
simple_text.jsonl
tool_call.jsonl
long_text.jsonl
gemini/
simple_text.jsonl
tool_call.jsonl
long_text.jsonl
ollama/
simple_text.jsonl
tool_call.jsonl
long_text.jsonl
```
## シナリオ定義
### simple_text
単純なテキスト応答。
| 項目 | 値 |
|---|---|
| ファイル名 | `simple_text.jsonl` |
| system prompt | `"You are a helpful assistant. Be very concise."` |
| user message | `"Say hello in one word."` |
| max_tokens | 50 |
| ツール | なし |
**期待パターン**:
- `BlockStart(Text)` が1つ以上
- `BlockDelta(Text)` が1つ以上
- `BlockStop(Text)` が1つ以上
- 応答が短い1単語程度
**用途**: 基本的なストリーミング動作、Timeline テキスト収集、Worker の単純な run 完了
### tool_call
ツール呼び出しを含む応答。
| 項目 | 値 |
|---|---|
| ファイル名 | `tool_call.jsonl` |
| system prompt | `"You are a helpful assistant. Use tools when appropriate."` |
| user message | `"What's the weather in Tokyo? Use the get_weather tool."` |
| max_tokens | 200 |
| ツール | `get_weather(city: string)` |
**期待パターン**:
- `BlockStart(ToolUse)` を含む
- ToolUse ブロック内に `tool_call_id`, `name: "get_weather"` がある
- tool input JSON に `"city"` キーを含む
**用途**: ToolCallCollector、Worker のツール実行フロー、Session の ToolResults ログ記録
### long_text
長文テキスト応答。
| 項目 | 値 |
|---|---|
| ファイル名 | `long_text.jsonl` |
| system prompt | `"You are a creative writer."` |
| user message | `"Write a short story about a robot discovering a garden. It should be at least 300 words."` |
| max_tokens | 1000 |
| ツール | なし |
**期待パターン**:
- `BlockDelta(Text)` が複数(ストリーミングチャンク)
- 最終テキストが 300 語以上
**用途**: ストリーミングの分割配信検証、Subscriber のデルタ受信テスト
## 共通検証項目
全 fixture に対して以下を検証する(`assert_*` ヘルパー関数群):
- `assert_events_deserialize` — 全イベントが `Event` にデシリアライズできる
- `assert_event_sequence` — BlockStart → BlockDelta → BlockStop の基本シーケンス
- `assert_usage_tokens``Usage` イベントが含まれる
- `assert_timeline_integration` — Timeline に流してテキスト収集できる
## 録画手順
### 前提
- API キーが環境変数に設定されていること
- `crates/llm-worker` ディレクトリで実行
### コマンド
```bash
# 単一シナリオ録画
ANTHROPIC_API_KEY=... cargo run --example record_test_fixtures -- -s simple_text
# 全シナリオ録画
ANTHROPIC_API_KEY=... cargo run --example record_test_fixtures -- --all
# プロバイダー指定
OPENAI_API_KEY=... cargo run --example record_test_fixtures -- --all -c openai
GEMINI_API_KEY=... cargo run --example record_test_fixtures -- --all -c gemini
# モデル指定
ANTHROPIC_API_KEY=... cargo run --example record_test_fixtures -- --all -m claude-sonnet-4-20250514
```
### シナリオ定義の場所
`crates/llm-worker/examples/record_test_fixtures/scenarios.rs`
新しいシナリオを追加する場合はこのファイルに `TestScenario` を追加し、
`scenarios()` 関数の返り値に含める。
## 録画後の確認チェックリスト
録画後、テストに組み込む前に以下を手動確認する:
- [ ] JSONL の各行が valid JSON か(`jq . < fixture.jsonl` で確認
- [ ] 1行目にメタデータ`timestamp`, `model`, `description`が入っているか
- [ ] simple_text: `BlockStart` `BlockDelta` `BlockStop` シーケンスがあるか
- [ ] tool_call: `BlockStart` `"block_type":"ToolUse"` を含むか
- [ ] long_text: `BlockDelta` が複数行あるかストリーミング分割の確認
- [ ] fixture `Usage` イベントが含まれるか
- [ ] エラーイベントが混入していないか
## 注意事項
- fixture API の応答に依存するためモデルバージョンアップで再録画が必要になることがある
- 録画の自動化CI での定期録画等は行わない手動実行 + 目視確認のフロー
- fixture が存在しない場合対応するテストは skip される`if !fixture_path.exists() { return; }` パターン