use std::fs;
use std::path::PathBuf;
use std::sync::Mutex;
use secrecy::ExposeSecret;
use tempfile::TempDir;
use agent_vault::core::vault::{CheckIssue, Vault};
static HOME_LOCK: Mutex<()> = Mutex::new(());
fn setup_git_repo() -> (TempDir, PathBuf) {
let tmp = TempDir::new().unwrap();
let root = tmp.path().to_path_buf();
git2::Repository::init(&root).unwrap();
let fake_home = root.join("fakehome");
fs::create_dir_all(&fake_home).unwrap();
unsafe {
std::env::set_var("HOME", &fake_home);
}
(tmp, root)
}
#[test]
fn test_full_flow_init_add_agent_set_get() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
assert!(root.join(".agent-vault/config.yaml").exists());
assert!(root.join(".agent-vault/owner.pub").exists());
assert!(root.join(".agent-vault/manifest.yaml").exists());
assert!(root.join(".agent-vault/.gitignore").exists());
let owner_key_path = agent_vault::core::paths::owner_key_path();
assert!(owner_key_path.exists());
let agent_key_path = vault.add_agent("test-agent").unwrap();
assert!(agent_key_path.exists());
assert!(root
.join(".agent-vault/agents/test-agent/public.key")
.exists());
assert!(root
.join(".agent-vault/agents/test-agent/private.key.escrow")
.exists());
vault
.set_secret("stripe/api-key", "sk_test_123", "stripe", None, None)
.unwrap();
let plaintext = vault
.get_secret("stripe/api-key", &owner_key_path)
.unwrap();
assert_eq!(plaintext.expose_secret(), "sk_test_123");
let result = vault.get_secret("stripe/api-key", &agent_key_path);
assert!(result.is_err());
let agents = vault.list_agents().unwrap();
assert_eq!(agents.len(), 1);
assert_eq!(agents[0].0, "test-agent");
assert!(agents[0].1.is_empty());
let secrets = vault.list_secrets(None).unwrap();
assert_eq!(secrets.len(), 1);
assert_eq!(secrets[0].name, "stripe/api-key");
}
#[test]
fn test_init_twice_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
Vault::init(&root).unwrap();
assert!(Vault::init(&root).is_err());
}
#[test]
fn test_add_duplicate_agent_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.add_agent("bot1").unwrap();
assert!(vault.add_agent("bot1").is_err());
}
#[test]
fn test_get_nonexistent_secret() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let key_path = agent_vault::core::paths::owner_key_path();
assert!(vault.get_secret("nope/nothing", &key_path).is_err());
}
#[test]
fn test_multiple_secrets() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.set_secret("stripe/webhook-secret", "whsec_456", "stripe", None, None).unwrap();
vault.set_secret("postgres/conn-string", "postgres://...", "postgres", None, None).unwrap();
assert_eq!(vault.list_secrets(None).unwrap().len(), 3);
assert_eq!(vault.list_secrets(Some("stripe")).unwrap().len(), 2);
let owner_key = agent_vault::core::paths::owner_key_path();
assert_eq!(
vault.get_secret("stripe/api-key", &owner_key).unwrap().expose_secret(),
"sk_123"
);
assert_eq!(
vault.get_secret("postgres/conn-string", &owner_key).unwrap().expose_secret(),
"postgres://..."
);
}
#[test]
fn test_grant_enables_agent_access() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let agent_key = vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
assert!(vault.get_secret("stripe/api-key", &agent_key).is_err());
let secrets = vault.grant_agent("bot1", "stripe").unwrap();
assert_eq!(secrets, vec!["stripe/api-key"]);
let plaintext = vault.get_secret("stripe/api-key", &agent_key).unwrap();
assert_eq!(plaintext.expose_secret(), "sk_123");
let owner_key = agent_vault::core::paths::owner_key_path();
let plaintext = vault.get_secret("stripe/api-key", &owner_key).unwrap();
assert_eq!(plaintext.expose_secret(), "sk_123");
let agents = vault.list_agents().unwrap();
assert_eq!(agents[0].1, vec!["stripe"]);
}
#[test]
fn test_revoke_removes_agent_access() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let agent_key = vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
assert_eq!(
vault.get_secret("stripe/api-key", &agent_key).unwrap().expose_secret(),
"sk_123"
);
let secrets = vault.revoke_agent("bot1", "stripe").unwrap();
assert_eq!(secrets, vec!["stripe/api-key"]);
assert!(vault.get_secret("stripe/api-key", &agent_key).is_err());
let owner_key = agent_vault::core::paths::owner_key_path();
assert_eq!(
vault.get_secret("stripe/api-key", &owner_key).unwrap().expose_secret(),
"sk_123"
);
}
#[test]
fn test_grant_nonexistent_agent_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
assert!(vault.grant_agent("ghost", "stripe").is_err());
}
#[test]
fn test_grant_nonexistent_group_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.add_agent("bot1").unwrap();
assert!(vault.grant_agent("bot1", "nonexistent").is_err());
}
#[test]
fn test_multi_agent_grant_revoke() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let key1 = vault.add_agent("bot1").unwrap();
let key2 = vault.add_agent("bot2").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
vault.grant_agent("bot2", "stripe").unwrap();
assert_eq!(vault.get_secret("stripe/api-key", &key1).unwrap().expose_secret(), "sk_123");
assert_eq!(vault.get_secret("stripe/api-key", &key2).unwrap().expose_secret(), "sk_123");
vault.revoke_agent("bot1", "stripe").unwrap();
assert!(vault.get_secret("stripe/api-key", &key1).is_err());
assert_eq!(vault.get_secret("stripe/api-key", &key2).unwrap().expose_secret(), "sk_123");
}
#[test]
fn test_remove_agent() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let agent_key = vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
assert_eq!(
vault.get_secret("stripe/api-key", &agent_key).unwrap().expose_secret(),
"sk_123"
);
let groups = vault.remove_agent("bot1").unwrap();
assert_eq!(groups, vec!["stripe"]);
assert!(!root.join(".agent-vault/agents/bot1").exists());
assert!(vault.get_secret("stripe/api-key", &agent_key).is_err());
let owner_key = agent_vault::core::paths::owner_key_path();
assert_eq!(
vault.get_secret("stripe/api-key", &owner_key).unwrap().expose_secret(),
"sk_123"
);
assert!(vault.list_agents().unwrap().is_empty());
}
#[test]
fn test_remove_nonexistent_agent_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
assert!(vault.remove_agent("ghost").is_err());
}
#[test]
fn test_restore_agent_from_escrow() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let original_key_path = vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
let original_key = fs::read_to_string(&original_key_path).unwrap();
let restore_path = root.join("restored.key");
vault.restore_agent("bot1", &restore_path).unwrap();
let restored_key = fs::read_to_string(&restore_path).unwrap();
assert_eq!(original_key.trim(), restored_key.trim());
assert_eq!(
vault.get_secret("stripe/api-key", &restore_path).unwrap().expose_secret(),
"sk_123"
);
}
#[test]
fn test_recover_agent_new_keypair() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let original_key_path = vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
let original_key = fs::read_to_string(&original_key_path).unwrap();
let new_key_path = vault.recover_agent("bot1").unwrap();
let new_key = fs::read_to_string(&new_key_path).unwrap();
assert_ne!(original_key.trim(), new_key.trim());
assert_eq!(
vault.get_secret("stripe/api-key", &new_key_path).unwrap().expose_secret(),
"sk_123"
);
let old_key_file = root.join("old.key");
fs::write(&old_key_file, &original_key).unwrap();
assert!(vault.get_secret("stripe/api-key", &old_key_file).is_err());
let owner_key = agent_vault::core::paths::owner_key_path();
assert_eq!(
vault.get_secret("stripe/api-key", &owner_key).unwrap().expose_secret(),
"sk_123"
);
}
#[test]
fn test_recover_nonexistent_agent_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
assert!(vault.recover_agent("ghost").is_err());
}
#[test]
fn test_check_healthy_vault() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.add_agent("bot1").unwrap();
vault.set_secret("stripe/api-key", "sk_123", "stripe", None, None).unwrap();
vault.grant_agent("bot1", "stripe").unwrap();
let issues = vault.check().unwrap();
let errors: Vec<_> = issues
.iter()
.filter(|i| matches!(i, CheckIssue::Error(_)))
.collect();
assert!(errors.is_empty());
}
#[test]
fn test_check_agent_no_access() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
vault.add_agent("bot1").unwrap();
let issues = vault.check().unwrap();
let has_no_access_warning = issues.iter().any(|i| match i {
CheckIssue::Warning(msg) => msg.contains("bot1") && msg.contains("no group access"),
_ => false,
});
assert!(has_no_access_warning);
}
#[test]
fn test_set_with_extra_agents() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let agent_key = vault.add_agent("bot1").unwrap();
vault
.set_secret(
"stripe/api-key",
"sk_123",
"stripe",
None,
Some(&["bot1".to_string()]),
)
.unwrap();
let plaintext = vault.get_secret("stripe/api-key", &agent_key).unwrap();
assert_eq!(plaintext.expose_secret(), "sk_123");
let owner_key = agent_vault::core::paths::owner_key_path();
assert_eq!(
vault
.get_secret("stripe/api-key", &owner_key)
.unwrap()
.expose_secret(),
"sk_123"
);
let secrets = vault.list_secrets(None).unwrap();
assert!(secrets[0].authorized_agents.contains(&"bot1".to_string()));
}
#[test]
fn test_set_with_nonexistent_extra_agent_fails() {
let _lock = HOME_LOCK.lock().unwrap();
let (_tmp, root) = setup_git_repo();
let vault = Vault::init(&root).unwrap();
let result = vault.set_secret(
"stripe/api-key",
"sk_123",
"stripe",
None,
Some(&["ghost".to_string()]),
);
assert!(result.is_err());
}