use super::*;
use std::collections::HashSet;
const PROMPT: &str = r#"{"type":"user","uuid":"p1","parentUuid":"att1","isMeta":true,"promptId":"pr-1","userType":"external","timestamp":"2026-06-07T05:00:00.000Z","message":{"role":"user","content":[{"type":"text","text":"Continue from where you left off."}]}}"#;
const PLACEHOLDER: &str = r#"{"type":"assistant","uuid":"h1","parentUuid":"p1","isApiErrorMessage":false,"timestamp":"2026-06-07T05:00:00.000Z","message":{"role":"assistant","model":"<synthetic>","stop_reason":"stop_sequence","content":[{"type":"text","text":"No response requested."}]}}"#;
fn ctx_with(prompt_uuids: &HashSet<String>) -> ClassifyCtx<'_> {
ClassifyCtx {
resume_prompt_uuids: Some(prompt_uuids),
..ClassifyCtx::top_level()
}
}
fn uuid_set(ids: &[&str]) -> HashSet<String> {
ids.iter().map(|s| (*s).to_string()).collect()
}
#[test]
fn the_pair_classifies_under_its_own_leaves() {
let prompt = parse(PROMPT);
let placeholder = parse(PLACEHOLDER);
let prompts = uuid_set(&["p1"]);
let ctx = ctx_with(&prompts);
assert_eq!(prompt.classify(&ctx), vec![Class::ResumePrompt]);
assert_eq!(placeholder.classify(&ctx), vec![Class::ResumePlaceholder]);
assert!(prompt.is_resume_prompt());
assert!(placeholder.is_resume_placeholder());
}
#[test]
fn neither_half_opens_a_turn() {
let prompt = parse(PROMPT);
assert!(!prompt.is_genuine_user());
assert!(!prompt.opens_turn());
assert!(prompt.reconstructed_user_text(None).is_none());
let placeholder = parse(PLACEHOLDER);
assert!(!placeholder.is_genuine_user());
assert!(!placeholder.opens_turn());
}
#[test]
fn both_halves_are_delivered_and_take_no_override() {
let prompt = parse(PROMPT);
let placeholder = parse(PLACEHOLDER);
assert_eq!(prompt.delivery_override(), None);
assert_eq!(placeholder.delivery_override(), None);
assert!(Class::ResumePrompt.llm_visible());
assert!(Class::ResumePlaceholder.llm_visible());
let api_error = parse(
r#"{"type":"assistant","isApiErrorMessage":true,"message":{"role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"API Error: overloaded"}]}}"#,
);
assert_eq!(api_error.delivery_override(), Some(false));
}
#[test]
fn pairing_is_by_parent_and_never_guessed() {
let placeholder = parse(PLACEHOLDER);
let prompts = uuid_set(&["p1"]);
assert_eq!(placeholder.resume_paired(&ctx_with(&prompts)), Some(true));
let others = uuid_set(&["somewhere-else"]);
assert_eq!(placeholder.resume_paired(&ctx_with(&others)), Some(false));
assert_eq!(placeholder.resume_paired(&ClassifyCtx::top_level()), None);
assert_eq!(parse(PROMPT).resume_paired(&ctx_with(&prompts)), None);
}
#[test]
fn an_unpaired_placeholder_keeps_the_leaf() {
let after_interrupt = parse(
r#"{"type":"assistant","uuid":"h2","parentUuid":"int1","isApiErrorMessage":false,"message":{"role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."}]}}"#,
);
let prompts = uuid_set(&["p1"]);
let ctx = ctx_with(&prompts);
assert_eq!(
after_interrupt.classify(&ctx),
vec![Class::ResumePlaceholder]
);
assert_eq!(after_interrupt.resume_paired(&ctx), Some(false));
}
#[test]
fn a_real_assistant_turn_is_never_the_placeholder() {
let real = parse(
r#"{"type":"assistant","message":{"role":"assistant","model":"claude-opus-4-8","content":[{"type":"text","text":"No response requested."}]}}"#,
);
assert!(!real.is_resume_placeholder());
assert_eq!(
real.classify(&ClassifyCtx::top_level()),
vec![Class::AgentMessage]
);
let notice = parse(
r#"{"type":"assistant","isApiErrorMessage":true,"message":{"role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."}]}}"#,
);
assert!(!notice.is_resume_placeholder());
let two_blocks = parse(
r#"{"type":"assistant","message":{"role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."},{"type":"text","text":"and more"}]}}"#,
);
assert!(!two_blocks.is_resume_placeholder());
}
#[test]
fn prompt_text_that_differs_by_one_char_carries_no_label() {
let near = parse(
r#"{"type":"user","isMeta":true,"message":{"role":"user","content":"Continue from where you left off"}}"#,
);
assert!(!near.is_resume_prompt());
assert!(near.classify(&ClassifyCtx::top_level()).is_empty());
let padded = parse(
r#"{"type":"user","isMeta":true,"message":{"role":"user","content":" Continue from where you left off."}}"#,
);
assert!(padded.is_resume_prompt());
}
#[test]
fn a_human_message_that_starts_with_the_sentence_stays_the_human() {
let typed = parse(
r#"{"type":"user","uuid":"u9","message":{"role":"user","content":"Continue from where you left off. and also fix the tests"}}"#,
);
assert!(!typed.is_resume_prompt());
assert_eq!(
typed.classify(&ClassifyCtx::top_level()),
vec![Class::UserMessage]
);
assert!(typed.is_genuine_user());
assert!(typed.opens_turn());
}
#[test]
fn an_ismeta_prompt_with_a_promptsource_is_still_accepted() {
let with_source = parse(
r#"{"type":"user","uuid":"p2","isMeta":true,"promptSource":"system","message":{"role":"user","content":[{"type":"text","text":"Continue from where you left off."}]}}"#,
);
assert!(with_source.is_resume_prompt());
assert_eq!(
with_source.classify(&ClassifyCtx::top_level()),
vec![Class::ResumePrompt]
);
}
#[test]
fn a_placeholder_with_no_parent_reads_unpaired() {
let orphan = parse(
r#"{"type":"assistant","uuid":"h3","isApiErrorMessage":false,"message":{"role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."}]}}"#,
);
let prompts = uuid_set(&["p1"]);
assert!(orphan.parent_uuid.is_none());
assert_eq!(orphan.resume_paired(&ctx_with(&prompts)), Some(false));
}
#[test]
fn a_claude_code_set_longer_variant_is_still_the_prompt() {
let restarted = parse(
r#"{"type":"user","isMeta":true,"message":{"role":"user","content":"Continue from where you left off. Note: this session was automatically restarted after its process exited unexpectedly."}}"#,
);
assert!(restarted.is_resume_prompt());
assert_eq!(
restarted.classify(&ClassifyCtx::top_level()),
vec![Class::ResumePrompt]
);
}
#[test]
fn a_tool_result_carrier_is_never_the_prompt() {
let carrier = parse(
r#"{"type":"user","isMeta":true,"message":{"role":"user","content":[{"type":"text","text":"Continue from where you left off."},{"type":"tool_result","tool_use_id":"t1","content":"ok"}]}}"#,
);
assert!(!carrier.is_resume_prompt());
assert_eq!(
carrier.classify(&ClassifyCtx::top_level()),
vec![Class::AgentToolResult]
);
}
#[test]
fn single_text_content_mirrors_the_producers_reader() {
let s = parse(r#"{"type":"user","message":{"role":"user","content":"solo"}}"#);
assert_eq!(s.single_text_content(), Some("solo"));
let one = parse(
r#"{"type":"user","message":{"role":"user","content":[{"type":"text","text":"one"}]}}"#,
);
assert_eq!(one.single_text_content(), Some("one"));
let two = parse(
r#"{"type":"user","message":{"role":"user","content":[{"type":"text","text":"a"},{"type":"text","text":"b"}]}}"#,
);
assert_eq!(two.single_text_content(), None);
let non_text = parse(
r#"{"type":"user","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"t","content":"x"}]}}"#,
);
assert_eq!(non_text.single_text_content(), None);
}