use std::os::unix::fs::PermissionsExt;
use chrono::{Duration, Utc};
use gloves::{
audit::{AuditEvent, AuditLog},
types::{AgentId, SecretId},
};
#[test]
fn log_writes_jsonl() {
let temp_dir = tempfile::tempdir().unwrap();
let log = AuditLog::new(temp_dir.path().join("audit.jsonl")).unwrap();
log.log(AuditEvent::SecretExpired {
secret_id: SecretId::new("a").unwrap(),
})
.unwrap();
let text = std::fs::read_to_string(log.path()).unwrap();
let line = text.lines().next().unwrap();
let value: serde_json::Value = serde_json::from_str(line).unwrap();
assert_eq!(value["event"], "secret_expired");
}
#[test]
fn log_appends() {
let temp_dir = tempfile::tempdir().unwrap();
let log = AuditLog::new(temp_dir.path().join("audit.jsonl")).unwrap();
log.log(AuditEvent::SecretExpired {
secret_id: SecretId::new("a").unwrap(),
})
.unwrap();
log.log(AuditEvent::SecretRevoked {
secret_id: SecretId::new("b").unwrap(),
by: AgentId::new("agent-a").unwrap(),
})
.unwrap();
let text = std::fs::read_to_string(log.path()).unwrap();
assert_eq!(text.lines().count(), 2);
}
#[test]
fn all_events_serialize() {
let events = vec![
AuditEvent::SecretAccessed {
secret_id: SecretId::new("a").unwrap(),
by: AgentId::new("agent-a").unwrap(),
},
AuditEvent::SecretExpired {
secret_id: SecretId::new("b").unwrap(),
},
AuditEvent::SecretCreated {
secret_id: SecretId::new("c").unwrap(),
by: AgentId::new("agent-b").unwrap(),
},
AuditEvent::SecretRevoked {
secret_id: SecretId::new("d").unwrap(),
by: AgentId::new("agent-c").unwrap(),
},
AuditEvent::RequestCreated {
request_id: uuid::Uuid::new_v4(),
secret_id: SecretId::new("human/token").unwrap(),
requested_by: AgentId::new("agent-a").unwrap(),
reason: "need deploy".to_owned(),
expires_at: Utc::now() + Duration::minutes(10),
},
AuditEvent::RequestApproved {
request_id: uuid::Uuid::new_v4(),
secret_id: SecretId::new("human/token").unwrap(),
requested_by: AgentId::new("agent-a").unwrap(),
approved_by: AgentId::new("reviewer-a").unwrap(),
},
AuditEvent::RequestDenied {
request_id: uuid::Uuid::new_v4(),
secret_id: SecretId::new("human/token").unwrap(),
requested_by: AgentId::new("agent-a").unwrap(),
denied_by: AgentId::new("reviewer-b").unwrap(),
},
AuditEvent::VaultCreated {
vault: "agent_data".to_owned(),
owner: gloves::types::Owner::Agent,
},
AuditEvent::VaultMounted {
vault: "agent_data".to_owned(),
agent: AgentId::new("agent-a").unwrap(),
ttl_minutes: 60,
},
AuditEvent::VaultUnmounted {
vault: "agent_data".to_owned(),
reason: "explicit".to_owned(),
agent: AgentId::new("agent-a").unwrap(),
},
AuditEvent::VaultSessionExpired {
vault: "agent_data".to_owned(),
},
AuditEvent::VaultHandoffPromptIssued {
vault: "agent_data".to_owned(),
requester: AgentId::new("agent-a").unwrap(),
trusted_agent: AgentId::new("agent-b").unwrap(),
requested_file: "docs/notes.txt".to_owned(),
},
AuditEvent::GpgKeyCreated {
agent: AgentId::new("agent-c").unwrap(),
fingerprint: "ABCDEF0123456789".to_owned(),
},
AuditEvent::CommandExecuted {
by: AgentId::new("agent-z").unwrap(),
interface: "cli".to_owned(),
command: "list".to_owned(),
target: Some("pending".to_owned()),
},
];
for event in events {
let json = serde_json::to_string(&event).unwrap();
assert!(serde_json::from_str::<serde_json::Value>(&json).is_ok());
}
}
#[test]
fn log_file_permissions() {
let temp_dir = tempfile::tempdir().unwrap();
let log = AuditLog::new(temp_dir.path().join("audit.jsonl")).unwrap();
let mode = std::fs::metadata(log.path()).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o600);
}
#[test]
fn log_includes_timestamp() {
let temp_dir = tempfile::tempdir().unwrap();
let log = AuditLog::new(temp_dir.path().join("audit.jsonl")).unwrap();
log.log(AuditEvent::SecretExpired {
secret_id: SecretId::new("a").unwrap(),
})
.unwrap();
let text = std::fs::read_to_string(log.path()).unwrap();
let line = text.lines().next().unwrap();
let value: serde_json::Value = serde_json::from_str(line).unwrap();
assert!(value["timestamp"].as_str().unwrap().contains('T'));
}