Files
yvgude--lean-ctx/rust/tests/config_path_parity_594.rs
T
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

127 lines
4.6 KiB
Rust

//! End-to-end regression for GitHub #594:
//! "LeanCTX config path is different for CLI and MCP".
//!
//! Older lean-ctx versions baked `LEAN_CTX_DATA_DIR` into the editor's MCP `env`
//! block. That collapsed config/state/cache onto the data dir *for the MCP
//! server only*, so the editor-spawned server read `config.toml` from
//! `~/.local/share/lean-ctx` while the terminal CLI read it from
//! `~/.config/lean-ctx` — the two silently diverged.
//!
//! The fix decoupled the *standard* XDG-data pin from config resolution. These
//! tests pin that contract end-to-end by running the real binary twice with a
//! controlled environment (terminal vs. simulated editor-MCP) and asserting the
//! resolved `config.toml` path is identical. `lean-ctx config path` is the
//! deterministic hook (no env mutation inside the test process, so it is immune
//! to parallel-test env races).
//!
//! Unix-only: `$HOME`/`$XDG_*` overrides + `dirs::home_dir()` honoring `HOME`.
#![cfg(unix)]
use std::path::Path;
use std::process::Command;
struct Sandbox {
_tmp: tempfile::TempDir,
home: std::path::PathBuf,
xdg_config: std::path::PathBuf,
xdg_data: std::path::PathBuf,
}
fn sandbox() -> Sandbox {
let tmp = tempfile::tempdir().unwrap();
let home = tmp.path().join("home");
let xdg_config = home.join(".config");
let xdg_data = home.join(".local/share");
for d in [&home, &xdg_config, &xdg_data] {
std::fs::create_dir_all(d).unwrap();
}
Sandbox {
_tmp: tmp,
home,
xdg_config,
xdg_data,
}
}
/// Run `lean-ctx config path` in a fully controlled environment and return the
/// trimmed stdout (the resolved absolute `config.toml` path).
///
/// `data_dir` simulates the `LEAN_CTX_DATA_DIR` an editor may have baked into the
/// MCP entry (`None` = a plain terminal). `cwd` simulates where the process is
/// launched (a terminal in `$HOME` vs. an editor-spawned MCP server at `/`). The
/// layout-relevant `LEAN_CTX_*` vars are removed first so host env never leaks
/// into the assertion.
fn config_path(sb: &Sandbox, data_dir: Option<&Path>, cwd: &Path) -> String {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_lean-ctx"));
cmd.args(["config", "path"])
.env("HOME", &sb.home)
.env("XDG_CONFIG_HOME", &sb.xdg_config)
.env("XDG_DATA_HOME", &sb.xdg_data)
.env("LEAN_CTX_DISABLED", "1")
.env("LEAN_CTX_ACTIVE", "1")
.env_remove("LEAN_CTX_CONFIG_DIR")
.env_remove("LEAN_CTX_DATA_DIR")
.env_remove("LEAN_CTX_STATE_DIR")
.env_remove("LEAN_CTX_CACHE_DIR")
.current_dir(cwd);
if let Some(d) = data_dir {
cmd.env("LEAN_CTX_DATA_DIR", d);
}
let out = cmd.output().expect("spawn lean-ctx config path");
assert!(
out.status.success(),
"`lean-ctx config path` failed; stderr:\n{}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
/// The reported bug: the terminal CLI and the editor-spawned MCP server must
/// resolve the SAME `config.toml`, even when the MCP env carries the standard
/// `LEAN_CTX_DATA_DIR` pin and the server is launched at `/`.
#[test]
fn cli_and_mcp_resolve_same_config_path_with_standard_data_pin() {
let sb = sandbox();
let cli = config_path(&sb, None, &sb.home);
let standard_data = sb.xdg_data.join("lean-ctx");
let mcp = config_path(&sb, Some(standard_data.as_path()), Path::new("/"));
let expected = sb.xdg_config.join("lean-ctx").join("config.toml");
assert_eq!(
cli,
expected.to_string_lossy(),
"terminal CLI must resolve the XDG config dir"
);
assert_eq!(
mcp, cli,
"GH #594: a standard LEAN_CTX_DATA_DIR pin must NOT make the MCP server \
diverge from the CLI"
);
}
/// Back-compat contract: a *custom* (non-standard) single-dir pin intentionally
/// still collapses config onto it, because legacy single-dir installs depend on
/// it. Only the standard XDG-data pin is decoupled by the #594 fix. Guarding
/// this proves the fix is surgical, not a blanket "ignore LEAN_CTX_DATA_DIR".
#[test]
fn custom_data_dir_still_collapses_for_backcompat() {
let sb = sandbox();
let cli = config_path(&sb, None, &sb.home);
let custom = sb.home.join(".lean-ctx-custom");
let collapsed = config_path(&sb, Some(custom.as_path()), Path::new("/"));
assert_ne!(
collapsed, cli,
"a custom single-dir pin must intentionally collapse config (back-compat)"
);
assert_eq!(
collapsed,
custom.join("config.toml").to_string_lossy(),
"a custom pin must resolve config under the custom dir"
);
}