use bestool_psql::audit::{Audit, AuditEntryWithTimestamp, QueryOptions};
#[test]
fn test_audit_export_basic() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(
bestool_psql::repl::ReplState::new(),
)),
working_info: None,
sync_thread: None,
};
for i in 0..10 {
audit.add_entry(format!("SELECT {};", i)).unwrap();
std::thread::sleep(std::time::Duration::from_micros(10));
}
}
let audit = Audit::open_file(&db_path).unwrap();
let options = QueryOptions::default();
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 10);
assert_eq!(entries[0].1.query, "SELECT 0;");
assert_eq!(entries[9].1.query, "SELECT 9;");
}
#[test]
fn test_audit_export_limit() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(
bestool_psql::repl::ReplState::new(),
)),
working_info: None,
sync_thread: None,
};
for i in 0..20 {
audit.add_entry(format!("SELECT {};", i)).unwrap();
std::thread::sleep(std::time::Duration::from_micros(10));
}
}
let audit = Audit::open_file(&db_path).unwrap();
let options = QueryOptions {
limit: Some(5),
from_oldest: false,
..Default::default()
};
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 5);
assert_eq!(entries[0].1.query, "SELECT 15;");
assert_eq!(entries[4].1.query, "SELECT 19;");
let options = QueryOptions {
limit: Some(5),
from_oldest: true,
..Default::default()
};
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 5);
assert_eq!(entries[0].1.query, "SELECT 0;");
assert_eq!(entries[4].1.query, "SELECT 4;");
}
#[test]
fn test_audit_export_json_serialization() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut repl_state = bestool_psql::repl::ReplState::new();
repl_state.db_user = "testdb".to_string();
repl_state.sys_user = "testuser".to_string();
repl_state.write_mode = true;
repl_state.ots = Some("John Doe".to_string());
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(repl_state)),
working_info: None,
sync_thread: None,
};
audit
.add_entry("INSERT INTO test VALUES (1);".to_string())
.unwrap();
}
let audit = Audit::open_file(&db_path).unwrap();
let options = QueryOptions::default();
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 1);
let entry_with_ts =
AuditEntryWithTimestamp::from_entry_and_timestamp(entries[0].1.clone(), entries[0].0);
let json = serde_json::to_string(&entry_with_ts).unwrap();
assert!(json.contains("\"ts\":"));
assert!(json.contains("INSERT INTO test VALUES (1);"));
assert!(json.contains("\"db_user\":\"testdb\""));
assert!(json.contains("\"sys_user\":\"testuser\""));
assert!(json.contains("John Doe"));
assert!(!json.contains("\n"));
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert!(parsed["ts"].is_string());
let ts_str = parsed["ts"].as_str().unwrap();
assert!(ts_str.parse::<jiff::Timestamp>().is_ok());
}
#[test]
fn test_audit_export_default_limit() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(
bestool_psql::repl::ReplState::new(),
)),
working_info: None,
sync_thread: None,
};
for i in 0..150 {
audit.add_entry(format!("SELECT {};", i)).unwrap();
std::thread::sleep(std::time::Duration::from_micros(10));
}
}
let audit = Audit::open_file(&db_path).unwrap();
let options = QueryOptions {
limit: Some(100),
..Default::default()
};
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 100);
assert_eq!(entries[0].1.query, "SELECT 50;");
assert_eq!(entries[99].1.query, "SELECT 149;");
}
#[test]
fn test_audit_export_unlimited() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(
bestool_psql::repl::ReplState::new(),
)),
working_info: None,
sync_thread: None,
};
for i in 0..150 {
audit.add_entry(format!("SELECT {};", i)).unwrap();
std::thread::sleep(std::time::Duration::from_micros(10));
}
}
let audit = Audit::open_file(&db_path).unwrap();
let options = QueryOptions {
limit: Some(0),
..Default::default()
};
let entries = audit.query(&options).unwrap();
assert_eq!(entries.len(), 150);
assert_eq!(entries[0].1.query, "SELECT 0;");
assert_eq!(entries[149].1.query, "SELECT 149;");
}
#[test]
fn test_audit_find_orphans() {
let temp_dir = tempfile::tempdir().unwrap();
let orphan_path = temp_dir.path().join("audit-working-test-orphan.redb");
std::fs::write(&orphan_path, b"fake data").unwrap();
let old_time = std::time::SystemTime::now() - std::time::Duration::from_secs(3600);
filetime::set_file_mtime(&orphan_path, filetime::FileTime::from_system_time(old_time)).unwrap();
let main_path = temp_dir.path().join("audit-main.redb");
std::fs::write(&main_path, b"fake main").unwrap();
let orphans = Audit::find_orphans(temp_dir.path()).unwrap();
assert_eq!(orphans.len(), 1);
assert!(
orphans[0]
.file_name()
.unwrap()
.to_str()
.unwrap()
.contains("working")
);
}
#[test]
fn test_audit_open_file() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
{
let db = redb::Database::create(&db_path).unwrap();
let mut audit = Audit {
db: std::sync::Arc::new(db),
repl_state: std::sync::Arc::new(std::sync::Mutex::new(
bestool_psql::repl::ReplState::new(),
)),
working_info: None,
sync_thread: None,
};
audit.add_entry("SELECT 1;".to_string()).unwrap();
audit.add_entry("SELECT 2;".to_string()).unwrap();
}
let audit = Audit::open_file(&db_path).unwrap();
let entries = audit.query(&QueryOptions::default()).unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].1.query, "SELECT 1;");
assert_eq!(entries[1].1.query, "SELECT 2;");
}