Initial public release v0.1.0
This commit is contained in:
commit
6c39e0bfbf
87 files changed
+10867
No files matched your search
@@ -0,0 +1,287 @@
|
||||
use nx9_auth::cli::{Commands, run};
|
||||
use nx9_auth::config::Config;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn setup_test_db(db_path: &str) {
|
||||
let _ = fs::remove_file(db_path);
|
||||
}
|
||||
|
||||
fn teardown_test_db(db_path: &str) {
|
||||
let _ = fs::remove_file(db_path);
|
||||
let _ = fs::remove_file(format!("{}-wal", db_path));
|
||||
let _ = fs::remove_file(format!("{}-shm", db_path));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_path_expansion() {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/home/user".to_string());
|
||||
|
||||
let mut config = Config::default();
|
||||
config.database.path = "~/test_subdir/test.db".to_string();
|
||||
config.resolve_paths();
|
||||
|
||||
let expected = Path::new(&home).join("test_subdir/test.db");
|
||||
assert_eq!(config.database.path, expected.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_backup_validation_and_integrity() {
|
||||
let db_path = "test_cli_backup.db";
|
||||
setup_test_db(db_path);
|
||||
|
||||
let mut config = Config::default();
|
||||
config.database.path = db_path.to_string();
|
||||
|
||||
// 1. Initialize DB and run migrations
|
||||
let pool = nx9_auth::db::create_pool(db_path).await.unwrap();
|
||||
nx9_auth::db::run_migrations(&pool).await.unwrap();
|
||||
|
||||
// 2. Validate backup safety rejects active DB
|
||||
let res = run(
|
||||
Commands::Backup {
|
||||
path: PathBuf::from(db_path),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
assert!(
|
||||
res.unwrap_err()
|
||||
.to_string()
|
||||
.contains("Backup destination cannot be the active database file")
|
||||
);
|
||||
|
||||
// Reject WAL file
|
||||
let wal_path = format!("{}-wal", db_path);
|
||||
let res = run(
|
||||
Commands::Backup {
|
||||
path: PathBuf::from(&wal_path),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
assert!(
|
||||
res.unwrap_err()
|
||||
.to_string()
|
||||
.contains("Backup destination cannot be the active WAL file")
|
||||
);
|
||||
|
||||
// Reject SHM file
|
||||
let shm_path = format!("{}-shm", db_path);
|
||||
let res = run(
|
||||
Commands::Backup {
|
||||
path: PathBuf::from(&shm_path),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_err());
|
||||
assert!(
|
||||
res.unwrap_err()
|
||||
.to_string()
|
||||
.contains("Backup destination cannot be the active SHM file")
|
||||
);
|
||||
|
||||
// 3. Test successful backup
|
||||
let backup_path = "test_cli_backup_dest.db";
|
||||
let _ = fs::remove_file(backup_path);
|
||||
|
||||
let res = run(
|
||||
Commands::Backup {
|
||||
path: PathBuf::from(backup_path),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
assert!(Path::new(backup_path).exists());
|
||||
|
||||
// 4. Verify integrity of the backup database
|
||||
let backup_pool = nx9_auth::db::create_pool(backup_path).await.unwrap();
|
||||
let integrity: (String,) = sqlx::query_as("PRAGMA integrity_check")
|
||||
.fetch_one(&backup_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(integrity.0, "ok");
|
||||
|
||||
// Clean up
|
||||
teardown_test_db(db_path);
|
||||
teardown_test_db(backup_path);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cli_config_path_json() {
|
||||
let mut config = Config::default();
|
||||
config.database.path = "test.db".to_string();
|
||||
|
||||
let res = run(Commands::ConfigPath { json: true }, config.clone()).await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
let res = run(Commands::ConfigPath { json: false }, config).await;
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cli_init_non_interactive() {
|
||||
let db_path = "test_cli_init.db";
|
||||
|
||||
// Clean up
|
||||
let _ = fs::remove_file(db_path);
|
||||
|
||||
let mut config = Config::default();
|
||||
config.database.path = db_path.to_string();
|
||||
|
||||
// Run init command in non-interactive mode
|
||||
let res = run(
|
||||
Commands::Init {
|
||||
non_interactive: true,
|
||||
skip_admin: false,
|
||||
force: false,
|
||||
admin_user: Some("init_admin".to_string()),
|
||||
admin_password: Some("S3cur3#P@ssw0rd$N0S3qu3nc3!".to_string()),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(ref e) = res {
|
||||
println!("INIT ERROR: {:?}", e);
|
||||
}
|
||||
assert!(res.is_ok());
|
||||
|
||||
// Verify DB exists
|
||||
assert!(Path::new(db_path).exists());
|
||||
|
||||
// Verify admin user is created in database
|
||||
let pool = nx9_auth::db::create_pool(db_path).await.unwrap();
|
||||
let admin_exists = nx9_auth::db::repository::users::username_exists(
|
||||
&pool,
|
||||
nx9_auth::db::models::Tenant::DEFAULT_ID,
|
||||
"init_admin",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(admin_exists);
|
||||
|
||||
// Clean up
|
||||
teardown_test_db(db_path);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cli_init_skip_admin() {
|
||||
let db_path = "test_cli_init_skip_admin.db";
|
||||
|
||||
// Clean up
|
||||
let _ = fs::remove_file(db_path);
|
||||
|
||||
let mut config = Config::default();
|
||||
config.database.path = db_path.to_string();
|
||||
|
||||
// Run init command with skip_admin
|
||||
let res = run(
|
||||
Commands::Init {
|
||||
non_interactive: true,
|
||||
skip_admin: true,
|
||||
force: false,
|
||||
admin_user: None,
|
||||
admin_password: None,
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(res.is_ok());
|
||||
|
||||
// Verify DB exists
|
||||
assert!(Path::new(db_path).exists());
|
||||
|
||||
// Verify no admin users exist
|
||||
let pool = nx9_auth::db::create_pool(db_path).await.unwrap();
|
||||
let admin_count = nx9_auth::db::repository::users::count_admins(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(admin_count, 0);
|
||||
|
||||
// Clean up
|
||||
teardown_test_db(db_path);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cli_show_user_and_token() {
|
||||
let db_path = "test_cli_show.db";
|
||||
setup_test_db(db_path);
|
||||
|
||||
let mut config = Config::default();
|
||||
config.database.path = db_path.to_string();
|
||||
|
||||
// 1. Init DB and seed user
|
||||
let pool = nx9_auth::db::create_pool(db_path).await.unwrap();
|
||||
nx9_auth::db::run_migrations(&pool).await.unwrap();
|
||||
|
||||
let user = nx9_auth::identity::users::create_user(
|
||||
&pool,
|
||||
&config.security,
|
||||
nx9_auth::db::models::Tenant::DEFAULT_ID,
|
||||
"show_test_user",
|
||||
"S3cur3#P@ssw0rd$N0S3qu3nc3!",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Assign role
|
||||
nx9_auth::identity::roles::assign_role(&pool, &user.id, "viewer", None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Create a token
|
||||
let (token, _raw) = nx9_auth::security::tokens::create_token(
|
||||
&pool,
|
||||
&user.id,
|
||||
"test-token",
|
||||
&config.security,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// 2. Run show-user command
|
||||
let res = run(
|
||||
Commands::ShowUser {
|
||||
id_or_username: "show_test_user".to_string(),
|
||||
permissions: true,
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
let res = run(
|
||||
Commands::ShowUser {
|
||||
id_or_username: user.id.clone(),
|
||||
permissions: false,
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
// 3. Run show-token command
|
||||
let res = run(
|
||||
Commands::ShowToken {
|
||||
id: token.id.clone(),
|
||||
},
|
||||
config.clone(),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
// Clean up
|
||||
teardown_test_db(db_path);
|
||||
}
|
||||
Reference in new issue
Block a user