use std::path::{Path, PathBuf};
use tempfile::TempDir;
use tokensave::agents::{
expected_tool_perms, AgentIntegration, AugmentIntegration, DoctorCounters, HealthcheckContext,
InstallContext, InstallScope,
};
fn make_ctx(home: &Path) -> InstallContext {
InstallContext {
home: home.to_path_buf(),
tokensave_bin: "/usr/local/bin/tokensave".to_string(),
tool_permissions: expected_tool_perms(),
scope: InstallScope::Global,
force_permission_style: false,
}
}
fn make_local_ctx(home: &Path, project: &Path) -> InstallContext {
InstallContext {
home: home.to_path_buf(),
tokensave_bin: "/usr/local/bin/tokensave".to_string(),
tool_permissions: expected_tool_perms(),
scope: InstallScope::Local {
project_path: project.to_path_buf(),
},
force_permission_style: false,
}
}
fn read_json(path: &Path) -> serde_json::Value {
let contents = std::fs::read_to_string(path).unwrap();
serde_json::from_str(&contents).unwrap()
}
fn settings_path(home: &Path) -> PathBuf {
home.join(".augment/settings.json")
}
fn rules_path(home: &Path) -> PathBuf {
home.join(".augment/rules/tokensave.md")
}
#[test]
fn test_install_creates_settings_json() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let path = settings_path(home);
assert!(path.exists(), "settings.json should be created");
let config = read_json(&path);
let ts = &config["mcpServers"]["tokensave"];
assert!(ts.is_object(), "mcpServers.tokensave should be an object");
assert_eq!(
ts["type"].as_str().unwrap(),
"stdio",
"type should be stdio"
);
assert_eq!(
ts["command"].as_str().unwrap(),
"/usr/local/bin/tokensave",
"command should be the tokensave binary path"
);
let args: Vec<&str> = ts["args"]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_str().unwrap())
.collect();
assert_eq!(args, vec!["serve"], "args should be [\"serve\"]");
}
#[test]
fn test_install_writes_rules_file() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let rules = std::fs::read_to_string(rules_path(home)).unwrap();
assert!(
rules.contains("## Prefer tokensave MCP tools"),
"tokensave.md should contain the tokensave rules marker"
);
assert!(
rules.contains("type: always_apply"),
"tokensave.md should carry always_apply frontmatter"
);
}
#[test]
fn test_install_preserves_existing_server() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = settings_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(
&path,
r#"{"someSetting": true, "mcpServers": {"other-tool": {"command": "other", "args": ["run"]}}}"#,
)
.unwrap();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let config = read_json(&path);
assert!(
config["someSetting"].as_bool().unwrap(),
"unrelated top-level key should be preserved"
);
assert!(
config["mcpServers"]["other-tool"].is_object(),
"existing MCP server should be preserved"
);
assert!(
config["mcpServers"]["tokensave"].is_object(),
"tokensave should be added"
);
}
#[test]
fn test_install_preserves_unrelated_top_level_keys() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = settings_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(
&path,
r#"{
"model": "prism-a",
"vimMode": false,
"indexingAllowDirs": ["/some/dir"],
"hooks": {"Stop": [{"hooks": [{"type": "command", "command": "echo hi"}]}]},
"mcpServers": {}
}"#,
)
.unwrap();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let config = read_json(&path);
assert_eq!(config["model"].as_str().unwrap(), "prism-a");
assert!(!config["vimMode"].as_bool().unwrap());
assert_eq!(
config["indexingAllowDirs"].as_array().unwrap().len(),
1,
"indexingAllowDirs should be preserved"
);
assert!(
config["hooks"]["Stop"].is_array(),
"hooks should be preserved"
);
assert!(config["mcpServers"]["tokensave"].is_object());
}
#[test]
fn test_install_idempotent() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
AugmentIntegration.install(&ctx).unwrap();
let config = read_json(&settings_path(home));
let servers = config["mcpServers"].as_object().unwrap();
let ts_count = servers.keys().filter(|k| *k == "tokensave").count();
assert_eq!(ts_count, 1, "tokensave should appear exactly once");
let rules = std::fs::read_to_string(rules_path(home)).unwrap();
assert_eq!(
rules.matches("## Prefer tokensave MCP tools").count(),
1,
"rules marker should appear exactly once"
);
}
#[test]
fn test_primary_config_path_matches_install_target() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let primary = AugmentIntegration.primary_config_path(home).unwrap();
assert!(
primary.exists(),
"primary_config_path should exist after install"
);
assert_eq!(
primary,
settings_path(home),
"primary_config_path should match where install wrote"
);
}
#[test]
fn test_local_install_targets_project() {
let home_dir = TempDir::new().unwrap();
let proj_dir = TempDir::new().unwrap();
let home = home_dir.path();
let project = proj_dir.path();
let ctx = make_local_ctx(home, project);
AugmentIntegration.install(&ctx).unwrap();
assert!(
project.join(".augment/settings.json").exists(),
"--local should write <project>/.augment/settings.json"
);
assert!(
project.join(".augment/rules/tokensave.md").exists(),
"--local should write <project>/.augment/rules/tokensave.md"
);
assert!(
!settings_path(home).exists(),
"global ~/.augment/settings.json should not be written for --local"
);
}
#[test]
fn test_uninstall_removes_only_tokensave() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = settings_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(
&path,
r#"{"mcpServers": {"other-tool": {"command": "other", "args": ["run"]}}}"#,
)
.unwrap();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
AugmentIntegration.uninstall(&ctx).unwrap();
assert!(
path.exists(),
"config should still exist because another server remains"
);
let config = read_json(&path);
assert!(
config["mcpServers"]["other-tool"].is_object(),
"other server should be preserved"
);
assert!(
config
.get("mcpServers")
.and_then(|v| v.get("tokensave"))
.is_none(),
"tokensave should be removed"
);
}
#[test]
fn test_uninstall_preserves_unrelated_top_level_keys() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = settings_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(
&path,
r#"{"model": "prism-a", "mcpServers": {"other-tool": {"command": "other"}}}"#,
)
.unwrap();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
AugmentIntegration.uninstall(&ctx).unwrap();
let config = read_json(&path);
assert_eq!(
config["model"].as_str().unwrap(),
"prism-a",
"unrelated key should survive install+uninstall"
);
}
#[test]
fn test_uninstall_removes_empty_config_file() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
AugmentIntegration.uninstall(&ctx).unwrap();
assert!(
!settings_path(home).exists(),
"settings.json should be deleted when tokensave was the only entry"
);
}
#[test]
fn test_uninstall_removes_rules_file() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
AugmentIntegration.uninstall(&ctx).unwrap();
assert!(
!rules_path(home).exists(),
"tokensave.md should be removed on uninstall"
);
}
#[test]
fn test_uninstall_preserves_unrelated_rules_file() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = rules_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, "# My own rule\n\nUnrelated content.\n").unwrap();
let ctx = make_ctx(home);
AugmentIntegration.uninstall(&ctx).unwrap();
assert!(path.exists(), "unrelated rules file should survive");
let contents = std::fs::read_to_string(&path).unwrap();
assert!(contents.contains("Unrelated content."));
}
#[test]
fn test_uninstall_without_install_does_not_crash() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.uninstall(&ctx).unwrap();
}
#[test]
fn test_healthcheck_clean_install_no_issues() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
let mut dc = DoctorCounters::new();
let hctx = HealthcheckContext {
home: home.to_path_buf(),
project_path: home.to_path_buf(),
};
AugmentIntegration.healthcheck(&mut dc, &hctx);
assert_eq!(dc.issues, 0, "clean auggie install should have no issues");
}
#[test]
fn test_healthcheck_missing_config_produces_warning() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let mut dc = DoctorCounters::new();
let hctx = HealthcheckContext {
home: home.to_path_buf(),
project_path: home.to_path_buf(),
};
AugmentIntegration.healthcheck(&mut dc, &hctx);
assert!(
dc.warnings > 0 || dc.issues > 0,
"healthcheck on empty dir should report warnings or issues"
);
}
#[test]
fn test_healthcheck_detects_missing_mcp_entry() {
let dir = TempDir::new().unwrap();
let home = dir.path();
let path = settings_path(home);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, r#"{"mcpServers": {}}"#).unwrap();
let mut dc = DoctorCounters::new();
let hctx = HealthcheckContext {
home: home.to_path_buf(),
project_path: home.to_path_buf(),
};
AugmentIntegration.healthcheck(&mut dc, &hctx);
assert!(dc.issues > 0, "healthcheck should detect missing MCP entry");
}
#[test]
fn test_is_detected_empty_home() {
let dir = TempDir::new().unwrap();
let home = dir.path();
assert!(
!AugmentIntegration.is_detected(home),
"should not be detected on empty home"
);
}
#[test]
fn test_is_detected_with_augment_dir() {
let dir = TempDir::new().unwrap();
let home = dir.path();
std::fs::create_dir_all(home.join(".augment")).unwrap();
assert!(
AugmentIntegration.is_detected(home),
"should be detected when ~/.augment exists"
);
}
#[test]
fn test_has_tokensave_before_and_after_install() {
let dir = TempDir::new().unwrap();
let home = dir.path();
assert!(
!AugmentIntegration.has_tokensave(home),
"has_tokensave should be false before install"
);
let ctx = make_ctx(home);
AugmentIntegration.install(&ctx).unwrap();
assert!(
AugmentIntegration.has_tokensave(home),
"has_tokensave should be true after install"
);
AugmentIntegration.uninstall(&ctx).unwrap();
assert!(
!AugmentIntegration.has_tokensave(home),
"has_tokensave should be false after uninstall"
);
}
#[test]
fn test_name_and_id() {
assert_eq!(AugmentIntegration.name(), "AugmentCode");
assert_eq!(AugmentIntegration.id(), "auggie");
}