Files
nx9-url-shortener/tests/config_precedence_tests.rs
T
thakares c2e138f823
Rust CI / Test & Quality Checks (push) Canceled after 0s
Rust CI / Build Docker Image (push) Canceled after 0s
refactor(config): require explicit BZOD data directory
2026-08-27 15:06:05 +05:30

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")
);
}