Files
wehub-resource-sync 26382a7ac6
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
JetBrains Plugin / Actionlint (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
JetBrains Plugin / Validation (push) Waiting to run
JetBrains Plugin / Build (push) Waiting to run
JetBrains Plugin / Test (push) Blocked by required conditions
Security Check / Security Scan (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

79 lines
2.8 KiB
Rust

use serde_json::json;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn context_engine_call_tool_text_reads_file() {
let dir = tempfile::tempdir().expect("tempdir");
let file_path = dir.path().join("a.txt");
std::fs::write(&file_path, "hello-engine\n").expect("write file");
let engine = lean_ctx::engine::ContextEngine::with_project_root(dir.path());
let out = engine
.call_tool_text(
"ctx_read",
Some(json!({
"path": file_path.to_string_lossy().to_string(),
"mode": "full"
})),
)
.await
.expect("call tool");
assert!(out.contains("hello-engine"));
}
/// #271 regression: many concurrent reads must all return through the
/// `spawn_blocking` dispatch path without starving the core workers or hanging.
/// Before the dispatch watchdog, a single hung handler could swallow its
/// response (client crash: "Cannot read properties of undefined (reading
/// 'invoke')"); this exercises the path under real concurrency.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn context_engine_handles_concurrent_reads_without_hang() {
let dir = tempfile::tempdir().expect("tempdir");
// Distinct files/contents per call so the session's auto-dedup (which
// returns a compact stub for a *re-read* of the same file) does not mask the
// body — here we assert each concurrent read returns real content.
const CALLS: usize = 16;
for i in 0..CALLS {
std::fs::write(
dir.path().join(format!("f{i}.txt")),
format!("content-{i}-unique\n"),
)
.expect("write file");
}
let engine = std::sync::Arc::new(lean_ctx::engine::ContextEngine::with_project_root(
dir.path(),
));
let mut handles = Vec::with_capacity(CALLS);
for i in 0..CALLS {
let engine = engine.clone();
let path = dir
.path()
.join(format!("f{i}.txt"))
.to_string_lossy()
.to_string();
handles.push(tokio::spawn(async move {
engine
.call_tool_text("ctx_read", Some(json!({ "path": path, "mode": "full" })))
.await
}));
}
// A generous bound: the reads are trivial, so completion well under this
// means no hang/starvation. A regression (dropped response) would time out.
let texts = tokio::time::timeout(std::time::Duration::from_secs(30), async {
let mut out = Vec::with_capacity(CALLS);
for h in handles {
out.push(h.await.expect("task joined").expect("tool call ok"));
}
out
})
.await
.expect("concurrent reads must not hang");
assert_eq!(texts.len(), CALLS);
for text in texts {
assert!(text.contains("content-"), "each read returns its file body");
}
}