- Prevent 'nx9-wg version' from creating data directories by avoiding database initialization. - Create parent directories when an explicit --database path is provided. - Redact printed generated administrator passwords; announce file path or redact instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
228 lines
6.1 KiB
Rust
228 lines
6.1 KiB
Rust
//! Tests for Settings, Audit Log, and Backup repositories.
|
|
|
|
use chrono::Utc;
|
|
use nx9_wg_core::types::audit::AuditEventType;
|
|
use nx9_wg_core::types::backup::BackupMeta;
|
|
use nx9_wg_db::{AuditFilter, Store};
|
|
use uuid::Uuid;
|
|
|
|
#[tokio::test]
|
|
async fn test_settings_repository() {
|
|
let store = Store::connect_in_memory().await.expect("connect");
|
|
store.migrate().await.expect("migrate");
|
|
|
|
// Initially missing key returns None
|
|
assert!(
|
|
store
|
|
.get_setting("non_existent")
|
|
.await
|
|
.expect("get")
|
|
.is_none()
|
|
);
|
|
assert!(
|
|
store
|
|
.get_setting_value("non_existent")
|
|
.await
|
|
.expect("get val")
|
|
.is_none()
|
|
);
|
|
|
|
// Set normal setting
|
|
store
|
|
.set_setting("server_endpoint", "vpn.example.com:51820", false)
|
|
.await
|
|
.expect("set");
|
|
let ep = store
|
|
.get_setting("server_endpoint")
|
|
.await
|
|
.expect("get")
|
|
.expect("setting found");
|
|
assert_eq!(ep.value, "vpn.example.com:51820");
|
|
assert!(!ep.is_secret);
|
|
|
|
// Set secret setting
|
|
store
|
|
.set_setting("session_secret", "SuperSecretKey999", true)
|
|
.await
|
|
.expect("set secret");
|
|
let sec = store
|
|
.get_setting("session_secret")
|
|
.await
|
|
.expect("get")
|
|
.expect("setting found");
|
|
assert_eq!(sec.value, "SuperSecretKey999");
|
|
assert!(sec.is_secret);
|
|
|
|
// Verify Debug formatting of secret setting redacts value
|
|
let sec_debug = format!("{:?}", sec);
|
|
assert!(sec_debug.contains("[REDACTED]"));
|
|
assert!(!sec_debug.contains("SuperSecretKey999"));
|
|
|
|
// Upsert existing setting
|
|
store
|
|
.set_setting("server_endpoint", "vpn2.example.com:51820", false)
|
|
.await
|
|
.expect("upsert");
|
|
let ep2 = store
|
|
.get_setting_value("server_endpoint")
|
|
.await
|
|
.expect("get")
|
|
.expect("value found");
|
|
assert_eq!(ep2, "vpn2.example.com:51820");
|
|
|
|
// List settings
|
|
let all = store.list_settings().await.expect("list");
|
|
assert_eq!(all.len(), 2);
|
|
|
|
// Delete setting
|
|
store
|
|
.delete_setting("server_endpoint")
|
|
.await
|
|
.expect("delete");
|
|
assert!(
|
|
store
|
|
.get_setting("server_endpoint")
|
|
.await
|
|
.expect("get")
|
|
.is_none()
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_audit_log_append_only_and_filtering() {
|
|
let store = Store::connect_in_memory().await.expect("connect");
|
|
store.migrate().await.expect("migrate");
|
|
|
|
// Record various events
|
|
store
|
|
.record_audit(
|
|
AuditEventType::Login,
|
|
"admin",
|
|
Some("session"),
|
|
Some("sess-1"),
|
|
Some("Admin login succeeded"),
|
|
None,
|
|
Some("192.168.1.50"),
|
|
)
|
|
.await
|
|
.expect("record login");
|
|
|
|
store
|
|
.record_audit(
|
|
AuditEventType::InterfaceCreate,
|
|
"admin",
|
|
Some("interface"),
|
|
Some("wg0"),
|
|
Some("Interface wg0 created"),
|
|
None,
|
|
Some("192.168.1.50"),
|
|
)
|
|
.await
|
|
.expect("record iface create");
|
|
|
|
store
|
|
.record_audit(
|
|
AuditEventType::PeerCreate,
|
|
"admin",
|
|
Some("peer"),
|
|
Some("peer-alice"),
|
|
Some("Peer alice created"),
|
|
None,
|
|
Some("192.168.1.50"),
|
|
)
|
|
.await
|
|
.expect("record peer create");
|
|
|
|
// Total count
|
|
let total = store
|
|
.count_audit_events(&AuditFilter::default())
|
|
.await
|
|
.expect("count");
|
|
assert_eq!(total, 3);
|
|
|
|
// Filter by event_type
|
|
let login_filter = AuditFilter {
|
|
event_type: Some(AuditEventType::Login),
|
|
..Default::default()
|
|
};
|
|
let login_events = store
|
|
.list_audit_events(&login_filter, 10, 0)
|
|
.await
|
|
.expect("list login");
|
|
assert_eq!(login_events.len(), 1);
|
|
assert_eq!(login_events[0].event_type, AuditEventType::Login);
|
|
|
|
// Filter by resource_type
|
|
let peer_filter = AuditFilter {
|
|
resource_type: Some("peer".to_string()),
|
|
..Default::default()
|
|
};
|
|
let peer_events = store
|
|
.list_audit_events(&peer_filter, 10, 0)
|
|
.await
|
|
.expect("list peer events");
|
|
assert_eq!(peer_events.len(), 1);
|
|
assert_eq!(peer_events[0].resource_id.as_deref(), Some("peer-alice"));
|
|
|
|
// Pagination test: limit 2, offset 0 -> 2 items; offset 2 -> 1 item
|
|
let page1 = store
|
|
.list_audit_events(&AuditFilter::default(), 2, 0)
|
|
.await
|
|
.expect("page1");
|
|
assert_eq!(page1.len(), 2);
|
|
|
|
let page2 = store
|
|
.list_audit_events(&AuditFilter::default(), 2, 2)
|
|
.await
|
|
.expect("page2");
|
|
assert_eq!(page2.len(), 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_backup_metadata_crud() {
|
|
let store = Store::connect_in_memory().await.expect("connect");
|
|
store.migrate().await.expect("migrate");
|
|
|
|
let now = Utc::now().naive_utc();
|
|
let backup_id = Uuid::new_v4();
|
|
|
|
let meta = BackupMeta {
|
|
id: backup_id,
|
|
filename: "nx9-wg-backup-20260816.tar.gz".to_string(),
|
|
size_bytes: 1048576,
|
|
checksum: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
|
.to_string(),
|
|
schema_version: "1".to_string(),
|
|
encrypted: true,
|
|
description: Some("Automated nightly backup".to_string()),
|
|
created_at: now,
|
|
};
|
|
|
|
store
|
|
.create_backup_meta(&meta)
|
|
.await
|
|
.expect("create_backup_meta");
|
|
|
|
let fetched = store
|
|
.get_backup_meta(backup_id)
|
|
.await
|
|
.expect("get")
|
|
.expect("backup found");
|
|
assert_eq!(fetched.filename, "nx9-wg-backup-20260816.tar.gz");
|
|
assert_eq!(fetched.size_bytes, 1048576);
|
|
assert!(fetched.encrypted);
|
|
assert_eq!(fetched.schema_version, "1");
|
|
|
|
let list = store.list_backups().await.expect("list");
|
|
assert_eq!(list.len(), 1);
|
|
|
|
store.delete_backup_meta(backup_id).await.expect("delete");
|
|
assert!(
|
|
store
|
|
.get_backup_meta(backup_id)
|
|
.await
|
|
.expect("get")
|
|
.is_none()
|
|
);
|
|
}
|