use super::*;
use std::time::Duration;
#[test]
fn test_header_toon_format_no_brackets() {
let _lock = crate::core::data_dir::test_env_lock();
std::env::set_var("LEAN_CTX_META", "1");
let content = "use std::io;\nfn main() {}\n";
let header = build_header("F1", "main.rs", "rs", content, 2, false);
assert!(!header.contains('['));
assert!(!header.contains(']'));
assert!(header.contains("F1=main.rs 2L"));
std::env::remove_var("LEAN_CTX_META");
}
#[test]
fn test_header_toon_deps_indented() {
let _lock = crate::core::data_dir::test_env_lock();
std::env::set_var("LEAN_CTX_META", "1");
let content = "use crate::core::cache;\nuse crate::tools;\npub fn main() {}\n";
let header = build_header("F1", "main.rs", "rs", content, 3, true);
if header.contains("deps") {
assert!(
header.contains("\n deps "),
"deps should use indented TOON format"
);
assert!(
!header.contains("deps:["),
"deps should not use bracket format"
);
}
std::env::remove_var("LEAN_CTX_META");
}
#[test]
fn test_header_toon_saves_tokens() {
let _lock = crate::core::data_dir::test_env_lock();
std::env::set_var("LEAN_CTX_META", "1");
let content = "use crate::foo;\nuse crate::bar;\npub fn baz() {}\npub fn qux() {}\n";
let old_header = "F1=main.rs [4L +] deps:[foo,bar] exports:[baz,qux]".to_string();
let new_header = build_header("F1", "main.rs", "rs", content, 4, true);
let old_tokens = count_tokens(&old_header);
let new_tokens = count_tokens(&new_header);
assert!(
new_tokens <= old_tokens,
"TOON header ({new_tokens} tok) should be <= old format ({old_tokens} tok)"
);
std::env::remove_var("LEAN_CTX_META");
}
#[test]
fn test_tdd_symbols_are_compact() {
let symbols = [
"⊕", "⊖", "∆", "→", "⇒", "✓", "✗", "⚠", "λ", "§", "∂", "τ", "ε",
];
for sym in &symbols {
let tok = count_tokens(sym);
assert!(tok <= 2, "Symbol {sym} should be 1-2 tokens, got {tok}");
}
}
#[test]
fn test_task_mode_filters_content() {
let content = (0..200)
.map(|i| {
if i % 20 == 0 {
format!("fn validate_token(token: &str) -> bool {{ /* line {i} */ }}")
} else {
format!("fn unrelated_helper_{i}(x: i32) -> i32 {{ x + {i} }}")
}
})
.collect::<Vec<_>>()
.join("\n");
let full_tokens = count_tokens(&content);
let task = Some("fix bug in validate_token");
let (result, result_tokens) = process_mode(
&content,
"task",
"F1",
"test.rs",
"rs",
full_tokens,
CrpMode::Off,
"test.rs",
task,
);
assert!(
result_tokens < full_tokens,
"task mode ({result_tokens} tok) should be less than full ({full_tokens} tok)"
);
assert!(
result.contains("task-filtered"),
"output should contain task-filtered marker"
);
}
#[test]
fn test_task_mode_without_task_returns_full() {
let content = "fn main() {}\nfn helper() {}\n";
let tokens = count_tokens(content);
let (result, _sent) = process_mode(
content,
"task",
"F1",
"test.rs",
"rs",
tokens,
CrpMode::Off,
"test.rs",
None,
);
assert!(
result.contains("no task set"),
"should indicate no task: {result}"
);
}
#[test]
fn test_reference_mode_one_line() {
let content = "fn main() {}\nfn helper() {}\nfn other() {}\n";
let tokens = count_tokens(content);
let (result, _sent) = process_mode(
content,
"reference",
"F1",
"test.rs",
"rs",
tokens,
CrpMode::Off,
"test.rs",
None,
);
let lines: Vec<&str> = result.lines().collect();
assert!(
lines.len() <= 3,
"reference mode should be very compact, got {} lines",
lines.len()
);
assert!(result.contains("lines"), "should contain line count");
assert!(result.contains("tok"), "should contain token count");
}
#[test]
fn cached_lines_mode_invalidates_on_mtime_change() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("file.txt");
let p = path.to_string_lossy().to_string();
std::fs::write(&path, "one\nsecond\n").unwrap();
let mut cache = SessionCache::new();
let r1 = handle_with_task_resolved(&mut cache, &p, "lines:1-1", CrpMode::Off, None);
let l1: Vec<&str> = r1.content.lines().collect();
let got1 = l1.get(1).copied().unwrap_or_default().trim();
let got1 = got1.split_once('|').map_or(got1, |(_, s)| s.trim());
assert_eq!(got1, "one");
std::thread::sleep(Duration::from_secs(1));
std::fs::write(&path, "two\nsecond\n").unwrap();
let r2 = handle_with_task_resolved(&mut cache, &p, "lines:1-1", CrpMode::Off, None);
let l2: Vec<&str> = r2.content.lines().collect();
let got2 = l2.get(1).copied().unwrap_or_default().trim();
let got2 = got2.split_once('|').map_or(got2, |(_, s)| s.trim());
assert_eq!(got2, "two");
}
#[test]
#[cfg_attr(tarpaulin, ignore)]
fn benchmark_task_conditioned_compression() {
let content = generate_benchmark_code(200);
let full_tokens = count_tokens(&content);
let task = Some("fix authentication in validate_token");
let (_full_output, full_tok) = process_mode(
&content,
"full",
"F1",
"server.rs",
"rs",
full_tokens,
CrpMode::Off,
"server.rs",
task,
);
let (_task_output, task_tok) = process_mode(
&content,
"task",
"F1",
"server.rs",
"rs",
full_tokens,
CrpMode::Off,
"server.rs",
task,
);
let (_sig_output, sig_tok) = process_mode(
&content,
"signatures",
"F1",
"server.rs",
"rs",
full_tokens,
CrpMode::Off,
"server.rs",
task,
);
let (_ref_output, ref_tok) = process_mode(
&content,
"reference",
"F1",
"server.rs",
"rs",
full_tokens,
CrpMode::Off,
"server.rs",
task,
);
eprintln!("\n=== Task-Conditioned Compression Benchmark ===");
eprintln!("Source: 200-line Rust file, task='fix authentication in validate_token'");
eprintln!(" full: {full_tok:>6} tokens (baseline)");
eprintln!(
" task: {task_tok:>6} tokens ({:.0}% savings)",
(1.0 - task_tok as f64 / full_tok as f64) * 100.0
);
eprintln!(
" signatures: {sig_tok:>6} tokens ({:.0}% savings)",
(1.0 - sig_tok as f64 / full_tok as f64) * 100.0
);
eprintln!(
" reference: {ref_tok:>6} tokens ({:.0}% savings)",
(1.0 - ref_tok as f64 / full_tok as f64) * 100.0
);
eprintln!("================================================\n");
assert!(task_tok < full_tok, "task mode should save tokens");
assert!(sig_tok < full_tok, "signatures should save tokens");
assert!(ref_tok < sig_tok, "reference should be most compact");
}
fn generate_benchmark_code(lines: usize) -> String {
let mut code = Vec::with_capacity(lines);
code.push("use std::collections::HashMap;".to_string());
code.push("use crate::core::auth;".to_string());
code.push(String::new());
code.push("pub struct Server {".to_string());
code.push(" config: Config,".to_string());
code.push(" cache: HashMap<String, String>,".to_string());
code.push("}".to_string());
code.push(String::new());
code.push("impl Server {".to_string());
code.push(
" pub fn validate_token(&self, token: &str) -> Result<Claims, AuthError> {".to_string(),
);
code.push(" let decoded = auth::decode_jwt(token)?;".to_string());
code.push(" if decoded.exp < chrono::Utc::now().timestamp() {".to_string());
code.push(" return Err(AuthError::Expired);".to_string());
code.push(" }".to_string());
code.push(" Ok(decoded.claims)".to_string());
code.push(" }".to_string());
code.push(String::new());
let remaining = lines.saturating_sub(code.len());
for i in 0..remaining {
if i % 30 == 0 {
code.push(format!(
" pub fn handler_{i}(&self, req: Request) -> Response {{"
));
} else if i % 30 == 29 {
code.push(" }".to_string());
} else {
code.push(format!(" let val_{i} = self.cache.get(\"key_{i}\").unwrap_or(&\"default\".to_string());"));
}
}
code.push("}".to_string());
code.join("\n")
}
#[test]
fn map_mode_inlines_task_relevant_body() {
let content = "pub fn alpha() {\n let a = 1;\n}\n\npub fn validate_token(t: &str) -> bool {\n let ok = check(t);\n ok\n}\n";
let tokens = count_tokens(content);
let (with_task, _) = process_mode(
content,
"map",
"F1",
"test.rs",
"rs",
tokens,
CrpMode::Off,
"test.rs",
Some("fix bug in validate_token"),
);
assert!(
with_task.contains("▸ body") && with_task.contains("validate_token"),
"map with task should inline the matching body: {with_task}"
);
let (no_task, _) = process_mode(
content,
"map",
"F1",
"test.rs",
"rs",
tokens,
CrpMode::Off,
"test.rs",
None,
);
assert!(
!no_task.contains("▸ body"),
"map without a task must not inline a body: {no_task}"
);
}
#[test]
fn compressed_cache_key_distinguishes_task() {
let no_task = compressed_cache_key("map", CrpMode::Off, None);
let tdd_no_task = compressed_cache_key("map", CrpMode::Tdd, None);
let with_task = compressed_cache_key("map", CrpMode::Off, Some("fix login"));
let other_task = compressed_cache_key("map", CrpMode::Off, Some("refactor db"));
assert_eq!(no_task, "map:v2");
assert_eq!(tdd_no_task, "map:v2:tdd");
assert_ne!(with_task, no_task);
assert_ne!(with_task, other_task);
}
#[test]
fn map_mode_includes_signature_line_ranges() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("lib.rs");
let p = path.to_string_lossy().to_string();
std::fs::write(
&path,
"pub struct Config {}\n\npub fn build() -> Config { Config {} }\n",
)
.unwrap();
let mut cache = SessionCache::new();
let result = handle(&mut cache, &p, "map", CrpMode::Off);
assert!(
result.contains("API:"),
"map output should include API: {result}"
);
assert!(
result.contains("cl ⊛ Config @L1"),
"struct signature should include line suffix: {result}"
);
assert!(
result.contains("fn ⊛ build() → Config @L3"),
"function signature should include line suffix: {result}"
);
}
#[test]
fn instruction_file_detection() {
assert!(is_instruction_file(
"/home/user/.pi/agent/skills/committing-changes/SKILL.md"
));
assert!(is_instruction_file("/workspace/.cursor/rules/lean-ctx.mdc"));
assert!(is_instruction_file("/project/AGENTS.md"));
assert!(is_instruction_file("/project/.cursorrules"));
assert!(is_instruction_file("/home/user/.claude/rules/my-rule.md"));
assert!(is_instruction_file("/skills/some-skill/README.md"));
assert!(!is_instruction_file("/project/src/main.rs"));
assert!(!is_instruction_file("/project/config.json"));
assert!(!is_instruction_file("/project/data/report.csv"));
}
#[test]
fn resolve_auto_mode_returns_full_for_instruction_files() {
let mode = resolve_auto_mode(
"/home/user/.pi/agent/skills/committing-changes/SKILL.md",
5000,
Some("read"),
);
assert_eq!(mode, "full", "SKILL.md must always be read in full");
let mode = resolve_auto_mode("/workspace/AGENTS.md", 3000, Some("read"));
assert_eq!(mode, "full", "AGENTS.md must always be read in full");
let mode = resolve_auto_mode("/workspace/.cursorrules", 2000, None);
assert_eq!(mode, "full", ".cursorrules must always be read in full");
}