Files
nx9-wg/crates/nx9-wg-db/tests/test_admin_repository.rs
T
thakaresandCopilot 2ac6c81dfe cli: avoid data-dir initialization for version; create db parent dirs; redact generated passwords in CLI output
- 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>
2026-08-16 16:26:24 +05:30

81 lines
3.0 KiB
Rust

//! Tests for Administrator repository operations and security invariants.
use nx9_wg_core::crypto::{hash_password, verify_password};
use nx9_wg_db::Store;
#[tokio::test]
async fn test_admin_single_identity_and_crud() {
let store = Store::connect_in_memory().await.expect("connect");
store.migrate().await.expect("migrate");
// Initially no admin exists
assert!(!store.admin_exists().await.expect("admin_exists"));
assert!(store.get_admin().await.expect("get_admin").is_none());
// Create single admin with Argon2id hash
let password = "CorrectHorseBatteryStaple123!";
let password_hash = hash_password(password).expect("hash password");
let admin = store
.create_admin("admin", &password_hash)
.await
.expect("create_admin");
assert_eq!(admin.id, 1);
assert_eq!(admin.username, "admin");
assert!(!admin.totp_enabled);
assert!(admin.last_login_at.is_none());
// Verify admin_exists returns true
assert!(store.admin_exists().await.expect("admin_exists"));
// Verify lookup by username
let fetched = store
.get_admin_by_username("admin")
.await
.expect("get_admin_by_username")
.expect("admin found");
assert_eq!(fetched.id, 1);
assert!(verify_password(password, &fetched.password_hash).expect("verify password"));
// Reject second admin creation
let second_res = store.create_admin("admin2", "hash2").await;
assert!(second_res.is_err(), "second admin must be rejected");
// Test password change
let new_password = "NewSuperSecurePassword456!";
let new_hash = hash_password(new_password).expect("new hash");
store
.update_admin_password(&new_hash)
.await
.expect("update_admin_password");
let updated = store.get_admin().await.expect("get_admin").expect("admin");
assert!(verify_password(new_password, &updated.password_hash).expect("verify new"));
assert!(!verify_password(password, &updated.password_hash).expect("old password fails"));
// Test TOTP update
store
.update_admin_totp(Some("JBSWY3DPEHPK3PXP"), true)
.await
.expect("update_admin_totp");
let totp_admin = store.get_admin().await.expect("get_admin").expect("admin");
assert!(totp_admin.totp_enabled);
assert_eq!(totp_admin.totp_secret.as_deref(), Some("JBSWY3DPEHPK3PXP"));
// Test recording login
store
.record_admin_login(Some("192.168.1.100"))
.await
.expect("record_admin_login");
let login_admin = store.get_admin().await.expect("get_admin").expect("admin");
assert!(login_admin.last_login_at.is_some());
assert_eq!(login_admin.last_login_ip.as_deref(), Some("192.168.1.100"));
// Verify Debug formatting redacts password_hash and totp_secret
let debug_str = format!("{:?}", login_admin);
assert!(debug_str.contains("[REDACTED]"));
assert!(!debug_str.contains(password));
assert!(!debug_str.contains(new_password));
assert!(!debug_str.contains("JBSWY3DPEHPK3PXP"));
}