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
121 lines
3.4 KiB
Rust
121 lines
3.4 KiB
Rust
//! Acceptance tests for the embedding [`Engine`] — exercises the real tool
|
|
//! dispatch path against a temp project, including the read → re-read delta
|
|
//! that motivates the SDK.
|
|
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use lean_ctx_sdk::{Engine, ReadMode};
|
|
|
|
/// Create a unique temp project dir with a couple of source files.
|
|
fn temp_project() -> PathBuf {
|
|
let unique = format!(
|
|
"lean-ctx-sdk-it-{}-{}",
|
|
std::process::id(),
|
|
std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_nanos()
|
|
);
|
|
let dir = std::env::temp_dir().join(unique);
|
|
fs::create_dir_all(dir.join("src")).unwrap();
|
|
fs::write(
|
|
dir.join("src/main.rs"),
|
|
"fn main() {\n println!(\"hello\");\n}\n\npub fn helper(x: i32) -> i32 {\n x + 1\n}\n",
|
|
)
|
|
.unwrap();
|
|
fs::write(
|
|
dir.join("src/lib.rs"),
|
|
"pub fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n",
|
|
)
|
|
.unwrap();
|
|
dir
|
|
}
|
|
|
|
#[test]
|
|
fn read_then_reread_is_cheaper() {
|
|
let dir = temp_project();
|
|
let engine = Engine::builder(&dir).build().expect("engine builds");
|
|
|
|
let first = engine
|
|
.read("src/main.rs", ReadMode::Full)
|
|
.expect("first read");
|
|
assert!(!first.text.is_empty(), "first read returns content");
|
|
|
|
let again = engine.read("src/main.rs", ReadMode::Full).expect("re-read");
|
|
// The shared cache makes the second read collapse to a delta/stub: it must
|
|
// never cost MORE than the first, and typically saves more tokens.
|
|
assert!(
|
|
again.saved_tokens >= first.saved_tokens,
|
|
"re-read should save at least as many tokens (first={}, again={})",
|
|
first.saved_tokens,
|
|
again.saved_tokens
|
|
);
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn pathjail_rejects_escape() {
|
|
let dir = temp_project();
|
|
let engine = Engine::builder(&dir).build().expect("engine builds");
|
|
|
|
let err = engine
|
|
.read("../../../etc/passwd", ReadMode::Full)
|
|
.expect_err("escape must be rejected");
|
|
assert!(
|
|
matches!(err, lean_ctx_sdk::Error::Path(_)),
|
|
"expected Path error, got {err:?}"
|
|
);
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn search_finds_symbol() {
|
|
let dir = temp_project();
|
|
let engine = Engine::builder(&dir).build().expect("engine builds");
|
|
|
|
let hits = engine.search("helper", None).expect("search runs");
|
|
assert!(
|
|
hits.contains("helper") || hits.contains("main.rs"),
|
|
"search should locate the helper fn, got: {hits}"
|
|
);
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn exec_tool_requires_optin() {
|
|
let dir = temp_project();
|
|
let engine = Engine::builder(&dir).build().expect("engine builds");
|
|
|
|
let mut args = serde_json::Map::new();
|
|
args.insert(
|
|
"command".into(),
|
|
serde_json::Value::String("echo hi".into()),
|
|
);
|
|
let err = engine
|
|
.call("ctx_shell", args)
|
|
.expect_err("shell must be gated");
|
|
assert!(
|
|
matches!(err, lean_ctx_sdk::Error::NotPermitted(_)),
|
|
"expected NotPermitted, got {err:?}"
|
|
);
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_tool_errors() {
|
|
let dir = temp_project();
|
|
let engine = Engine::builder(&dir).build().expect("engine builds");
|
|
|
|
let err = engine
|
|
.call("ctx_nonexistent", serde_json::Map::new())
|
|
.expect_err("unknown tool");
|
|
assert!(matches!(err, lean_ctx_sdk::Error::UnknownTool(_)));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|