use std::sync::Mutex; use lean_ctx::core::cache::SessionCache; use lean_ctx::tools::CrpMode; use lean_ctx::tools::ctx_multi_read; /// `LCTX_MAX_MULTI_READ_BYTES` is process-global, so tests that mutate it must /// not run concurrently or they clobber each other's value. Serialize them. static ENV_GUARD: Mutex<()> = Mutex::new(()); fn setup_test_files(count: usize, size_per_file: usize) -> (tempfile::TempDir, Vec) { let dir = tempfile::tempdir().unwrap(); let mut paths = Vec::new(); for i in 0..count { let path = dir.path().join(format!("file_{i}.txt")); let content = format!("// File {i}\n{}", "x".repeat(size_per_file)); std::fs::write(&path, &content).unwrap(); paths.push(path.to_string_lossy().to_string()); } (dir, paths) } #[test] fn multi_read_respects_output_cap() { let _guard = ENV_GUARD .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::set_var("LCTX_MAX_MULTI_READ_BYTES", "10000") }; let (_dir, paths) = setup_test_files(20, 5000); let mut cache = SessionCache::new(); let output = ctx_multi_read::handle(&mut cache, &paths, "full", CrpMode::Off); assert!( output.contains("Output capped"), "output must contain cap warning when exceeding limit:\n{}", &output[output.len().saturating_sub(300)..] ); assert!( output.contains("file(s) skipped"), "must report skipped files" ); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::remove_var("LCTX_MAX_MULTI_READ_BYTES") }; } #[test] fn multi_read_no_cap_when_under_limit() { let _guard = ENV_GUARD .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::set_var("LCTX_MAX_MULTI_READ_BYTES", "1000000") }; let (_dir, paths) = setup_test_files(3, 100); let mut cache = SessionCache::new(); let output = ctx_multi_read::handle(&mut cache, &paths, "full", CrpMode::Off); assert!( !output.contains("Output capped"), "should not cap when under limit" ); assert!(output.contains("Read 3 files"), "should read all files"); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::remove_var("LCTX_MAX_MULTI_READ_BYTES") }; } #[test] fn multi_read_empty_paths() { let _guard = ENV_GUARD .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); let mut cache = SessionCache::new(); let output = ctx_multi_read::handle(&mut cache, &[], "full", CrpMode::Off); assert!(output.contains("Read 0 files")); } #[test] fn multi_read_single_large_file_passes() { let _guard = ENV_GUARD .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::set_var("LCTX_MAX_MULTI_READ_BYTES", "100000") }; let (_dir, paths) = setup_test_files(1, 50000); let mut cache = SessionCache::new(); let output = ctx_multi_read::handle(&mut cache, &paths, "full", CrpMode::Off); assert!( output.contains("Read 1 files"), "single file should always be included even if large" ); assert!( !output.contains("Output capped"), "single file should not trigger cap" ); // TODO: Audit that the environment access only happens in single-threaded code. unsafe { std::env::remove_var("LCTX_MAX_MULTI_READ_BYTES") }; }