use claude_codes::{
assert_fully_wrapped, ClaudeOutput, StreamPostQueuedBehind, SystemSubtype, TurnStartResumeKind,
};
use serde_json::json;
#[test]
fn assistant_carries_thinking_duration_ms() {
let frame = json!({
"type": "assistant",
"message": {
"id": "msg_01",
"type": "message",
"role": "assistant",
"model": "claude-fable-5-1",
"content": [{"type": "thinking", "thinking": "hmm", "signature": "sig"}],
"stop_reason": null,
"stop_sequence": null,
"usage": {"input_tokens": 1, "output_tokens": 1}
},
"parent_tool_use_id": null,
"session_id": "s1",
"uuid": "u1",
"thinking_duration_ms": 1375
});
assert_fully_wrapped(&frame);
let ClaudeOutput::Assistant(msg) = serde_json::from_value(frame).unwrap() else {
panic!("expected Assistant");
};
assert_eq!(msg.thinking_duration_ms, Some(1375));
}
#[test]
fn result_carries_text_post_queue_and_resume_kind() {
let frame = json!({
"type": "result",
"subtype": "success",
"is_error": false,
"duration_ms": 1200,
"duration_api_ms": 900,
"num_turns": 1,
"result": "done",
"stop_reason": "end_turn",
"session_id": "s1",
"uuid": "u1",
"total_cost_usd": 0.01,
"usage": {
"input_tokens": 10,
"output_tokens": 38,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
},
"modelUsage": {},
"permission_denials": [],
"time_to_request_ms": 40,
"time_to_request_phases_ms": {
"system_prompt": 10,
"resume_other": 1,
"resume_store_confirm": 4,
"turn_start_tool_run": 20,
"other": 5
},
"turn_start_resume_kind": "turn_handoff",
"first_text_post_ms": 70,
"first_text_post_queue_wait_ms": 12,
"first_text_post_queued_behind": "durable_post"
});
assert_fully_wrapped(&frame);
let ClaudeOutput::Result(res) = serde_json::from_value(frame).unwrap() else {
panic!("expected Result");
};
assert_eq!(
res.turn_start_resume_kind,
Some(TurnStartResumeKind::TurnHandoff)
);
assert_eq!(res.first_text_post_queue_wait_ms, Some(12));
assert_eq!(
res.first_text_post_queued_behind,
Some(StreamPostQueuedBehind::DurablePost)
);
let phases = res.time_to_request_phases_ms.expect("phases");
assert_eq!(phases.get("turn_start_tool_run"), Some(&20));
}
#[test]
fn turn_start_resume_kind_is_an_open_set() {
let kind: TurnStartResumeKind = serde_json::from_value(json!("future_kind")).unwrap();
assert_eq!(kind, TurnStartResumeKind::Unknown("future_kind".into()));
assert_eq!(kind.to_string(), "future_kind");
assert_eq!(
serde_json::to_value(TurnStartResumeKind::DeferredToolUse).unwrap(),
json!("deferred_tool_use")
);
}
#[test]
fn turn_handoff_available_carries_home_files_and_file_names() {
let frame = json!({
"type": "system",
"subtype": "turn_handoff_available",
"v": 1,
"tools": ["Bash"],
"worker_epoch": 5,
"staged_files": true,
"home_files": true,
"file_names": true,
"uuid": "u1",
"session_id": "s1"
});
assert_fully_wrapped(&frame);
let ClaudeOutput::System(sys) = serde_json::from_value(frame).unwrap() else {
panic!("expected System");
};
assert_eq!(sys.subtype, SystemSubtype::TurnHandoffAvailable);
let handoff = sys.as_turn_handoff_available().expect("typed accessor");
assert_eq!(handoff.home_files, Some(true));
assert_eq!(handoff.file_names, Some(true));
assert!(handoff.extra.is_empty());
}