use crate::utils::text_complete::text_looks_complete;
fn pad(text: &str) -> String {
let filler = "This is filler prose that brings the response above the minimum length threshold. \
The model wrote real content earlier in the response and we want to focus the \
assertion on how the text ends, not how long it is. ";
format!("{filler}{text}")
}
#[test]
fn ends_with_period_is_complete() {
assert!(text_looks_complete(&pad("This is a final sentence.")));
}
#[test]
fn ends_with_question_mark_is_complete() {
assert!(text_looks_complete(&pad("Is this complete?")));
}
#[test]
fn ends_with_exclamation_is_complete() {
assert!(text_looks_complete(&pad("Done!")));
}
#[test]
fn ends_with_closing_quote_is_complete() {
assert!(text_looks_complete(&pad("She said \"yes\"")));
}
#[test]
fn ends_with_closing_paren_is_complete() {
assert!(text_looks_complete(&pad("Available actions (see docs)")));
}
#[test]
fn ends_with_ellipsis_unicode_is_complete() {
assert!(text_looks_complete(&pad(
"Waiting for the upstream\u{2026}"
)));
}
#[test]
fn ends_with_inline_code_backtick_is_complete() {
assert!(text_looks_complete(&pad("Use `cargo test`")));
}
#[test]
fn ends_with_colon_is_truncated() {
assert!(!text_looks_complete(&pad("Let me check the README:")));
}
#[test]
fn ends_with_comma_is_truncated() {
assert!(!text_looks_complete(&pad("The options include foo, bar,")));
}
#[test]
fn ends_with_semicolon_is_truncated() {
assert!(!text_looks_complete(&pad("Three statements;")));
}
#[test]
fn ends_with_em_dash_is_truncated() {
assert!(!text_looks_complete(&pad("Considering the next step -")));
}
#[test]
fn ends_with_open_paren_is_truncated() {
assert!(!text_looks_complete(&pad("See the docs (")));
}
#[test]
fn empty_text_is_not_complete() {
assert!(!text_looks_complete(""));
}
#[test]
fn short_text_below_threshold_is_not_complete() {
assert!(!text_looks_complete("All done."));
}
#[test]
fn whitespace_only_is_not_complete() {
assert!(!text_looks_complete(&format!(" {} ", "\n".repeat(10))));
}
#[test]
fn unclosed_code_fence_is_truncated() {
let text = format!(
"{}\n```rust\nfn main() {{ let x = 1;",
"Here is some leading prose that exceeds the minimum length threshold. ".repeat(4)
);
assert!(!text_looks_complete(&text));
}
#[test]
fn closed_code_fence_is_complete() {
let text = format!(
"{}\n```rust\nfn main() {{ let x = 1; }}\n```",
"Here is some leading prose that exceeds the minimum length threshold. ".repeat(4)
);
assert!(text_looks_complete(&text));
}
#[test]
fn multiple_balanced_code_fences_are_complete() {
let text = format!(
"{}\n```rust\nlet a = 1;\n```\n\nAnd another:\n```rust\nlet b = 2;\n```",
"Here is some leading prose that exceeds the minimum length threshold. ".repeat(3)
);
assert!(text_looks_complete(&text));
}
#[test]
fn ends_with_word_only_is_treated_as_complete_when_long_enough() {
let text = "Here is a long body of text where the model ended without a final \
punctuation mark but the response is otherwise coherent and addresses \
what was asked. The user can read it and the conversation continues \
from there without any visible glitch."
.to_string();
assert!(text_looks_complete(&text));
}
#[test]
fn regression_screenshot_changelog_response() {
let text = "## v0.3.31 Breakdown\n\
**37 commits across 10 categories.** Much bigger than the v0.3.31 draft \
I had prepped earlier (which only covered 20 commits). The new additions \
are: the sanitization fix, RSI subsystem classifier + skill proposal kind, \
CI/build hardening, usage dashboard consolidation, IDE-format ban in \
prompt, THINKING_QUIPS removal, and forum topic name capture.\n\n\
Ready to draft the CHANGELOG entry when you give the word.";
assert!(text_looks_complete(text));
}
#[test]
fn regression_truncated_preamble() {
let text = format!(
"{}\n\nLet me check the README:",
"I'll start by reading through the existing documentation to understand the structure. \
I want to confirm the patterns before making changes. "
.repeat(3)
);
assert!(!text_looks_complete(&text));
}