223 lines
7.2 KiB
Rust
223 lines
7.2 KiB
Rust
use bzod::config::{Config, ConfigError, NX9_BZOD_DATA_DIR_ENV};
|
|
use bzod::db::topology::Topology;
|
|
use bzod::identity::TenantId;
|
|
use std::env;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::Mutex;
|
|
|
|
// Global lock to prevent race conditions during env-var testing
|
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
|
|
|
struct EnvGuard {
|
|
key: &'static str,
|
|
original_val: Option<String>,
|
|
}
|
|
|
|
impl EnvGuard {
|
|
fn set(key: &'static str, val: &str) -> Self {
|
|
let original_val = env::var(key).ok();
|
|
env::set_var(key, val);
|
|
Self { key, original_val }
|
|
}
|
|
|
|
fn unset(key: &'static str) -> Self {
|
|
let original_val = env::var(key).ok();
|
|
env::remove_var(key);
|
|
Self { key, original_val }
|
|
}
|
|
}
|
|
|
|
impl Drop for EnvGuard {
|
|
fn drop(&mut self) {
|
|
if let Some(ref val) = self.original_val {
|
|
env::set_var(self.key, val);
|
|
} else {
|
|
env::remove_var(self.key);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_no_configuration_returns_missing_data_dir_error() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let _guard = EnvGuard::unset(NX9_BZOD_DATA_DIR_ENV);
|
|
|
|
// Call load_from_sources with no CLI and load_dotenv=false in an isolated context
|
|
let res = Config::load_from_sources(None::<&Path>, false);
|
|
|
|
assert!(
|
|
res.is_err(),
|
|
"Expected error when no data directory is configured"
|
|
);
|
|
match res.err().unwrap() {
|
|
ConfigError::MissingDataDir => {}
|
|
other => panic!("Expected ConfigError::MissingDataDir, got {:?}", other),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_environment_variable_resolves_data_dir() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let test_path = "/tmp/nx9-env-test-data";
|
|
let _guard = EnvGuard::set(NX9_BZOD_DATA_DIR_ENV, test_path);
|
|
|
|
let config = Config::load_from_sources(None::<&Path>, false)
|
|
.expect("Config should load with NX9_BZOD_DATA_DIR set");
|
|
|
|
assert_eq!(config.data_dir, PathBuf::from(test_path));
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_file_resolves_data_dir() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let _guard = EnvGuard::unset(NX9_BZOD_DATA_DIR_ENV);
|
|
|
|
let temp_dir = std::env::temp_dir().join(format!("bzod_cfg_test_{}", uuid::Uuid::new_v4()));
|
|
fs::create_dir_all(&temp_dir).unwrap();
|
|
|
|
let toml_file = temp_dir.join("bzod.toml");
|
|
let target_data_dir = "/tmp/config-file-data";
|
|
fs::write(&toml_file, format!("data_dir = \"{}\"\n", target_data_dir)).unwrap();
|
|
|
|
// Change current directory temporarily for config file reading
|
|
let original_cwd = env::current_dir().unwrap();
|
|
env::set_current_dir(&temp_dir).unwrap();
|
|
|
|
let config_res = Config::load_from_sources(None::<&Path>, false);
|
|
|
|
// Restore cwd
|
|
env::set_current_dir(original_cwd).unwrap();
|
|
let _ = fs::remove_dir_all(&temp_dir);
|
|
|
|
let config = config_res.expect("Config should load from bzod.toml");
|
|
assert_eq!(config.data_dir, PathBuf::from(target_data_dir));
|
|
}
|
|
|
|
#[test]
|
|
fn test_environment_overrides_config_file() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let env_data_dir = "/tmp/env-override-data";
|
|
let _guard = EnvGuard::set(NX9_BZOD_DATA_DIR_ENV, env_data_dir);
|
|
|
|
let temp_dir = std::env::temp_dir().join(format!("bzod_cfg_test_{}", uuid::Uuid::new_v4()));
|
|
fs::create_dir_all(&temp_dir).unwrap();
|
|
|
|
let toml_file = temp_dir.join("bzod.toml");
|
|
let config_data_dir = "/tmp/config-file-data";
|
|
fs::write(&toml_file, format!("data_dir = \"{}\"\n", config_data_dir)).unwrap();
|
|
|
|
let original_cwd = env::current_dir().unwrap();
|
|
env::set_current_dir(&temp_dir).unwrap();
|
|
|
|
let config_res = Config::load_from_sources(None::<&Path>, false);
|
|
|
|
env::set_current_dir(original_cwd).unwrap();
|
|
let _ = fs::remove_dir_all(&temp_dir);
|
|
|
|
let config = config_res.expect("Config should load");
|
|
assert_eq!(config.data_dir, PathBuf::from(env_data_dir));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_overrides_environment() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let env_data_dir = "/tmp/env-data";
|
|
let _guard = EnvGuard::set(NX9_BZOD_DATA_DIR_ENV, env_data_dir);
|
|
|
|
let cli_data_dir = "/tmp/cli-override-data";
|
|
let config = Config::load_from_sources(Some(cli_data_dir), false)
|
|
.expect("Config should load with CLI override");
|
|
|
|
assert_eq!(config.data_dir, PathBuf::from(cli_data_dir));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_overrides_config_file() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let _guard = EnvGuard::unset(NX9_BZOD_DATA_DIR_ENV);
|
|
|
|
let temp_dir = std::env::temp_dir().join(format!("bzod_cfg_test_{}", uuid::Uuid::new_v4()));
|
|
fs::create_dir_all(&temp_dir).unwrap();
|
|
|
|
let toml_file = temp_dir.join("bzod.toml");
|
|
let config_data_dir = "/tmp/config-file-data";
|
|
fs::write(&toml_file, format!("data_dir = \"{}\"\n", config_data_dir)).unwrap();
|
|
|
|
let original_cwd = env::current_dir().unwrap();
|
|
env::set_current_dir(&temp_dir).unwrap();
|
|
|
|
let cli_data_dir = "/tmp/cli-data-highest";
|
|
let config_res = Config::load_from_sources(Some(cli_data_dir), false);
|
|
|
|
env::set_current_dir(original_cwd).unwrap();
|
|
let _ = fs::remove_dir_all(&temp_dir);
|
|
|
|
let config = config_res.expect("Config should load with CLI override");
|
|
assert_eq!(config.data_dir, PathBuf::from(cli_data_dir));
|
|
}
|
|
|
|
#[test]
|
|
fn test_relative_path_semantics_preserved() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
let relative_path = "custom_relative/data";
|
|
let config = Config::load_from_sources(Some(relative_path), false)
|
|
.expect("Relative path in CLI should be accepted as-is without forced canonicalization");
|
|
|
|
assert_eq!(config.data_dir, PathBuf::from(relative_path));
|
|
}
|
|
|
|
#[test]
|
|
fn test_topology_resolves_all_databases_relative_to_configured_root() {
|
|
let custom_root = PathBuf::from("/var/lib/bzod/custom_root");
|
|
let topology = Topology::new(&custom_root);
|
|
|
|
assert_eq!(topology.root(), &custom_root);
|
|
assert_eq!(topology.admin_dir(), custom_root.join("admin"));
|
|
assert_eq!(topology.slugs_dir(), custom_root.join("slugs"));
|
|
assert_eq!(topology.users_dir(), custom_root.join("users"));
|
|
|
|
// Core databases
|
|
assert_eq!(topology.admin_db(), custom_root.join("admin/admin.db"));
|
|
assert_eq!(topology.system_db(), custom_root.join("admin/system.db"));
|
|
assert_eq!(
|
|
topology.users_registry_db(),
|
|
custom_root.join("admin/users.db")
|
|
);
|
|
assert_eq!(
|
|
topology.global_urls_db(),
|
|
custom_root.join("slugs/global_urls.db")
|
|
);
|
|
assert_eq!(
|
|
topology.global_landing_pages_db(),
|
|
custom_root.join("slugs/global_landing_pages.db")
|
|
);
|
|
assert_eq!(
|
|
topology.reserved_db(),
|
|
custom_root.join("slugs/reserved.db")
|
|
);
|
|
|
|
// Tenant databases
|
|
let tenant_id = TenantId::parse("a1b2c3d4e5f6").unwrap();
|
|
assert_eq!(
|
|
topology.tenant_dir(tenant_id),
|
|
custom_root.join("users/a1b2c3d4e5f6")
|
|
);
|
|
assert_eq!(
|
|
topology.tenant_content_db(tenant_id),
|
|
custom_root.join("users/a1b2c3d4e5f6/content.db")
|
|
);
|
|
assert_eq!(
|
|
topology.tenant_analytics_db(tenant_id),
|
|
custom_root.join("users/a1b2c3d4e5f6/analytics.db")
|
|
);
|
|
assert_eq!(
|
|
topology.tenant_profile_db(tenant_id),
|
|
custom_root.join("users/a1b2c3d4e5f6/profile.db")
|
|
);
|
|
assert_eq!(
|
|
topology.extension_db("a1b2c3d4e5f6", "cv").unwrap(),
|
|
custom_root.join("users/a1b2c3d4e5f6/extensions/cv/cv.db")
|
|
);
|
|
}
|