use super::*;
#[test]
fn cap_to_raw_falls_back_when_framing_inflates() {
let raw = "pub fn a() {}\n";
let framed = format!("F1=x.rs 1L\n deps foo,bar\n{raw}");
let raw_tokens = count_tokens(raw);
let framed_tokens = count_tokens(&framed);
assert!(
framed_tokens > raw_tokens,
"fixture must inflate to exercise the guard"
);
assert_eq!(
cap_to_raw(framed, framed_tokens, raw, raw_tokens, "signatures"),
raw,
"framing larger than raw must fall back to bare content — and on a file\n this small the notice is suppressed so the #361 cap still holds"
);
}
#[test]
fn cap_to_raw_keeps_framing_when_not_larger() {
let raw = "a long original body that compresses well";
let framed = "sig summary".to_string();
let framed_tokens = count_tokens(&framed);
assert_eq!(
cap_to_raw(framed.clone(), framed_tokens, raw, 100, "signatures"),
framed,
"output at or below raw must be returned untouched"
);
}
#[test]
fn cap_to_raw_keeps_framing_for_empty_file() {
let framed = "F1=empty.rs 0L".to_string();
let framed_tokens = count_tokens(&framed);
assert_eq!(
cap_to_raw(framed.clone(), framed_tokens, "", 0, "signatures"),
framed,
"empty files keep their framing signal"
);
}
fn big_raw() -> String {
"pub fn item(value: u32) -> u32 { value.saturating_add(1) }\n".repeat(120)
}
#[test]
fn cap_to_raw_labels_silent_fallback_for_compressed_request() {
let raw = big_raw();
let raw_tokens = count_tokens(&raw);
assert!(
raw_tokens >= crate::tools::ctx_read::render::NO_COMPRESSION_BANNER_MIN_TOKENS,
"fixture must exceed the banner threshold"
);
let framed = format!("F1=big.rs 120L\n{raw}");
let framed_tokens = count_tokens(&framed);
let out = cap_to_raw(framed, framed_tokens, &raw, raw_tokens, "signatures");
assert!(
out.starts_with("[lean-ctx] no compression applied (mode=signatures)"),
"a summary request answered with the whole file must say so: {}",
out.lines().next().unwrap_or_default()
);
assert!(
out.ends_with(&raw),
"the full content still follows the notice"
);
}
#[test]
fn cap_to_raw_stays_silent_for_verbatim_full_request() {
let raw = big_raw();
let raw_tokens = count_tokens(&raw);
let framed = format!("F1=big.rs 120L\n{raw}");
let framed_tokens = count_tokens(&framed);
assert_eq!(
cap_to_raw(framed, framed_tokens, &raw, raw_tokens, "full"),
raw,
"`full` asked for the whole file — stripping framing is not a degradation"
);
}
#[test]
fn cap_to_raw_notice_never_breaks_the_361_cap() {
let raw = "pub fn a() {}\n";
let raw_tokens = count_tokens(raw);
let framed = format!("F1=x.rs 1L\n deps foo,bar\n{raw}");
let framed_tokens = count_tokens(&framed);
let out = cap_to_raw(framed, framed_tokens, raw, raw_tokens, "map");
assert_eq!(out, raw);
assert!(count_tokens(&out) <= raw_tokens);
}
#[test]
fn auto_read_never_inflates_small_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("small.rs");
let p = path.to_string_lossy().to_string();
let content =
"use std::io;\n\npub fn greet(name: &str) -> String {\n format!(\"hi {name}\")\n}\n";
std::fs::write(&path, content).unwrap();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "auto", CrpMode::Off, None);
assert!(
out.output_tokens <= count_tokens(content),
"auto cold read inflated a small file: {} output tok > {} raw tok\n{}",
out.output_tokens,
count_tokens(content),
out.content
);
}
const AUTO_CORPUS: &[&str] = &[
"crates/lean-ctx-fs/src/error.rs",
"src/core/surprise.rs",
"crates/lean-ctx-compress/src/compressor.rs",
"src/core/entropy.rs",
"crates/lean-ctx-server/src/tools/ctx_read/render.rs",
"../VISION.md",
"../README.md",
"../integrations/hermes-lean-ctx/__init__.py",
"../packages/vscode-lean-ctx/src/extension.ts",
];
#[test]
fn auto_read_never_exceeds_raw_on_corpus() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
let root = std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../.."));
let mut checked = 0;
for rel in AUTO_CORPUS {
let Ok(content) = std::fs::read_to_string(root.join(rel)) else {
continue;
};
let dir = tempfile::tempdir().unwrap();
let name = rel.rsplit('/').next().unwrap();
let path = dir.path().join(name);
std::fs::write(&path, &content).unwrap();
let p = path.to_string_lossy().to_string();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "auto", CrpMode::Off, None);
let raw = count_tokens(&content);
assert!(
out.output_tokens <= raw,
"{rel}: auto ({}) {} tok > raw {raw} tok",
out.resolved_mode,
out.output_tokens
);
assert!(
!out.content.contains("no compression applied"),
"{rel}: auto must not announce its own fallback"
);
checked += 1;
}
assert!(checked >= 5, "corpus mostly missing ({checked} files read)");
}
#[test]
fn auto_cognitive_fallback_is_bare_file() {
let _lock = lean_ctx::core::data_dir::test_env_lock();
lean_ctx::test_env::set_var("LEAN_CTX_SHOW_SAVINGS", "0");
let root = std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../.."));
let content = std::fs::read_to_string(root.join("crates/lean-ctx-fs/src/error.rs")).unwrap();
let raw = count_tokens(&content);
let render = || {
process_mode_tuned(
&content,
"cognitive",
"F1",
"error.rs",
"rs",
raw,
CrpMode::Off,
"crates/lean-ctx-fs/src/error.rs",
None,
ReadTuning::default(),
)
.0
};
let auto = {
let _auto = super::render::AutoRequestGuard::new();
render()
};
let explicit = render();
lean_ctx::test_env::remove_var("LEAN_CTX_SHOW_SAVINGS");
assert!(
count_tokens(&auto) <= raw,
"auto: {} > {raw}",
count_tokens(&auto)
);
assert!(!auto.contains("no compression applied"), "{auto}");
if count_tokens(&explicit) > raw {
assert!(explicit.contains("no compression applied"), "{explicit}");
}
}
#[test]
fn cognitive_read_never_inflates_small_tier_file() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("mid.rs");
let p = path.to_string_lossy().to_string();
let mut content = String::new();
for i in 0..40 {
content.push_str(&format!(
"pub fn step_{i}(input: &[u8]) -> usize {{\n input.iter().filter(|b| **b > {i}).count()\n}}\n\n"
));
}
std::fs::write(&path, &content).unwrap();
let raw_tokens = count_tokens(&content);
assert!(
(500..=2000).contains(&raw_tokens),
"fixture tier: {raw_tokens}"
);
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "cognitive", CrpMode::Off, None);
assert!(
out.output_tokens <= raw_tokens,
"cognitive read inflated a small-tier file: {} > {raw_tokens}\n{}",
out.output_tokens,
out.content
);
}
#[test]
fn full_read_never_inflates_tiny_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("tiny.rs");
let p = path.to_string_lossy().to_string();
let content = "pub fn a() {}\n";
std::fs::write(&path, content).unwrap();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "full", CrpMode::Off, None);
assert!(
out.output_tokens <= count_tokens(content),
"full cold read inflated a tiny file: {} > {}\n{}",
out.output_tokens,
count_tokens(content),
out.content
);
}
#[test]
fn auto_read_still_compresses_large_file() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("big.rs");
let p = path.to_string_lossy().to_string();
let mut content = String::new();
for i in 0..400 {
content.push_str(&format!(
"pub fn function_number_{i}(x: i32, y: i32) -> i32 {{\n let z = x + y + {i};\n z * 2\n}}\n\n"
));
}
std::fs::write(&path, &content).unwrap();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "auto", CrpMode::Off, None);
assert!(
out.output_tokens < count_tokens(&content),
"auto read of a large file must still compress: {} >= {} (mode={})",
out.output_tokens,
count_tokens(&content),
out.resolved_mode
);
}
#[test]
fn explicit_compressed_mode_capped_on_tiny_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("lib.rs");
let p = path.to_string_lossy().to_string();
let content = "pub fn alpha() {}\npub fn beta() {}\n";
std::fs::write(&path, content).unwrap();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "signatures", CrpMode::Off, None);
assert!(
out.output_tokens <= count_tokens(content),
"explicit signatures of a tiny file must not inflate past raw: {} > {}\n{}",
out.output_tokens,
count_tokens(content),
out.content
);
}
#[test]
fn explicit_signatures_still_compresses_large_file() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("big.rs");
let p = path.to_string_lossy().to_string();
let mut content = String::new();
for i in 0..400 {
content.push_str(&format!(
"pub fn function_number_{i}(x: i32, y: i32) -> i32 {{\n let z = x + y + {i};\n z * 2\n}}\n\n"
));
}
std::fs::write(&path, &content).unwrap();
let mut cache = SessionCache::new();
let out = handle_with_task_resolved(&mut cache, &p, "signatures", CrpMode::Off, None);
assert!(
out.output_tokens < count_tokens(&content),
"explicit signatures of a large file must compress: {} >= {}",
out.output_tokens,
count_tokens(&content)
);
}
#[test]
fn cache_hit_stub_is_byte_stable_across_rereads() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
let _env_lock = lean_ctx::core::data_dir::test_env_lock();
lean_ctx::test_env::set_var("LCTX_FULL_DEGRADATION_THRESHOLD", "100");
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("stable.rs");
let p = path.to_string_lossy().to_string();
std::fs::write(&path, "pub fn alpha() {}\npub fn beta() {}\n").unwrap();
let mut cache = SessionCache::new();
let _ = handle_with_task_resolved(&mut cache, &p, "full", CrpMode::Off, None);
let r2 = handle_with_task_resolved(&mut cache, &p, "full", CrpMode::Off, None);
let r3 = handle_with_task_resolved(&mut cache, &p, "full", CrpMode::Off, None);
let r4 = handle_with_task_resolved(&mut cache, &p, "full", CrpMode::Off, None);
assert_eq!(
r2.content, r3.content,
"re-read drifted between reads 2 and 3"
);
assert_eq!(
r3.content, r4.content,
"re-read drifted between reads 3 and 4"
);
assert!(
!r2.content.contains("(read"),
"read-count note must not appear in the cache-hit body: {}",
r2.content
);
}
#[test]
fn compress_protect_glob_forces_full_verbatim_read() {
let _iso = lean_ctx::core::data_dir::isolated_data_dir();
lean_ctx::test_env::set_var("LEAN_CTX_SHOW_SAVINGS", "0");
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("protected.rs");
let p = path.to_string_lossy().to_string();
let mut content = String::new();
for i in 0..60 {
content.push_str(&format!(
"// distinctive-comment-{i}\npub fn handler_{i}(x: u32) -> u32 {{ x + {i} }}\n"
));
}
std::fs::write(&path, &content).unwrap();
lean_ctx::core::config::Config::update_global(|c| c.proxy.compress_protect = None).unwrap();
let mut cold = SessionCache::new();
let stripped = handle_with_task_resolved(&mut cold, &p, "aggressive", CrpMode::Off, None);
assert!(
!stripped.content.contains("// distinctive-comment-0"),
"control: aggressive must strip comments when the path is not protected"
);
lean_ctx::core::config::Config::update_global(|c| {
c.proxy.compress_protect = Some(vec!["*.rs".into()]);
})
.unwrap();
let mut warm = SessionCache::new();
let protected = handle_with_task_resolved(&mut warm, &p, "aggressive", CrpMode::Off, None);
assert!(
protected.content.contains("// distinctive-comment-0")
&& protected.content.contains("// distinctive-comment-59"),
"a protected path must be returned verbatim with every comment intact"
);
lean_ctx::core::config::Config::update_global(|c| c.proxy.compress_protect = None).unwrap();
lean_ctx::test_env::remove_var("LEAN_CTX_SHOW_SAVINGS");
}