use super::*;
#[test]
fn anchored_mode_emits_line_hash_anchors() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("anc.rs");
let p = path.to_string_lossy().to_string();
let content = "fn main() {\n let x = 1;\n}\n";
std::fs::write(&path, content).unwrap();
let mut cache = SessionCache::new();
let r = handle_with_task_resolved(&mut cache, &p, "anchored", CrpMode::Off, None);
assert_eq!(r.resolved_mode, "anchored");
assert!(
r.content.contains("[anchored:"),
"anchored output must carry the self-describing legend: {}",
r.content
);
for (i, line) in content.lines().enumerate() {
let n = i + 1;
let expected = format!("{n}:{}|{line}", crate::core::anchor::line_hash(line));
assert!(
r.content.contains(&expected),
"missing anchor for line {n}: expected `{expected}` in:\n{}",
r.content
);
}
}
#[test]
fn anchored_mode_is_not_capped_to_raw_on_small_files() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tiny.rs");
let p = path.to_string_lossy().to_string();
std::fs::write(&path, "a\n").unwrap();
let mut cache = SessionCache::new();
let r = handle_with_task_resolved(&mut cache, &p, "anchored", CrpMode::Off, None);
assert!(
r.content.contains("|a"),
"anchored output must keep anchors even on a tiny file: {}",
r.content
);
assert!(r.content.contains("[anchored:"), "legend must survive");
}
#[test]
fn raw_mode_returns_exact_file_content() {
let _lock = crate::core::data_dir::test_env_lock();
let content = "fn main() {\n println!(\"hello\");\n}\n";
let (output, _sent) = render::process_mode(
content,
"raw",
"F1",
"main.rs",
"rs",
100,
CrpMode::Off,
"/tmp/main.rs",
None,
);
assert_eq!(
output, content,
"raw mode must return exact file content with zero overhead"
);
assert!(
!output.contains("main.rs"),
"raw mode must not contain filename header"
);
assert!(!output.contains("deps"), "raw mode must not contain deps");
}
#[test]
fn verbatim_modes_preserve_decorative_comment_lines() {
let _lock = crate::core::data_dir::test_env_lock();
let sep_em = "// ————————————————————————————————————————";
let sep_dash = "// ------------------------------------------";
let content = format!(
"import {{ describe, it, expect }} from \"vitest\";\n\
\n\
{sep_em}\n\
// Section: arithmetic\n\
{sep_em}\n\
describe(\"add\", () => {{\n it(\"adds\", () => expect(1 + 1).toBe(2));\n}});\n\
\n\
{sep_dash}\n\
// Section: strings\n\
{sep_dash}\n"
);
for mode in ["full", "raw"] {
let (output, _) = render::process_mode(
&content,
mode,
"F1",
"math.test.ts",
"ts",
count_tokens(&content),
CrpMode::Off,
"/tmp/math.test.ts",
None,
);
assert!(
output.contains(sep_em) && output.contains(sep_dash),
"{mode} mode must keep every separator comment verbatim:\n{output}"
);
for line in content.lines().filter(|l| !l.is_empty()) {
assert!(
output.contains(line),
"{mode} mode dropped a source line: {line:?}"
);
}
}
let window = render::extract_line_range(&content, "1-5");
assert!(
window.contains(sep_em),
"lines: window dropped the separator comment:\n{window}"
);
assert!(
window.contains(" 3| ") && window.contains(" 5| "),
"lines: window must number the separator lines (3 and 5):\n{window}"
);
}
#[test]
fn process_mode_output_is_byte_stable_across_calls() {
let _iso = crate::core::data_dir::isolated_data_dir();
crate::test_env::remove_var("LEAN_CTX_SAVINGS_FOOTER");
crate::test_env::remove_var("LEAN_CTX_SHOW_SAVINGS");
crate::test_env::remove_var("LEAN_CTX_QUIET");
let content: String = (0..120)
.map(|i| format!("pub fn handler_{i}(x: u32) -> u32 {{ x * {i} }}"))
.collect::<Vec<_>>()
.join("\n");
let tokens = count_tokens(&content);
for mode in [
"map",
"signatures",
"reference",
"aggressive",
"entropy",
"raw",
"lines:5-20",
"anchored",
] {
let run = || {
render::process_mode(
&content,
mode,
"F1",
"stable.rs",
"rs",
tokens,
CrpMode::Off,
"/tmp/stable.rs",
None,
)
.0
};
let first = run();
let second = run();
assert_eq!(
first, second,
"mode '{mode}' produced non-deterministic output"
);
}
}
#[test]
fn recovery_footer_is_compressed_only_and_togglable() {
let _iso = crate::core::data_dir::isolated_data_dir();
let content: String = (0..120)
.map(|i| format!("pub fn handler_{i}(x: u32) -> u32 {{ x * {i} }}"))
.collect::<Vec<_>>()
.join("\n");
let tokens = count_tokens(&content);
let run = |mode: &str| {
render::process_mode(
&content,
mode,
"F1",
"rec.rs",
"rs",
tokens,
CrpMode::Off,
"/tmp/rec.rs",
None,
)
.0
};
crate::test_env::set_var("LEAN_CTX_RECOVERY_HINTS", "minimal");
let sigs = run("signatures");
assert!(
sigs.contains("read \"/tmp/rec.rs\" directly (no MCP)"),
"compressed view must surface the MCP-free recovery path: {sigs}"
);
assert_eq!(sigs, run("signatures"), "footer must be byte-stable");
assert!(
!run("raw").contains("(no MCP)"),
"raw view needs no recovery footer"
);
crate::test_env::set_var("LEAN_CTX_RECOVERY_HINTS", "off");
assert!(
!run("signatures").contains("(no MCP)"),
"recovery_hints=off must drop the footer"
);
crate::test_env::remove_var("LEAN_CTX_RECOVERY_HINTS");
}
#[test]
fn raw_mode_no_savings_footer() {
let _lock = crate::core::data_dir::test_env_lock();
let content = "x = 1\n";
let (output, _) = render::process_mode(
content,
"raw",
"F1",
"tiny.py",
"py",
50,
CrpMode::Off,
"/tmp/tiny.py",
None,
);
assert!(
!output.contains('\u{2500}'),
"raw mode must not contain savings footer box-drawing chars"
);
assert_eq!(output, content);
}