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
73 lines
2.7 KiB
Rust
73 lines
2.7 KiB
Rust
//! End-to-end proof that a plugin observes a real `ctx_read` event.
|
|
//!
|
|
//! This is its own integration-test binary, so the process-global plugin
|
|
//! registry (`OnceLock`) is fresh and uncontended: we point it at an isolated
|
|
//! plugins root via `LEAN_CTX_PLUGINS_DIR`, perform a genuine read through the
|
|
//! public `ctx_read::handle` entry, and assert the plugin's `pre_read` hook
|
|
//! actually ran (it writes a sentinel file). Unix-only: the hook is a `sh`
|
|
//! script.
|
|
|
|
#![cfg(unix)]
|
|
|
|
use std::fs;
|
|
use std::time::{Duration, Instant};
|
|
|
|
use lean_ctx::core::cache::SessionCache;
|
|
use lean_ctx::core::plugins::PluginManager;
|
|
use lean_ctx::core::protocol::CrpMode;
|
|
use lean_ctx::tools::ctx_read;
|
|
|
|
#[test]
|
|
fn plugin_observes_real_read_event() {
|
|
let root = tempfile::tempdir().expect("tempdir");
|
|
let plugin_dir = root.path().join("sentinel-plugin");
|
|
fs::create_dir_all(&plugin_dir).expect("plugin dir");
|
|
|
|
// The hook writes a sentinel into the plugin's own directory; the executor
|
|
// exports LEAN_CTX_PLUGIN_DIR (the single plugin's path) for the child.
|
|
let script = plugin_dir.join("hook.sh");
|
|
fs::write(
|
|
&script,
|
|
"#!/bin/sh\nprintf fired > \"$LEAN_CTX_PLUGIN_DIR/fired\"\n",
|
|
)
|
|
.expect("script");
|
|
|
|
fs::write(
|
|
plugin_dir.join("plugin.toml"),
|
|
format!(
|
|
"[plugin]\nname = \"sentinel-plugin\"\nversion = \"1.0.0\"\n\n\
|
|
[hooks.pre_read]\ncommand = \"sh {}\"\ntimeout_ms = 5000\n",
|
|
script.display()
|
|
),
|
|
)
|
|
.expect("manifest");
|
|
|
|
// Point the global registry at our isolated root, then activate it.
|
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
|
unsafe { std::env::set_var("LEAN_CTX_PLUGINS_DIR", root.path()) };
|
|
PluginManager::init();
|
|
assert!(
|
|
PluginManager::has_listener("pre_read"),
|
|
"the installed plugin should register a pre_read listener"
|
|
);
|
|
|
|
// Perform a genuine read through the public read entry point.
|
|
let target = root.path().join("target.rs");
|
|
fs::write(&target, "fn main() { println!(\"hi\"); }\n").expect("target");
|
|
let mut cache = SessionCache::new();
|
|
let _ = ctx_read::handle(&mut cache, target.to_str().unwrap(), "full", CrpMode::Off);
|
|
|
|
// Hooks fire in the background; poll for the sentinel the hook writes.
|
|
let sentinel = plugin_dir.join("fired");
|
|
let deadline = Instant::now() + Duration::from_secs(10);
|
|
while Instant::now() < deadline && !sentinel.exists() {
|
|
std::thread::sleep(Duration::from_millis(50));
|
|
}
|
|
|
|
assert!(
|
|
sentinel.exists(),
|
|
"pre_read hook should have executed and written the sentinel"
|
|
);
|
|
assert_eq!(fs::read_to_string(&sentinel).expect("sentinel"), "fired");
|
|
}
|