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
476 lines
16 KiB
Rust
476 lines
16 KiB
Rust
use std::process::Command;
|
|
|
|
fn lean_ctx_bin() -> Command {
|
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_lean-ctx"));
|
|
cmd.current_dir(env!("CARGO_MANIFEST_DIR"));
|
|
cmd.env("LEAN_CTX_ACTIVE", "1");
|
|
cmd.env("__LEAN_CTX_SKIP_EVENTS", "1");
|
|
// These tests exercise CLI/compression behavior, not the shell allowlist
|
|
// (which has its own unit tests in shell::exec). On CI stderr is not a TTY,
|
|
// so `allowlist_must_enforce()` would block test scripts (`for`/`while`
|
|
// loops are not allowlisted) with exit 126 — hermetic warn-only keeps the
|
|
// tests pinned to what they actually assert.
|
|
cmd.env("LEAN_CTX_ALLOWLIST_WARN_ONLY", "1");
|
|
cmd
|
|
}
|
|
|
|
#[test]
|
|
fn binary_prints_version() {
|
|
let output = lean_ctx_bin()
|
|
.arg("--version")
|
|
.output()
|
|
.expect("failed to run lean-ctx");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("lean-ctx"),
|
|
"version output should contain 'lean-ctx', got: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn binary_prints_help() {
|
|
let output = lean_ctx_bin()
|
|
.arg("--help")
|
|
.output()
|
|
.expect("failed to run lean-ctx");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("Context Runtime"),
|
|
"help should contain tagline"
|
|
);
|
|
assert!(stdout.contains("lean-ctx"), "help should mention lean-ctx");
|
|
}
|
|
|
|
#[test]
|
|
fn binary_read_file() {
|
|
let output = lean_ctx_bin()
|
|
.args(["read", "Cargo.toml", "-m", "signatures"])
|
|
.output()
|
|
.expect("failed to run lean-ctx");
|
|
assert!(output.status.success(), "read should succeed");
|
|
}
|
|
|
|
#[test]
|
|
fn binary_config_shows_defaults() {
|
|
let output = lean_ctx_bin()
|
|
.arg("config")
|
|
.output()
|
|
.expect("failed to run lean-ctx");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("checkpoint_interval"),
|
|
"config should show checkpoint_interval"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_hook_compresses_echo() {
|
|
let output = lean_ctx_bin()
|
|
.args(["-c", "echo", "hello", "world"])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("hello"),
|
|
"shell hook should pass through echo output"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn disabled_env_bypasses_compression() {
|
|
let output = Command::new(env!("CARGO_BIN_EXE_lean-ctx"))
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env("LEAN_CTX_DISABLED", "1")
|
|
.env("LEAN_CTX_COMPRESS", "1")
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
.args(["-c", "echo", "passthrough test"])
|
|
.output()
|
|
.expect("failed to run lean-ctx with LEAN_CTX_DISABLED");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("passthrough"),
|
|
"LEAN_CTX_DISABLED should pass output through unmodified"
|
|
);
|
|
assert!(
|
|
!stdout.contains("[lean-ctx:"),
|
|
"LEAN_CTX_DISABLED should not add compression markers"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn help_shows_environment_section() {
|
|
let output = lean_ctx_bin()
|
|
.arg("--help")
|
|
.output()
|
|
.expect("failed to run lean-ctx");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("LEAN_CTX_DISABLED"),
|
|
"help should document LEAN_CTX_DISABLED"
|
|
);
|
|
assert!(
|
|
stdout.contains("LEAN_CTX_RAW"),
|
|
"help should document LEAN_CTX_RAW"
|
|
);
|
|
}
|
|
|
|
// ── Pipe Guard Tests ────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn pipe_guard_no_compression_when_stdout_is_piped() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let output = Command::new(env!("CARGO_BIN_EXE_lean-ctx"))
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
.args(["-c", "echo hello world"])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c with piped stdout");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert_eq!(
|
|
stdout.trim(),
|
|
"hello world",
|
|
"piped stdout must pass through raw output, got: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_force_compress_overrides_pipe_guard() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let output = Command::new(env!("CARGO_BIN_EXE_lean-ctx"))
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env("LEAN_CTX_COMPRESS", "1")
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
.args(["-c", "echo hello world"])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c with LEAN_CTX_COMPRESS");
|
|
assert!(
|
|
output.status.success(),
|
|
"LEAN_CTX_COMPRESS should not crash even with piped stdout"
|
|
);
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("hello"),
|
|
"output should contain the echoed text"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_multiline_output_unchanged_when_piped() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let script = "echo line1; echo line2; echo line3; echo 'result: 42'";
|
|
let output = Command::new(env!("CARGO_BIN_EXE_lean-ctx"))
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
.args(["-c", script])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c with multiline output");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("line1"), "must contain line1");
|
|
assert!(stdout.contains("line2"), "must contain line2");
|
|
assert!(stdout.contains("line3"), "must contain line3");
|
|
assert!(
|
|
stdout.contains("result: 42"),
|
|
"must preserve exact output content"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_bash_hook_script_test() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let binary = env!("CARGO_BIN_EXE_lean-ctx");
|
|
let script = format!(
|
|
r#"
|
|
_lc() {{
|
|
if [ -n "${{LEAN_CTX_DISABLED:-}}" ] || [ ! -t 1 ]; then
|
|
command "$@"
|
|
return
|
|
fi
|
|
'{binary}' -c "$@"
|
|
}}
|
|
# Pipe test: _lc echo should bypass lean-ctx when piped
|
|
RESULT=$(_lc echo "pipe-guard-test-value")
|
|
echo "CAPTURED:$RESULT"
|
|
"#
|
|
);
|
|
let output = Command::new("bash")
|
|
.args(["-c", &script])
|
|
.output()
|
|
.expect("failed to run bash pipe guard test");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("CAPTURED:pipe-guard-test-value"),
|
|
"pipe guard must bypass lean-ctx in command substitution, got: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_bash_hook_pipe_to_sh() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let binary = env!("CARGO_BIN_EXE_lean-ctx");
|
|
let script = format!(
|
|
r#"
|
|
_lc() {{
|
|
if [ -n "${{LEAN_CTX_DISABLED:-}}" ] || [ ! -t 1 ]; then
|
|
command "$@"
|
|
return
|
|
fi
|
|
'{binary}' -c "$@"
|
|
}}
|
|
# Simulate curl | sh: echo a script, pipe to sh
|
|
_lc echo 'echo INSTALL_SUCCESS' | sh
|
|
"#
|
|
);
|
|
let output = Command::new("bash")
|
|
.args(["-c", &script])
|
|
.output()
|
|
.expect("failed to run bash pipe-to-sh test");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("INSTALL_SUCCESS"),
|
|
"piped script must execute correctly through sh, got: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_bash_hook_redirect_to_file() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let binary = env!("CARGO_BIN_EXE_lean-ctx");
|
|
let tmp = std::env::temp_dir().join("lean-ctx-pipe-guard-test.txt");
|
|
let tmp_path = tmp.to_str().unwrap();
|
|
let script = format!(
|
|
r#"
|
|
_lc() {{
|
|
if [ -n "${{LEAN_CTX_DISABLED:-}}" ] || [ ! -t 1 ]; then
|
|
command "$@"
|
|
return
|
|
fi
|
|
'{binary}' -c "$@"
|
|
}}
|
|
_lc echo "redirect-test-value" > {tmp_path}
|
|
cat {tmp_path}
|
|
rm -f {tmp_path}
|
|
"#
|
|
);
|
|
let output = Command::new("bash")
|
|
.args(["-c", &script])
|
|
.output()
|
|
.expect("failed to run bash redirect test");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("redirect-test-value"),
|
|
"redirected output must be raw, got: {stdout}"
|
|
);
|
|
}
|
|
|
|
// ── Redirect-to-file fidelity under FORCED compression ──────
|
|
// Regression: the agent shell hook deliberately bypasses the `[ ! -t 1 ]` pipe
|
|
// guard (an agent IS the consumer), so an agent running
|
|
// `git status --short > files.txt` reached `lean-ctx -c` with stdout pointing at a
|
|
// regular FILE and forced compression on. The compressed digest was written INTO
|
|
// the file, dropping/abbreviating lines and producing contradictory diffs. Output
|
|
// redirected to a regular file must ALWAYS stay byte-faithful, even when
|
|
// compression is forced; pipes (agent capture) must keep compressing.
|
|
|
|
// >30 distinct lines so the compression engine definitely rewrites the output
|
|
// (the truncate-with-safety-scan fallback collapses the middle). This makes the
|
|
// assertions below meaningful — pre-fix, the file held the compressed digest.
|
|
// The payload must NOT contain any passthrough/verbatim keyword (notably the
|
|
// literal "lean-ctx", which classifies the whole command as Passthrough), or the
|
|
// output would never be compressed and the test would be vacuous.
|
|
fn redirect_fidelity_command() -> &'static str {
|
|
"i=0; while [ $i -lt 60 ]; do echo \"row $i alpha beta gamma delta payload\"; i=$((i+1)); done"
|
|
}
|
|
|
|
/// `lean-ctx -c` with a *hermetic* control environment for the compression
|
|
/// fidelity tests. A developer's shell hook exports `LEAN_CTX_RAW` /
|
|
/// `LEAN_CTX_DISABLED` (and CI may set `LEAN_CTX_ACTIVE`) — each forces raw
|
|
/// passthrough, which would make these tests assert nothing (the redirect test
|
|
/// would pass vacuously). Stripping them pins the exact forced-compress path.
|
|
/// `LEAN_CTX_WRAPPED` must go too: running `cargo test` *through* the lean-ctx
|
|
/// wrapper stamps it on every child, and the nested `lean-ctx -c` under test
|
|
/// would then (correctly) defer to the parent instead of compressing.
|
|
fn forced_compress_bin() -> Command {
|
|
let mut cmd = Command::new(env!("CARGO_BIN_EXE_lean-ctx"));
|
|
cmd.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env_remove("LEAN_CTX_RAW")
|
|
.env_remove("LEAN_CTX_DISABLED")
|
|
.env_remove("LEAN_CTX_ACTIVE")
|
|
.env_remove("LEAN_CTX_WRAPPED")
|
|
.env("LEAN_CTX_COMPRESS", "1")
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
// Allowlist enforcement (non-TTY stderr ⇒ block) is out of scope here;
|
|
// the fidelity scripts use `while` loops that are not allowlisted.
|
|
.env("LEAN_CTX_ALLOWLIST_WARN_ONLY", "1");
|
|
cmd
|
|
}
|
|
|
|
#[test]
|
|
fn force_compress_redirect_to_file_is_byte_faithful() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let cmd = redirect_fidelity_command();
|
|
|
|
// Ground truth: the raw command output (no lean-ctx in the loop).
|
|
let raw = Command::new("sh")
|
|
.args(["-c", cmd])
|
|
.output()
|
|
.expect("failed to run raw command");
|
|
let raw_stdout = String::from_utf8_lossy(&raw.stdout).to_string();
|
|
|
|
// Run lean-ctx with forced compression, stdout redirected to a real file —
|
|
// exactly the agent `cmd > file` scenario.
|
|
let tmp = std::env::temp_dir().join(format!(
|
|
"lean-ctx-redirect-fidelity-{}.txt",
|
|
std::process::id()
|
|
));
|
|
let file = std::fs::File::create(&tmp).expect("create temp file");
|
|
let status = forced_compress_bin()
|
|
.args(["-c", cmd])
|
|
.stdout(std::process::Stdio::from(file))
|
|
.status()
|
|
.expect("failed to run lean-ctx -c with file redirect");
|
|
assert!(status.success(), "lean-ctx -c should exit cleanly");
|
|
|
|
let file_content = std::fs::read_to_string(&tmp).expect("read temp file");
|
|
let _ = std::fs::remove_file(&tmp);
|
|
|
|
assert_eq!(
|
|
file_content, raw_stdout,
|
|
"redirect to a regular file under LEAN_CTX_COMPRESS must be byte-faithful (never compressed)"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn force_compress_pipe_still_compresses() {
|
|
// Companion to the redirect test: proves compression is genuinely active for
|
|
// the SAME command + env when stdout is a PIPE (an agent's capture). If this
|
|
// ever stops compressing, the redirect test above would be vacuous.
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let cmd = redirect_fidelity_command();
|
|
|
|
let raw = Command::new("sh")
|
|
.args(["-c", cmd])
|
|
.output()
|
|
.expect("failed to run raw command");
|
|
let raw_stdout = String::from_utf8_lossy(&raw.stdout).to_string();
|
|
|
|
let piped = forced_compress_bin()
|
|
.args(["-c", cmd])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c with piped stdout");
|
|
let piped_stdout = String::from_utf8_lossy(&piped.stdout).to_string();
|
|
|
|
assert_ne!(
|
|
piped_stdout, raw_stdout,
|
|
"piped stdout under LEAN_CTX_COMPRESS should be compressed (changed) — proves the redirect guard is meaningful"
|
|
);
|
|
}
|
|
|
|
// ── Extended CLI Smoke Tests ────────────────────────────────
|
|
|
|
#[test]
|
|
fn binary_gain_json_returns_valid_json() {
|
|
let output = lean_ctx_bin()
|
|
.args(["gain", "--json"])
|
|
.output()
|
|
.expect("failed to run lean-ctx gain --json");
|
|
assert!(output.status.success(), "gain --json should succeed");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let parsed: Result<serde_json::Value, _> = serde_json::from_str(stdout.trim());
|
|
assert!(
|
|
parsed.is_ok(),
|
|
"gain --json should return valid JSON, got: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn binary_grep_finds_matches() {
|
|
let output = lean_ctx_bin()
|
|
.args(["grep", "fn main", "src/"])
|
|
.output()
|
|
.expect("failed to run lean-ctx grep");
|
|
assert!(
|
|
output.status.success(),
|
|
"grep should succeed (exit={})",
|
|
output.status.code().unwrap_or(-1)
|
|
);
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("main") || stdout.contains("fn"),
|
|
"grep should find matches: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn binary_ls_shows_tree() {
|
|
let output = lean_ctx_bin()
|
|
.args(["ls", "--depth", "2"])
|
|
.output()
|
|
.expect("failed to run lean-ctx ls");
|
|
assert!(
|
|
output.status.success(),
|
|
"ls should succeed (exit={})",
|
|
output.status.code().unwrap_or(-1)
|
|
);
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("src") || stdout.contains("Cargo"),
|
|
"ls should show directory tree: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn binary_doctor_runs_without_crash() {
|
|
let output = lean_ctx_bin()
|
|
.arg("doctor")
|
|
.output()
|
|
.expect("failed to run lean-ctx doctor");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("lean-ctx")
|
|
|| stdout.contains("doctor")
|
|
|| stdout.contains("✓")
|
|
|| stdout.contains("check"),
|
|
"doctor should produce diagnostic output: {stdout}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pipe_guard_rust_side_defense_in_depth() {
|
|
if cfg!(windows) {
|
|
return;
|
|
}
|
|
let script = "for i in 1 2 3 4 5; do echo \"item_$i: $(date +%s)\"; done";
|
|
let output = Command::new(env!("CARGO_BIN_EXE_lean-ctx"))
|
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
|
.env("__LEAN_CTX_SKIP_EVENTS", "1")
|
|
// `for` + command substitution are not allowlisted; this test pins the
|
|
// pipe guard, not enforcement (covered by shell::exec unit tests).
|
|
.env("LEAN_CTX_ALLOWLIST_WARN_ONLY", "1")
|
|
.args(["-c", script])
|
|
.output()
|
|
.expect("failed to run lean-ctx -c");
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
for i in 1..=5 {
|
|
assert!(
|
|
stdout.contains(&format!("item_{i}:")),
|
|
"Rust-side pipe guard must pass through all lines unchanged (missing item_{i})"
|
|
);
|
|
}
|
|
}
|