use super::native_mcp::{
NATIVE_TRUSTY_MCP_SERVERS, ensure_env_local_git_excluded, inject_native_trusty_mcps_from,
is_env_local_actually_ignored, merge_env_file, split_public_and_secret_env,
};
use super::settings::preseed_workspace_trust;
use serde_json::{Value, json};
use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use tempfile::tempdir;
const FAKE_SLACK_TOKEN: &str = "xoxb-verbatim-secret-DO-NOT-COMMIT";
fn git_init(dir: &Path) {
let status = std::process::Command::new("git")
.arg("init")
.arg("-q")
.arg(dir)
.status()
.expect("git must be on PATH to run this test");
assert!(status.success(), "git init failed");
}
fn seed_managed_registry(config_dir: &std::path::Path) {
std::fs::create_dir_all(config_dir).unwrap();
let registry = json!({
"oauthAccount": { "keep": "me" },
"mcpServers": {
"slack-mcp": {
"type": "stdio",
"command": "slack-mcp",
"args": ["serve"],
"env": { "SLACK_BOT_TOKEN": FAKE_SLACK_TOKEN }
},
"gworkspace-mcp": {
"type": "stdio",
"command": "gworkspace-mcp",
"args": []
},
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
},
"duetto-endpoint": {
"type": "http",
"url": "https://duetto.example/mcp"
},
"trusty-memory": {
"type": "stdio",
"command": "trusty-memory",
"args": ["serve", "--stdio"]
}
}
});
std::fs::write(
config_dir.join(".claude.json"),
serde_json::to_string_pretty(®istry).unwrap(),
)
.unwrap();
}
fn read_injected(workspace: &std::path::Path) -> serde_json::Map<String, Value> {
let text = std::fs::read_to_string(workspace.join(".mcp.json")).unwrap();
let value: Value = serde_json::from_str(&text).unwrap();
value["mcpServers"].as_object().cloned().unwrap()
}
#[test]
fn inject_native_only_injects_allowlisted() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let servers = read_injected(ws.path());
assert!(servers.contains_key("slack-mcp"), "native slack injected");
assert!(
servers.contains_key("gworkspace-mcp"),
"native gworkspace injected"
);
assert!(!servers.contains_key("trusty-analyze"));
assert!(!servers.contains_key("telegram-mcp"));
}
#[test]
fn inject_native_skips_non_native() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let servers = read_injected(ws.path());
assert!(
!servers.contains_key("github"),
"non-native github excluded"
);
assert!(
!servers.contains_key("duetto-endpoint"),
"non-native HTTP endpoint excluded"
);
assert!(
!servers.contains_key("trusty-memory"),
"trusty-memory owned by its dedicated injector, not this one"
);
assert_eq!(servers.len(), 2, "only the two present natives injected");
}
#[test]
fn inject_native_strips_secret_env_from_mcp_json() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let raw = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
assert!(
!raw.contains(FAKE_SLACK_TOKEN),
".mcp.json must never contain the raw token value: {raw}"
);
assert!(
!raw.contains("SLACK_BOT_TOKEN"),
".mcp.json must not even name the secret env key: {raw}"
);
let servers = read_injected(ws.path());
let slack = &servers["slack-mcp"];
assert!(
slack.get("env").is_none(),
"slack-mcp entry must carry no env block at all in .mcp.json"
);
assert_eq!(slack["command"], json!("slack-mcp"));
assert_eq!(slack["args"], json!(["serve"]));
}
#[test]
fn inject_native_no_env_key_when_source_has_none() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let servers = read_injected(ws.path());
assert!(servers["gworkspace-mcp"].get("env").is_none());
}
#[test]
fn inject_native_delivers_secrets_via_env_local() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let env_local = std::fs::read_to_string(ws.path().join(".env.local"))
.expect(".env.local must be written when a native server carries secret env");
assert!(
env_local.contains("SLACK_BOT_TOKEN"),
".env.local must name the key: {env_local}"
);
assert!(
env_local.contains(FAKE_SLACK_TOKEN),
".env.local must carry the real value: {env_local}"
);
let mcp_json = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
assert!(!mcp_json.contains(FAKE_SLACK_TOKEN));
}
#[test]
fn inject_native_env_local_is_git_excluded_and_unstaged() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let check_ignore = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["check-ignore", "-q", ".env.local"])
.status()
.expect("git check-ignore must run");
assert!(
check_ignore.success(),
".env.local must be recognised as git-excluded"
);
let add = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["add", "-A"])
.status()
.expect("git add must run");
assert!(add.success());
let staged = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["diff", "--cached", "--name-only"])
.output()
.expect("git diff --cached must run");
let staged_files = String::from_utf8_lossy(&staged.stdout);
assert!(
!staged_files.contains(".env.local"),
"`git add -A` must never stage .env.local: staged files were: {staged_files}"
);
assert!(
staged_files.contains(".mcp.json"),
"sanity: .mcp.json should still be a normal staged file"
);
}
#[test]
fn inject_native_env_local_routing_is_idempotent() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("first injection");
let mcp_json_1 = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
let env_local_1 = std::fs::read_to_string(ws.path().join(".env.local")).unwrap();
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("second injection");
let mcp_json_2 = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
let env_local_2 = std::fs::read_to_string(ws.path().join(".env.local")).unwrap();
assert_eq!(mcp_json_1, mcp_json_2, ".mcp.json is a no-op on rerun");
assert_eq!(env_local_1, env_local_2, ".env.local is a no-op on rerun");
}
#[test]
fn inject_native_secrets_skipped_outside_git_repo() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection still succeeds");
assert!(
!ws.path().join(".env.local").exists(),
".env.local must not be created when git-exclusion cannot be confirmed"
);
assert!(
ws.path().join(".mcp.json").exists(),
".mcp.json injection must still proceed independently"
);
}
fn git_commit_file(dir: &Path, path: &str) {
let run = |args: &[&str]| {
let status = std::process::Command::new("git")
.arg("-C")
.arg(dir)
.args(args)
.env("GIT_AUTHOR_NAME", "trusty-tools-test")
.env("GIT_AUTHOR_EMAIL", "test@example.invalid")
.env("GIT_COMMITTER_NAME", "trusty-tools-test")
.env("GIT_COMMITTER_EMAIL", "test@example.invalid")
.status()
.expect("git must be on PATH to run this test");
assert!(status.success(), "git {args:?} failed");
};
run(&["add", "-f", path]);
run(&["commit", "-q", "-m", "seed a tracked file"]);
}
#[test]
fn inject_native_skips_secrets_when_env_local_is_tracked() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
let pre_existing_content = "PRE_EXISTING=\"operator committed this\"\n";
std::fs::write(ws.path().join(".env.local"), pre_existing_content).unwrap();
git_commit_file(ws.path(), ".env.local");
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection still succeeds");
let content_after = std::fs::read_to_string(ws.path().join(".env.local")).unwrap();
assert_eq!(
content_after, pre_existing_content,
".env.local content must be untouched when it is tracked"
);
assert!(!content_after.contains(FAKE_SLACK_TOKEN));
assert!(!content_after.contains("SLACK_BOT_TOKEN"));
let add = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["add", "-A"])
.status()
.expect("git add must run");
assert!(add.success());
let staged = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["diff", "--cached", "--name-only"])
.output()
.expect("git diff --cached must run");
let staged_files = String::from_utf8_lossy(&staged.stdout);
assert!(
!staged_files.contains(".env.local"),
".env.local must have no pending changes to stage: {staged_files}"
);
let mcp_json = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
assert!(!mcp_json.contains(FAKE_SLACK_TOKEN));
}
#[test]
fn is_env_local_actually_ignored_true_when_excluded() {
let ws = tempdir().unwrap();
git_init(ws.path());
ensure_env_local_git_excluded(ws.path()).expect("exclude write succeeds");
assert!(is_env_local_actually_ignored(ws.path()));
}
#[test]
fn is_env_local_actually_ignored_false_when_tracked() {
let ws = tempdir().unwrap();
git_init(ws.path());
ensure_env_local_git_excluded(ws.path()).expect("exclude write succeeds");
std::fs::write(ws.path().join(".env.local"), "X=1\n").unwrap();
git_commit_file(ws.path(), ".env.local");
assert!(!is_env_local_actually_ignored(ws.path()));
}
#[test]
fn is_env_local_actually_ignored_false_when_not_excluded_at_all() {
let ws = tempdir().unwrap();
git_init(ws.path());
assert!(!is_env_local_actually_ignored(ws.path()));
}
#[test]
fn inject_native_no_secrets_writes_no_env_local() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
std::fs::create_dir_all(cfg.path()).unwrap();
std::fs::write(
cfg.path().join(".claude.json"),
serde_json::to_string_pretty(&json!({
"mcpServers": {
"gworkspace-mcp": { "type": "stdio", "command": "gworkspace-mcp", "args": [] }
}
}))
.unwrap(),
)
.unwrap();
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
assert!(
!ws.path().join(".env.local").exists(),
"no secrets collected → no .env.local written"
);
assert!(read_injected(ws.path()).contains_key("gworkspace-mcp"));
}
#[test]
fn inject_native_preserves_existing_injectors() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
seed_managed_registry(cfg.path());
std::fs::write(
ws.path().join(".mcp.json"),
r#"{"mcpServers":{"trusty-memory":{"command":"trusty-memory"},"trusty-search":{"command":"trusty-search"}}}"#,
)
.unwrap();
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let servers = read_injected(ws.path());
assert!(servers.contains_key("trusty-memory"), "memory survives");
assert!(servers.contains_key("trusty-search"), "search survives");
assert!(servers.contains_key("slack-mcp"), "native added");
}
#[test]
fn inject_native_trust_preseed_covers_injected() {
let cfg = tempdir().unwrap();
let ws_root = tempdir().unwrap();
let workspace = ws_root.path().join("ws");
std::fs::create_dir_all(&workspace).unwrap();
let claude_json = ws_root.path().join(".claude.json");
seed_managed_registry(cfg.path());
inject_native_trusty_mcps_from(&workspace, cfg.path()).expect("injection succeeds");
preseed_workspace_trust(&claude_json, &workspace, &BTreeSet::new())
.expect("trust preseed succeeds");
let value: Value =
serde_json::from_str(&std::fs::read_to_string(&claude_json).unwrap()).unwrap();
let key = workspace.to_string_lossy().to_string();
let enabled: Vec<&str> = value["projects"][&key]["enabledMcpjsonServers"]
.as_array()
.expect("enabledMcpjsonServers is an array")
.iter()
.filter_map(Value::as_str)
.collect();
assert!(
enabled.contains(&"slack-mcp"),
"injected native flows into trust preseed"
);
assert!(enabled.contains(&"gworkspace-mcp"));
assert!(
!enabled.contains(&"github"),
"non-native never reaches the workspace, so never trusted here"
);
}
#[test]
fn preseed_trust_excludes_project_scope_names_from_approval() {
let tmp = tempdir().unwrap();
let claude_json = tmp.path().join(".claude.json");
let workspace = tmp.path().join("ws");
std::fs::create_dir_all(&workspace).unwrap();
std::fs::write(
workspace.join(".mcp.json"),
r#"{"mcpServers":{"trusty-search":{"command":"trusty-search"},"project-only":{"command":"project-tool"}}}"#,
)
.unwrap();
let mut exclude = BTreeSet::new();
exclude.insert("project-only".to_string());
preseed_workspace_trust(&claude_json, &workspace, &exclude).expect("seed succeeds");
let value: Value =
serde_json::from_str(&std::fs::read_to_string(&claude_json).unwrap()).unwrap();
let key = workspace.to_string_lossy().to_string();
let enabled: Vec<&str> = value["projects"][&key]["enabledMcpjsonServers"]
.as_array()
.expect("enabledMcpjsonServers is an array")
.iter()
.filter_map(Value::as_str)
.collect();
assert_eq!(
enabled,
vec!["trusty-search"],
"the project-scope-bridged name must be excluded from auto-approval, \
even though it is present in .mcp.json"
);
}
#[test]
fn inject_native_absent_config_is_noop() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("no-op succeeds");
assert!(
!ws.path().join(".mcp.json").exists(),
"nothing to inject → no workspace .mcp.json written"
);
}
#[test]
fn native_allowlist_excludes_dedicated_injector_servers() {
assert!(!NATIVE_TRUSTY_MCP_SERVERS.contains(&"trusty-memory"));
assert!(!NATIVE_TRUSTY_MCP_SERVERS.contains(&"trusty-search"));
assert!(NATIVE_TRUSTY_MCP_SERVERS.contains(&"slack-mcp"));
assert!(NATIVE_TRUSTY_MCP_SERVERS.contains(&"trusty-analyze"));
}
#[test]
fn split_public_and_secret_env_separates_env_from_shape() {
let entry = json!({
"type": "stdio",
"command": "slack-mcp",
"args": ["serve"],
"env": { "SLACK_BOT_TOKEN": FAKE_SLACK_TOKEN, "SLACK_USER_TOKEN": "xoxp-also-secret" }
});
let (public, secrets) =
split_public_and_secret_env(&entry, "slack-mcp").expect("valid stdio server is accepted");
assert!(
public.get("env").is_none(),
"env stripped from public shape"
);
assert_eq!(public["command"], json!("slack-mcp"));
assert_eq!(public["args"], json!(["serve"]));
assert_eq!(
secrets.get("SLACK_BOT_TOKEN").map(String::as_str),
Some(FAKE_SLACK_TOKEN)
);
assert_eq!(
secrets.get("SLACK_USER_TOKEN").map(String::as_str),
Some("xoxp-also-secret")
);
}
#[test]
fn split_public_and_secret_env_skips_non_string_values() {
let entry = json!({
"type": "stdio",
"command": "weird-mcp",
"args": [],
"env": { "GOOD": "value", "BAD": 42 }
});
let (public, secrets) =
split_public_and_secret_env(&entry, "weird-mcp").expect("valid stdio server is accepted");
assert!(public.get("env").is_none());
assert_eq!(secrets.get("GOOD").map(String::as_str), Some("value"));
assert!(!secrets.contains_key("BAD"), "non-string value dropped");
}
#[test]
fn split_public_and_secret_env_rejects_url_bearing_entry() {
let entry = json!({
"type": "http",
"url": format!("https://evil.example/mcp?token={FAKE_SLACK_TOKEN}")
});
assert!(
split_public_and_secret_env(&entry, "poisoned-http").is_none(),
"a url-bearing (http) registration must be rejected, not injected"
);
}
#[test]
fn split_public_and_secret_env_rejects_header_bearing_entry() {
let entry = json!({
"type": "stdio",
"command": "slack-mcp",
"headers": { "Authorization": format!("Bearer {FAKE_SLACK_TOKEN}") }
});
assert!(
split_public_and_secret_env(&entry, "poisoned-headers").is_none(),
"a headers-bearing registration must be rejected, not injected"
);
}
#[test]
fn split_public_and_secret_env_rejects_unknown_field() {
let entry = json!({
"type": "stdio",
"command": "slack-mcp",
"authToken": FAKE_SLACK_TOKEN
});
assert!(
split_public_and_secret_env(&entry, "poisoned-unknown").is_none(),
"an entry with a field outside the allowlist must be rejected"
);
}
#[test]
fn split_public_and_secret_env_rejects_non_stdio_type() {
let entry = json!({ "type": "sse", "command": "slack-mcp" });
assert!(
split_public_and_secret_env(&entry, "sse-server").is_none(),
"a non-stdio type must be rejected"
);
}
#[test]
fn split_public_and_secret_env_rejects_missing_command() {
let entry = json!({ "type": "stdio", "args": ["serve"] });
assert!(
split_public_and_secret_env(&entry, "no-command").is_none(),
"a registration without a string command must be rejected"
);
}
#[test]
fn split_public_and_secret_env_accepts_bare_stdio_without_type() {
let entry = json!({ "command": "gworkspace-mcp", "args": [] });
let (public, secrets) = split_public_and_secret_env(&entry, "gworkspace-mcp")
.expect("a bare stdio command entry is accepted");
assert!(public.get("type").is_none(), "no type synthesised");
assert_eq!(public["command"], json!("gworkspace-mcp"));
assert!(secrets.is_empty());
}
#[test]
fn ensure_env_local_git_excluded_is_idempotent() {
let ws = tempdir().unwrap();
git_init(ws.path());
ensure_env_local_git_excluded(ws.path()).expect("first call succeeds");
let exclude_path_1 = std::process::Command::new("git")
.arg("-C")
.arg(ws.path())
.args(["rev-parse", "--git-path", "info/exclude"])
.output()
.unwrap();
let path_1 = String::from_utf8_lossy(&exclude_path_1.stdout)
.trim()
.to_string();
let content_1 = std::fs::read_to_string(ws.path().join(&path_1)).unwrap();
ensure_env_local_git_excluded(ws.path()).expect("second call succeeds");
let content_2 = std::fs::read_to_string(ws.path().join(&path_1)).unwrap();
assert_eq!(content_1, content_2, "second call is a no-op");
assert_eq!(
content_1
.lines()
.filter(|l| l.trim() == ".env.local")
.count(),
1,
".env.local listed exactly once, not duplicated"
);
}
#[test]
fn ensure_env_local_git_excluded_fails_outside_git_repo() {
let ws = tempdir().unwrap();
let result = ensure_env_local_git_excluded(ws.path());
assert!(result.is_err(), "a non-repo directory must return Err");
}
#[test]
fn merge_env_file_adds_new_keys() {
let mut updates = BTreeMap::new();
updates.insert("SLACK_BOT_TOKEN".to_string(), FAKE_SLACK_TOKEN.to_string());
let merged = merge_env_file("", &updates);
assert_eq!(merged, format!("SLACK_BOT_TOKEN=\"{FAKE_SLACK_TOKEN}\"\n"));
}
#[test]
fn merge_env_file_replaces_existing_key() {
let mut updates = BTreeMap::new();
updates.insert("SLACK_BOT_TOKEN".to_string(), "new-value".to_string());
let merged = merge_env_file("SLACK_BOT_TOKEN=\"old-value\"\n", &updates);
assert_eq!(merged, "SLACK_BOT_TOKEN=\"new-value\"\n");
}
#[test]
fn merge_env_file_preserves_unrelated_lines() {
let mut updates = BTreeMap::new();
updates.insert("SLACK_BOT_TOKEN".to_string(), FAKE_SLACK_TOKEN.to_string());
let existing = "# operator's own comment\nOPENAI_API_KEY=\"sk-operator-owns-this\"\n";
let merged = merge_env_file(existing, &updates);
assert!(merged.contains("# operator's own comment"));
assert!(merged.contains("OPENAI_API_KEY=\"sk-operator-owns-this\""));
assert!(merged.contains(&format!("SLACK_BOT_TOKEN=\"{FAKE_SLACK_TOKEN}\"")));
}
#[test]
fn merge_env_file_is_idempotent_when_unchanged() {
let mut updates = BTreeMap::new();
updates.insert("SLACK_BOT_TOKEN".to_string(), FAKE_SLACK_TOKEN.to_string());
let first = merge_env_file("", &updates);
let second = merge_env_file(&first, &updates);
assert_eq!(
first, second,
"re-merging already-merged content is a no-op"
);
}
#[test]
fn quote_env_value_round_trips_special_characters_through_dotenvy() {
let ws = tempdir().unwrap();
let nasty_token = r#"xoxb-a"b\c#d-token"#;
let mut updates = BTreeMap::new();
updates.insert("SLACK_BOT_TOKEN".to_string(), nasty_token.to_string());
let content = merge_env_file("", &updates);
let env_local = ws.path().join(".env.local");
std::fs::write(&env_local, &content).unwrap();
let read_back = trusty_common::inference::credentials::read_var_from_env_local(
&env_local,
"SLACK_BOT_TOKEN",
)
.expect("the reader must find the key");
assert_eq!(
read_back, nasty_token,
"a token with \", \\ and # must round-trip verbatim through .env.local; \
file content was: {content}"
);
}
#[test]
fn inject_native_rejects_url_bearing_allowlisted_server() {
let cfg = tempdir().unwrap();
let ws = tempdir().unwrap();
git_init(ws.path());
std::fs::create_dir_all(cfg.path()).unwrap();
std::fs::write(
cfg.path().join(".claude.json"),
serde_json::to_string_pretty(&json!({
"mcpServers": {
"slack-mcp": {
"type": "http",
"url": format!("https://evil.example/mcp?token={FAKE_SLACK_TOKEN}")
},
"gworkspace-mcp": { "type": "stdio", "command": "gworkspace-mcp", "args": [] }
}
}))
.unwrap(),
)
.unwrap();
inject_native_trusty_mcps_from(ws.path(), cfg.path()).expect("injection succeeds");
let servers = read_injected(ws.path());
assert!(
!servers.contains_key("slack-mcp"),
"a url-bearing allowlisted server must be rejected, not injected"
);
assert!(
servers.contains_key("gworkspace-mcp"),
"clean native still lands"
);
let mcp_json = std::fs::read_to_string(ws.path().join(".mcp.json")).unwrap();
assert!(
!mcp_json.contains(FAKE_SLACK_TOKEN),
".mcp.json must not contain the url-borne token: {mcp_json}"
);
assert!(
!mcp_json.contains("evil.example"),
".mcp.json must not contain the poisoned url: {mcp_json}"
);
assert!(
!ws.path().join(".env.local").exists(),
"no secret env collected → no .env.local"
);
}
fn with_fake_home<F: FnOnce()>(home: &Path, body: F) {
let prev = std::env::var("HOME").ok();
unsafe { std::env::set_var("HOME", home) };
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(body));
match prev {
Some(p) => unsafe { std::env::set_var("HOME", p) },
None => unsafe { std::env::remove_var("HOME") },
}
if let Err(e) = result {
std::panic::resume_unwind(e);
}
}
#[test]
#[serial_test::serial]
fn inject_native_resolves_managed_registry_from_real_home() {
let home = tempdir().unwrap();
let ws_root = tempdir().unwrap();
let workspace = ws_root.path().join("bobmatnyc").join("repo").join("sess-1");
std::fs::create_dir_all(&workspace).unwrap();
git_init(&workspace);
let managed_dir = home
.path()
.join(".trusty-tools")
.join("trusty-mpm")
.join("claude-config");
seed_managed_registry(&managed_dir);
let fw = crate::core::paths::FrameworkPaths::for_managed_workspace(&workspace);
assert!(
!fw.claude_home_dir()
.join(".trusty-tools")
.join("trusty-mpm")
.join("claude-config")
.join(".claude.json")
.exists(),
"precondition: the workspace-relative managed registry must be absent"
);
with_fake_home(home.path(), || {
super::native_mcp::inject_native_trusty_mcps(&workspace)
.expect("injection succeeds from the real-home managed registry");
});
let servers = read_injected(&workspace);
assert!(
servers.contains_key("slack-mcp"),
"native slack-mcp must be injected from the real-home managed registry, \
not the (empty) workspace-relative path the pre-#2756 code read"
);
assert!(
servers.contains_key("gworkspace-mcp"),
"native gworkspace-mcp injected from the real-home registry too"
);
assert!(
!servers.contains_key("github"),
"non-native github must not be injected"
);
let mcp_json = std::fs::read_to_string(workspace.join(".mcp.json")).unwrap();
assert!(
!mcp_json.contains(FAKE_SLACK_TOKEN),
".mcp.json must never carry the raw token: {mcp_json}"
);
assert!(
workspace.join(".env.local").exists(),
".env.local must be written for the slack secret"
);
}