#[cfg(test)]
mod integration_tests {
use std::path::PathBuf;
use tempfile::TempDir;
use vtcode_core::{
audit::{PermissionAuditLog, PermissionDecision},
tools::{CommandResolver, PermissionCache},
};
#[test]
fn test_command_resolver_basic() {
let mut resolver = CommandResolver::new();
let resolution = resolver.resolve("ls");
assert_eq!(resolution.command, "ls");
assert!(resolution.found, "ls should be found on Unix systems");
assert!(resolution.resolved_path.is_some());
}
#[test]
fn test_command_resolver_caching() {
let mut resolver = CommandResolver::new();
resolver.resolve("cargo");
let (hits1, misses1) = resolver.cache_stats();
assert_eq!(hits1, 0, "First resolve should be a miss");
assert_eq!(misses1, 1, "Should have 1 miss");
resolver.resolve("cargo");
let (hits2, misses2) = resolver.cache_stats();
assert_eq!(hits2, 1, "Second resolve should be a hit");
assert_eq!(misses2, 1, "Should still have 1 miss");
}
#[test]
fn test_permission_cache_store_and_retrieve() {
let mut cache = PermissionCache::new();
cache.put("cargo fmt", true, "test reason");
assert_eq!(cache.get("cargo fmt"), Some(true));
assert_eq!(cache.get("cargo check"), None);
}
#[test]
fn test_permission_cache_retains_entries() {
use std::thread;
use std::time::Duration;
let mut cache = PermissionCache::new();
cache.put("test", true, "reason");
assert_eq!(
cache.get("test"),
Some(true),
"Should be available immediately",
);
thread::sleep(Duration::from_millis(100));
assert_eq!(cache.get("test"), Some(true), "Should remain without TTL",);
}
#[test]
fn test_audit_log_creation_and_logging() -> anyhow::Result<()> {
let dir = TempDir::new()?;
let mut log = PermissionAuditLog::new(dir.path().to_path_buf())?;
assert_eq!(log.event_count(), 0);
assert!(!log.log_path().exists());
log.log_command_decision(
"cargo fmt",
PermissionDecision::Allowed,
"allow_glob match: cargo *",
Some(PathBuf::from("/usr/local/bin/cargo")),
)?;
assert_eq!(log.event_count(), 1);
assert!(dir.path().exists());
assert!(log.log_path().exists());
Ok(())
}
#[test]
fn test_full_permission_flow() -> anyhow::Result<()> {
let audit_dir = TempDir::new()?;
let mut audit_log = PermissionAuditLog::new(audit_dir.path().to_path_buf())?;
let mut resolver = CommandResolver::new();
let mut cache = PermissionCache::new();
let command = "cargo fmt";
let cached_decision = cache.get(command);
assert!(cached_decision.is_none(), "Cache should be empty initially");
let resolution = resolver.resolve(command);
assert_eq!(resolution.command, "cargo");
let allowed = true;
cache.put(command, allowed, "allow_glob match: cargo *");
audit_log.log_command_decision(
command,
if allowed {
PermissionDecision::Allowed
} else {
PermissionDecision::Denied
},
"allow_glob match: cargo *",
resolution.resolved_path.clone(),
)?;
assert_eq!(cache.get(command), Some(true));
assert_eq!(audit_log.event_count(), 1);
Ok(())
}
#[test]
fn test_resolver_with_args_extracts_base_command() {
let mut resolver = CommandResolver::new();
let resolution = resolver.resolve("cargo fmt --check");
assert_eq!(resolution.command, "cargo");
}
#[test]
fn test_nonexistent_command_not_found() {
let mut resolver = CommandResolver::new();
let resolution = resolver.resolve("this_command_definitely_does_not_exist_xyz_123");
assert!(!resolution.found);
assert!(resolution.resolved_path.is_none());
}
#[test]
fn test_cache_multiple_commands() {
let mut cache = PermissionCache::new();
cache.put("cmd1", true, "allowed");
cache.put("cmd2", false, "denied");
cache.put("cmd3", true, "allowed");
assert_eq!(cache.get("cmd1"), Some(true));
assert_eq!(cache.get("cmd2"), Some(false));
assert_eq!(cache.get("cmd3"), Some(true));
let stats = cache.stats();
assert_eq!(stats.cached_entries, 3);
}
#[test]
fn test_cache_clear() {
let mut cache = PermissionCache::new();
cache.put("cmd1", true, "test");
cache.put("cmd2", false, "test");
let stats_before = cache.stats();
assert_eq!(stats_before.cached_entries, 2);
cache.clear();
let stats_after = cache.stats();
assert_eq!(stats_after.cached_entries, 0);
assert_eq!(cache.get("cmd1"), None);
}
#[test]
fn test_audit_log_file_location() -> anyhow::Result<()> {
let dir = TempDir::new()?;
let audit_log = PermissionAuditLog::new(dir.path().to_path_buf())?;
let log_path = audit_log.log_path();
assert!(!log_path.exists());
assert!(log_path.to_string_lossy().contains("permissions-"));
assert!(log_path.to_string_lossy().contains(".log"));
Ok(())
}
#[tokio::test]
async fn test_resolver_stats() {
let mut resolver = CommandResolver::new();
resolver.resolve("ls");
resolver.resolve("ls");
resolver.resolve("pwd");
resolver.resolve("pwd");
resolver.resolve("pwd");
let (hits, misses) = resolver.cache_stats();
assert_eq!(hits, 3, "Should have 3 cache hits");
assert_eq!(misses, 2, "Should have 2 cache misses");
}
}