Files
wehub-resource-sync 26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

278 lines
6.8 KiB
Rust

use lean_ctx::core::cache::SessionCache;
use lean_ctx::core::config::AutonomyConfig;
use lean_ctx::tools::CrpMode;
use lean_ctx::tools::autonomy::{
AutonomyState, enrich_after_read, maybe_auto_dedup, session_lifecycle_pre_hook,
shell_efficiency_hint,
};
use std::sync::OnceLock;
use std::sync::atomic::Ordering;
fn init_test_data_dir() {
static DIR: OnceLock<tempfile::TempDir> = OnceLock::new();
let dir = DIR.get_or_init(|| tempfile::tempdir().expect("tempdir"));
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", dir.path()) };
}
fn make_state() -> AutonomyState {
init_test_data_dir();
AutonomyState::new()
}
fn make_disabled_state() -> AutonomyState {
init_test_data_dir();
let mut state = AutonomyState::new();
state.config.enabled = false;
state
}
#[test]
fn session_lifecycle_fires_once() {
let state = make_state();
let mut cache = SessionCache::new();
let _first = session_lifecycle_pre_hook(
&state,
"ctx_read",
&mut cache,
Some("fix auth bug"),
Some("/tmp/test-project"),
CrpMode::Tdd,
);
let second = session_lifecycle_pre_hook(
&state,
"ctx_read",
&mut cache,
Some("fix auth bug"),
Some("/tmp/test-project"),
CrpMode::Tdd,
);
assert!(
state.session_initialized.load(Ordering::SeqCst),
"flag must be set after first call"
);
assert!(second.is_none(), "second call must return None");
}
#[test]
fn session_lifecycle_skips_without_project_root() {
let state = make_state();
let mut cache = SessionCache::new();
let result = session_lifecycle_pre_hook(
&state,
"ctx_read",
&mut cache,
Some("fix auth bug"),
None,
CrpMode::Tdd,
);
assert!(result.is_none(), "must skip when project_root is None");
assert!(
!state.session_initialized.load(Ordering::SeqCst),
"flag must not be set without project_root"
);
}
#[test]
fn session_lifecycle_skips_overview_tool() {
let state = make_state();
let mut cache = SessionCache::new();
let result = session_lifecycle_pre_hook(
&state,
"ctx_overview",
&mut cache,
Some("task"),
None,
CrpMode::Tdd,
);
assert!(result.is_none(), "must skip when tool is ctx_overview");
assert!(
!state.session_initialized.load(Ordering::SeqCst),
"flag must NOT be set when skipped"
);
}
#[test]
fn session_lifecycle_skips_preload_tool() {
let state = make_state();
let mut cache = SessionCache::new();
let result = session_lifecycle_pre_hook(
&state,
"ctx_preload",
&mut cache,
Some("task"),
None,
CrpMode::Tdd,
);
assert!(result.is_none(), "must skip when tool is ctx_preload");
assert!(
!state.session_initialized.load(Ordering::SeqCst),
"flag must NOT be set when skipped"
);
}
#[test]
fn session_lifecycle_disabled_returns_none() {
let state = make_disabled_state();
let mut cache = SessionCache::new();
let result = session_lifecycle_pre_hook(
&state,
"ctx_read",
&mut cache,
Some("task"),
None,
CrpMode::Tdd,
);
assert!(result.is_none(), "disabled state must return None");
assert!(
!state.session_initialized.load(Ordering::SeqCst),
"flag must NOT be set when disabled"
);
}
#[test]
fn auto_dedup_fires_at_threshold() {
let state = make_state();
let mut cache = SessionCache::new();
for i in 0..8 {
let path = format!("test_file_{i}.rs");
let content = format!("fn func_{i}() {{ println!(\"hello {i}\"); }}");
cache.store(&path, &content);
}
maybe_auto_dedup(&state, &mut cache, "ctx_read");
assert!(
state.dedup_applied.load(Ordering::SeqCst),
"dedup must be applied at threshold"
);
}
#[test]
fn auto_dedup_skips_below_threshold() {
let state = make_state();
let mut cache = SessionCache::new();
for i in 0..3 {
let path = format!("test_file_{i}.rs");
cache.store(&path, &format!("content {i}"));
}
maybe_auto_dedup(&state, &mut cache, "ctx_read");
assert!(
!state.dedup_applied.load(Ordering::SeqCst),
"dedup must NOT be applied below threshold"
);
}
#[test]
fn auto_dedup_disabled() {
let state = make_disabled_state();
let mut cache = SessionCache::new();
for i in 0..10 {
cache.store(&format!("f{i}.rs"), &format!("c{i}"));
}
maybe_auto_dedup(&state, &mut cache, "ctx_read");
assert!(!state.dedup_applied.load(Ordering::SeqCst));
}
#[test]
fn shell_hint_grep_low_savings() {
let state = make_state();
let hint = shell_efficiency_hint(&state, "grep -rn pattern src/", 200, 190);
assert!(hint.is_some());
assert!(hint.unwrap().contains("ctx_search"));
}
#[test]
fn shell_hint_cat_low_savings() {
let state = make_state();
let hint = shell_efficiency_hint(&state, "cat src/main.rs", 500, 490);
assert!(hint.is_some());
assert!(hint.unwrap().contains("ctx_read"));
}
#[test]
fn shell_hint_none_for_good_savings() {
let state = make_state();
let hint = shell_efficiency_hint(&state, "grep -rn foo .", 1000, 200);
assert!(hint.is_none());
}
#[test]
fn shell_hint_none_for_non_search_command() {
let state = make_state();
let hint = shell_efficiency_hint(&state, "cargo build --release", 100, 95);
assert!(hint.is_none());
}
#[test]
fn shell_hint_disabled() {
let state = make_disabled_state();
let hint = shell_efficiency_hint(&state, "grep foo bar", 100, 95);
assert!(hint.is_none());
}
#[test]
fn config_defaults_all_enabled() {
let cfg = AutonomyConfig::default();
assert!(cfg.enabled);
assert!(cfg.auto_preload);
assert!(cfg.auto_dedup);
assert!(cfg.auto_related);
assert!(cfg.silent_preload);
assert_eq!(cfg.dedup_threshold, 8);
}
#[test]
fn enrich_after_read_disabled() {
let state = make_disabled_state();
let mut cache = SessionCache::new();
cache.store("test.rs", "fn main() {}");
let result = enrich_after_read(
&state,
&mut cache,
"test.rs",
None,
None,
CrpMode::Tdd,
false,
);
assert!(result.related_hint.is_none());
}
#[test]
fn enrich_after_read_no_index() {
let state = make_state();
let mut cache = SessionCache::new();
cache.store("test.rs", "fn main() {}");
let result = enrich_after_read(
&state,
&mut cache,
"test.rs",
Some("/nonexistent/path"),
None,
CrpMode::Tdd,
false,
);
assert!(
result.related_hint.is_none(),
"must return None when no project index exists"
);
}