From c2e138f8236de8bbd7454e95eb99a8dad4f97919 Mon Sep 17 00:00:00 2001 From: Sunil Thakare Date: Thu, 27 Aug 2026 15:06:05 +0530 Subject: [PATCH] refactor(config): require explicit BZOD data directory --- .env.example | 4 +- Dockerfile | 2 +- README.md | 34 ++- deploy.sh | 4 +- docker-compose.yml | 2 +- docs/CLI.md | 24 ++ docs/DOCKER-Deploy.md | 16 +- src/cli/mod.rs | 31 +++ src/cli/restore.rs | 3 +- src/config.rs | 208 ++++++++++++----- src/db/mod.rs | 5 +- src/db/topology.rs | 2 +- src/main.rs | 8 +- tests/admin_dashboard_tests.rs | 3 +- tests/admin_user_management_tests.rs | 5 +- tests/analytics_parity_tests.rs | 3 +- tests/auth_migration_tests.rs | 3 +- tests/auth_tests.rs | 3 +- tests/backup_restore_tests.rs | 6 +- tests/business_workflow_tests.rs | 3 +- tests/concurrency_tests.rs | 3 +- tests/config_precedence_tests.rs | 222 +++++++++++++++++++ tests/disaster_recovery_tests.rs | 3 +- tests/http_e2e_tests.rs | 3 +- tests/init_admin_tests.rs | 3 +- tests/integrity_tests.rs | 3 +- tests/legacy_restore_tests.rs | 3 +- tests/migration_tests.rs | 3 +- tests/moderation_tests.rs | 3 +- tests/next_features_tests.rs | 3 +- tests/ownership_tests.rs | 3 +- tests/qr_endpoint_tests.rs | 3 +- tests/quota_tests.rs | 3 +- tests/redirect_security_tests.rs | 3 +- tests/registry_repair_tests.rs | 3 +- tests/routing_tests.rs | 3 +- tests/slug_namespace_tests.rs | 3 +- tests/slug_registry_tests.rs | 3 +- tests/slug_transfer_tests.rs | 3 +- tests/soft_delete_tests.rs | 3 +- tests/transaction_tests.rs | 3 +- tests/upgrade_validation_tests.rs | 3 +- tests/user_deletion_tests.rs | 3 +- tests/user_isolation_tests.rs | 3 +- tests/user_management_tests.rs | 3 +- tests/v08_admin_capability_boundary_tests.rs | 3 +- tests/v08_admin_core_separation_tests.rs | 3 +- tests/v08_admin_user_route_boundary_tests.rs | 3 +- tests/v08_foundation_tests.rs | 3 +- tests/v08_identity_migration_tests.rs | 3 +- tests/v08_phase6a_elimination_tests.rs | 3 +- tests/v08_slug_migration_tests.rs | 3 +- tests/v08_tenant_boundary_tests.rs | 3 +- tests/wal_recovery_tests.rs | 3 +- www/deploy.sh | 2 +- 55 files changed, 522 insertions(+), 170 deletions(-) create mode 100644 tests/config_precedence_tests.rs diff --git a/.env.example b/.env.example index 63ebc1f..27ca0e3 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,9 @@ # BZOD Platform Configuration HOST=0.0.0.0 PORT=8080 -DATA_DIR=./data +# Physical BZOD data root. +# REQUIRED: Set this explicitly; there is no implicit ./data fallback. +NX9_BZOD_DATA_DIR=/var/lib/bzod/data # Security Settings COOKIE_SECURE=false diff --git a/Dockerfile b/Dockerfile index f6fe111..4760251 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,7 +73,7 @@ RUN mkdir -p \ /usr/local/bin/docker-entrypoint.sh # Runtime configuration -ENV DATA_DIR=/app/data \ +ENV NX9_BZOD_DATA_DIR=/app/data \ CONFIG_DIR=/app/config \ IMAGES_DIR=/app/images \ PORT=8654 \ diff --git a/README.md b/README.md index 80b9ca9..62cae98 100644 --- a/README.md +++ b/README.md @@ -168,9 +168,10 @@ services: - "8654:8654" volumes: - - ./data:/app/data + - /var/lib/bzod/data:/app/data environment: + - NX9_BZOD_DATA_DIR=/app/data - RUST_LOG=info ``` @@ -182,6 +183,28 @@ docker compose up -d --- +# Data Directory & Configuration Precedence + +BZOD requires an explicit physical data root. There is **no implicit `./data` fallback**. + +Precedence: +1. **CLI `--data-dir`** (Highest) +2. **`NX9_BZOD_DATA_DIR`** environment variable +3. **`config.toml` / `bzod.toml` `data_dir`** setting +4. **Configuration Error** if no data directory is configured. + +Examples: + +```bash +# Via environment variable +NX9_BZOD_DATA_DIR=/var/lib/bzod/data bzod serve + +# Via CLI argument +bzod migrate --data-dir=/var/lib/bzod/data --dry-run +``` + +--- + # Native Installation Download the latest release. @@ -202,16 +225,16 @@ bzod --help # Initialize -Create the administrator. +Create the administrator (specifying `--data-dir` or `NX9_BZOD_DATA_DIR`): ```bash -bzod create-admin +NX9_BZOD_DATA_DIR=/var/lib/bzod/data bzod create-admin ``` -Start the server. +Start the server: ```bash -bzod serve +NX9_BZOD_DATA_DIR=/var/lib/bzod/data bzod serve ``` Default server: @@ -234,6 +257,7 @@ After=network.target [Service] +Environment=NX9_BZOD_DATA_DIR=/var/lib/bzod/data ExecStart=/usr/local/bin/bzod serve Restart=always diff --git a/deploy.sh b/deploy.sh index 55bee07..1738988 100755 --- a/deploy.sh +++ b/deploy.sh @@ -180,7 +180,7 @@ BZOD_IMAGE=${BZOD_IMAGE} HOST=0.0.0.0 PORT=8654 -DATA_DIR=/app/data +NX9_BZOD_DATA_DIR=/app/data CONFIG_DIR=/app/config IMAGES_DIR=/app/images @@ -238,7 +238,7 @@ services: HOST: "${HOST:-0.0.0.0}" PORT: "${PORT:-8654}" - DATA_DIR: "/app/data" + NX9_BZOD_DATA_DIR: "/app/data" CONFIG_DIR: "/app/config" IMAGES_DIR: "/app/images" diff --git a/docker-compose.yml b/docker-compose.yml index ae5e159..9c6beab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: - ADMIN_USERNAME=${ADMIN_USERNAME} - CONFIG_DIR=/app/config - COOKIE_SECURE=false - - DATA_DIR=/app/data + - NX9_BZOD_DATA_DIR=/app/data - HOST=0.0.0.0 - IMAGES_DIR=/app/images - PORT=8654 diff --git a/docs/CLI.md b/docs/CLI.md index 66c5a40..0855632 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -284,3 +284,27 @@ bzod doctor * SECURITY.md * API.md * ARCHITECTURE.md + +--- + +# Data Directory Configuration + +BZOD requires an explicit physical data directory root. There is **no implicit `./data` fallback**. + +### Configuration Precedence + +1. **CLI `--data-dir`** (Highest priority) + ```bash + bzod serve --data-dir /var/lib/bzod/data + bzod migrate --data-dir=/var/lib/bzod/data --dry-run + ``` +2. **Environment Variable `NX9_BZOD_DATA_DIR`** + ```bash + export NX9_BZOD_DATA_DIR=/var/lib/bzod/data + bzod serve + ``` +3. **Configuration File (`bzod.toml` / `config.toml`)** + ```toml + data_dir = "/var/lib/bzod/data" + ``` +4. **Error**: If no data directory is provided via CLI, `NX9_BZOD_DATA_DIR`, or config file, BZOD terminates with an actionable error. diff --git a/docs/DOCKER-Deploy.md b/docs/DOCKER-Deploy.md index e1e4ba0..feecf31 100644 --- a/docs/DOCKER-Deploy.md +++ b/docs/DOCKER-Deploy.md @@ -100,7 +100,7 @@ services: environment: HOST: 0.0.0.0 PORT: 8654 - DATA_DIR: /app/data + NX9_BZOD_DATA_DIR: /app/data COOKIE_SECURE: "false" healthcheck: @@ -204,13 +204,13 @@ BZOD Docker Container # Environment Variables -| Variable | Description | Default | -| ------------- | ------------------ | --------- | -| HOST | Bind address | 0.0.0.0 | -| PORT | Listen port | 8654 | -| DATA_DIR | Database directory | /app/data | -| COOKIE_SECURE | Secure cookies | false | -| RUST_LOG | Logging level | info | +| Variable | Description | Default | +| ----------------- | ------------------ | ------------- | +| HOST | Bind address | 0.0.0.0 | +| PORT | Listen port | 8654 | +| NX9_BZOD_DATA_DIR | Database directory | (required) | +| COOKIE_SECURE | Secure cookies | false | +| RUST_LOG | Logging level | info | Production recommendation: diff --git a/src/cli/mod.rs b/src/cli/mod.rs index ab0934c..a8d5ff2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -213,3 +213,34 @@ pub enum RepairCommands { data_dir: Option, }, } + +impl Commands { + pub fn data_dir(&self) -> Option<&str> { + match self { + Commands::Serve { data_dir, .. } => data_dir.as_deref(), + Commands::Backup { data_dir, .. } => data_dir.as_deref(), + Commands::Restore { data_dir, .. } => data_dir.as_deref(), + Commands::Migrate { data_dir, .. } => data_dir.as_deref(), + Commands::Stats { data_dir, .. } => data_dir.as_deref(), + Commands::Validate { data_dir, .. } => data_dir.as_deref(), + Commands::AuditDestinations { data_dir, .. } => data_dir.as_deref(), + Commands::CreateAdmin { data_dir, .. } => data_dir.as_deref(), + Commands::InitAdmin { data_dir, .. } => data_dir.as_deref(), + Commands::Doctor { data_dir, .. } => data_dir.as_deref(), + Commands::Shorten { data_dir, .. } => data_dir.as_deref(), + Commands::Expand { data_dir, .. } => data_dir.as_deref(), + Commands::CreateUser { data_dir, .. } => data_dir.as_deref(), + Commands::DeleteUser { data_dir, .. } => data_dir.as_deref(), + Commands::DisableUser { data_dir, .. } => data_dir.as_deref(), + Commands::EnableUser { data_dir, .. } => data_dir.as_deref(), + Commands::ResetPassword { data_dir, .. } => data_dir.as_deref(), + Commands::ListUsers { data_dir, .. } => data_dir.as_deref(), + Commands::BackupUser { data_dir, .. } => data_dir.as_deref(), + Commands::RestoreUser { data_dir, .. } => data_dir.as_deref(), + Commands::AdminMigrate { data_dir, .. } => data_dir.as_deref(), + Commands::Repair { command } => match command { + RepairCommands::Registry { data_dir, .. } => data_dir.as_deref(), + }, + } + } +} diff --git a/src/cli/restore.rs b/src/cli/restore.rs index c22f937..11e52d6 100644 --- a/src/cli/restore.rs +++ b/src/cli/restore.rs @@ -277,8 +277,7 @@ pub fn perform_restore( } // 5. Run validation on the normalized temp_dir - let mut temp_config = Config::load(); - temp_config.data_dir = temp_dir.clone(); + let temp_config = Config::load_with_cli(Some(&temp_dir))?; // Namespace audit match crate::db::users::audit_slug_namespace(&temp_config) { diff --git a/src/config.rs b/src/config.rs index 59eb14e..d6fb587 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,45 @@ use std::env; use std::fs; use std::path::PathBuf; +pub const NX9_BZOD_DATA_DIR_ENV: &str = "NX9_BZOD_DATA_DIR"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConfigError { + MissingDataDir, + InvalidConfig(String), +} + +impl std::fmt::Display for ConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MissingDataDir => write!( + f, + "data directory is not configured; set NX9_BZOD_DATA_DIR or use --data-dir=" + ), + Self::InvalidConfig(msg) => write!(f, "configuration error: {msg}"), + } + } +} + +impl std::error::Error for ConfigError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DataDirSource { + Cli, + Env, + ConfigFile, +} + +impl std::fmt::Display for DataDirSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Cli => write!(f, "CLI"), + Self::Env => write!(f, "NX9_BZOD_DATA_DIR"), + Self::ConfigFile => write!(f, "config.toml"), + } + } +} + #[derive(Clone, Debug)] pub struct Config { pub host: String, @@ -45,11 +84,23 @@ struct TomlBackupConfig { } impl Config { - pub fn load() -> Self { - // 1. Built-in defaults + pub fn load() -> Result { + Self::load_with_cli(None::<&std::path::Path>) + } + + pub fn load_with_cli>( + cli_data_dir: Option

, + ) -> Result { + Self::load_from_sources(cli_data_dir, true) + } + + pub fn load_from_sources>( + cli_data_dir: Option

, + load_dotenv: bool, + ) -> Result { + // 1. Built-in defaults (no implicit data_dir default) let mut host = "0.0.0.0".to_string(); let mut port = 8080u16; - let mut data_dir = PathBuf::from("./data"); let mut admin_username = "admin".to_string(); let mut bootstrap_password_sha256 = "".to_string(); let mut session_secret = @@ -63,71 +114,84 @@ impl Config { let mut backup_dir = PathBuf::from("./backups"); let mut base_url = None; - // 2. Load bzod.toml if it exists - let mut toml_path = "bzod.toml".to_string(); - if fs::metadata("bzod.toml").is_err() && fs::metadata("config/bzod.toml").is_ok() { - toml_path = "config/bzod.toml".to_string(); - } - if let Ok(toml_content) = fs::read_to_string(&toml_path) { - if let Ok(toml_config) = toml::from_str::(&toml_content) { - if let Some(h) = toml_config.host { - host = h; - } - if let Some(p) = toml_config.port { - port = p; - } - if let Some(d) = toml_config.data_dir { - data_dir = PathBuf::from(d); - } - if let Some(u) = toml_config.admin_username { - admin_username = u; - } - if let Some(s) = toml_config.bootstrap_password_sha256 { - bootstrap_password_sha256 = s; - } - if let Some(sec) = toml_config.session_secret { - session_secret = sec; - } - if let Some(c) = toml_config.cookie_secure { - cookie_secure = c; - } - if let Some(ret) = toml_config.data_retention_days { - if ret.eq_ignore_ascii_case("unlimited") { - data_retention_days = None; - } else if let Ok(parsed) = ret.parse::() { - data_retention_days = Some(parsed); + let mut resolved_data_dir: Option = None; + let mut data_dir_source: Option = None; + + // 2. Load bzod.toml / config.toml if it exists + let possible_tomls = [ + "bzod.toml", + "config/bzod.toml", + "config.toml", + "config/config.toml", + ]; + for toml_path in possible_tomls { + if let Ok(toml_content) = fs::read_to_string(toml_path) { + if let Ok(toml_config) = toml::from_str::(&toml_content) { + if let Some(h) = toml_config.host { + host = h; } - } - if let Some(lc) = toml_config.link_check_interval_mins { - link_check_interval_mins = lc; - } - if let Some(ag) = toml_config.aggregation_interval_mins { - aggregation_interval_mins = ag; - } - if let Some(b) = toml_config.backup { - if let Some(be) = b.enabled { - backup_enabled = be; + if let Some(p) = toml_config.port { + port = p; } - if let Some(bi) = b.interval_mins { - backup_interval_mins = bi; + if let Some(d) = toml_config.data_dir { + if !d.trim().is_empty() { + resolved_data_dir = Some(PathBuf::from(d)); + data_dir_source = Some(DataDirSource::ConfigFile); + } } - if let Some(bo) = b.out_dir { - backup_dir = PathBuf::from(bo); + if let Some(u) = toml_config.admin_username { + admin_username = u; } - } - if let Some(bu) = toml_config.base_url { - base_url = Some(bu); + if let Some(s) = toml_config.bootstrap_password_sha256 { + bootstrap_password_sha256 = s; + } + if let Some(sec) = toml_config.session_secret { + session_secret = sec; + } + if let Some(c) = toml_config.cookie_secure { + cookie_secure = c; + } + if let Some(ret) = toml_config.data_retention_days { + if ret.eq_ignore_ascii_case("unlimited") { + data_retention_days = None; + } else if let Ok(parsed) = ret.parse::() { + data_retention_days = Some(parsed); + } + } + if let Some(lc) = toml_config.link_check_interval_mins { + link_check_interval_mins = lc; + } + if let Some(ag) = toml_config.aggregation_interval_mins { + aggregation_interval_mins = ag; + } + if let Some(b) = toml_config.backup { + if let Some(be) = b.enabled { + backup_enabled = be; + } + if let Some(bi) = b.interval_mins { + backup_interval_mins = bi; + } + if let Some(bo) = b.out_dir { + backup_dir = PathBuf::from(bo); + } + } + if let Some(bu) = toml_config.base_url { + base_url = Some(bu); + } + break; } } } // 3. Load .env if present - let _ = dotenvy::dotenv(); - if fs::metadata("config/.env").is_ok() { - let _ = dotenvy::from_path("config/.env"); + if load_dotenv { + let _ = dotenvy::dotenv(); + if fs::metadata("config/.env").is_ok() { + let _ = dotenvy::from_path("config/.env"); + } } - // 4. Load from Environment Variables (taking highest precedence) + // 4. Load from Environment Variables (taking precedence over config file) if let Ok(h) = env::var("HOST") { host = h; } @@ -136,8 +200,11 @@ impl Config { port = p; } } - if let Ok(d_str) = env::var("DATA_DIR") { - data_dir = PathBuf::from(d_str); + if let Ok(d_str) = env::var(NX9_BZOD_DATA_DIR_ENV) { + if !d_str.trim().is_empty() { + resolved_data_dir = Some(PathBuf::from(d_str)); + data_dir_source = Some(DataDirSource::Env); + } } if let Ok(u) = env::var("ADMIN_USERNAME") { admin_username = u; @@ -187,7 +254,26 @@ impl Config { base_url = Some(bu); } - Self { + // 5. CLI override (highest precedence) + if let Some(cli_dir) = cli_data_dir { + let path_ref = cli_dir.as_ref(); + if !path_ref.as_os_str().is_empty() { + resolved_data_dir = Some(path_ref.to_path_buf()); + data_dir_source = Some(DataDirSource::Cli); + } + } + + let data_dir = match resolved_data_dir { + Some(dir) => dir, + None => return Err(ConfigError::MissingDataDir), + }; + + if let Some(source) = data_dir_source { + tracing::info!("Data directory: {}", data_dir.display()); + tracing::info!("Data directory source: {}", source); + } + + Ok(Self { host, port, data_dir, @@ -202,6 +288,6 @@ impl Config { backup_interval_mins, backup_dir, base_url, - } + }) } } diff --git a/src/db/mod.rs b/src/db/mod.rs index 34ea366..d7f9ba6 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -392,12 +392,11 @@ mod db_init_tests { #[test] fn test_db_init() { - let temp_dir = PathBuf::from("./temp_test_db_dir"); + let temp_dir = std::env::temp_dir().join(format!("test_db_init_{}", uuid::Uuid::new_v4())); if temp_dir.exists() { let _ = std::fs::remove_dir_all(&temp_dir); } - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let config = Config::load_with_cli(Some(&temp_dir)).unwrap(); let db = Db::init(&config); // Cleanup diff --git a/src/db/topology.rs b/src/db/topology.rs index a89862c..a728808 100644 --- a/src/db/topology.rs +++ b/src/db/topology.rs @@ -10,7 +10,7 @@ //! └── extensions//.db //! ``` //! -//! `` is `Config.data_dir` (env `DATA_DIR`, default `./data`). +//! `` is `Config.data_dir` (CLI `--data-dir`, env `NX9_BZOD_DATA_DIR`, or config file). //! It is not renamed to `database/`. Production Docker, CasaOS, and //! `deploy.sh` bind this physical root. diff --git a/src/main.rs b/src/main.rs index a18eae6..e7763ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,13 @@ async fn main() -> Result<(), Box> { .init(); let cli = Cli::parse(); - let config = Config::load(); + let config = match Config::load_with_cli(cli.command.data_dir()) { + Ok(config) => config, + Err(err) => { + eprintln!("Error: {err}"); + std::process::exit(1); + } + }; match cli.command { Commands::Serve { diff --git a/tests/admin_dashboard_tests.rs b/tests/admin_dashboard_tests.rs index 3b580c8..44a90f6 100644 --- a/tests/admin_dashboard_tests.rs +++ b/tests/admin_dashboard_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/admin_user_management_tests.rs b/tests/admin_user_management_tests.rs index c4822d4..f53fa36 100644 --- a/tests/admin_user_management_tests.rs +++ b/tests/admin_user_management_tests.rs @@ -19,10 +19,9 @@ use bzod::web::admin::{ }; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); - config.base_url = Some("http://localhost:8080".to_string()); + config.base_url = Some("http://bzo.in".to_string()); config } diff --git a/tests/analytics_parity_tests.rs b/tests/analytics_parity_tests.rs index 13689bd..95bbf6d 100644 --- a/tests/analytics_parity_tests.rs +++ b/tests/analytics_parity_tests.rs @@ -18,8 +18,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/auth_migration_tests.rs b/tests/auth_migration_tests.rs index 6f163de..fbac86b 100644 --- a/tests/auth_migration_tests.rs +++ b/tests/auth_migration_tests.rs @@ -21,8 +21,7 @@ use bzod::db::Db; use bzod::web::admin::{dashboard_get, login_post, LoginForm}; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/auth_tests.rs b/tests/auth_tests.rs index d68cfd7..666f584 100644 --- a/tests/auth_tests.rs +++ b/tests/auth_tests.rs @@ -7,8 +7,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/backup_restore_tests.rs b/tests/backup_restore_tests.rs index b5e650d..80730f1 100644 --- a/tests/backup_restore_tests.rs +++ b/tests/backup_restore_tests.rs @@ -7,8 +7,7 @@ use tar::Archive; use zstd::Decoder; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config @@ -200,8 +199,7 @@ async fn test_restore_slug_collision_rejection() { )); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); fs::create_dir_all(&config.backup_dir).unwrap(); diff --git a/tests/business_workflow_tests.rs b/tests/business_workflow_tests.rs index ad8dd17..576c390 100644 --- a/tests/business_workflow_tests.rs +++ b/tests/business_workflow_tests.rs @@ -18,8 +18,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/concurrency_tests.rs b/tests/concurrency_tests.rs index 3514c19..e74cba4 100644 --- a/tests/concurrency_tests.rs +++ b/tests/concurrency_tests.rs @@ -6,8 +6,7 @@ use std::sync::Arc; use tokio::sync::Barrier; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/config_precedence_tests.rs b/tests/config_precedence_tests.rs new file mode 100644 index 0000000..d8de76e --- /dev/null +++ b/tests/config_precedence_tests.rs @@ -0,0 +1,222 @@ +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, +} + +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") + ); +} diff --git a/tests/disaster_recovery_tests.rs b/tests/disaster_recovery_tests.rs index f9913fb..28cb370 100644 --- a/tests/disaster_recovery_tests.rs +++ b/tests/disaster_recovery_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/http_e2e_tests.rs b/tests/http_e2e_tests.rs index 38f5f40..c2f0570 100644 --- a/tests/http_e2e_tests.rs +++ b/tests/http_e2e_tests.rs @@ -18,8 +18,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/init_admin_tests.rs b/tests/init_admin_tests.rs index 5f31553..aa76a58 100644 --- a/tests/init_admin_tests.rs +++ b/tests/init_admin_tests.rs @@ -5,8 +5,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/integrity_tests.rs b/tests/integrity_tests.rs index 773589f..43be947 100644 --- a/tests/integrity_tests.rs +++ b/tests/integrity_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/legacy_restore_tests.rs b/tests/legacy_restore_tests.rs index c7f84f7..0ff1dda 100644 --- a/tests/legacy_restore_tests.rs +++ b/tests/legacy_restore_tests.rs @@ -21,8 +21,7 @@ use std::path::PathBuf; use tar::Builder; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/migration_tests.rs b/tests/migration_tests.rs index 8493b15..6b44f59 100644 --- a/tests/migration_tests.rs +++ b/tests/migration_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/moderation_tests.rs b/tests/moderation_tests.rs index fb411fb..06e2b5d 100644 --- a/tests/moderation_tests.rs +++ b/tests/moderation_tests.rs @@ -6,8 +6,7 @@ use std::path::PathBuf; use uuid::Uuid; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/next_features_tests.rs b/tests/next_features_tests.rs index 14c2df8..a7759b1 100644 --- a/tests/next_features_tests.rs +++ b/tests/next_features_tests.rs @@ -34,8 +34,7 @@ fn test_custom_slug_validation() { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/ownership_tests.rs b/tests/ownership_tests.rs index edf3fa9..5744241 100644 --- a/tests/ownership_tests.rs +++ b/tests/ownership_tests.rs @@ -18,8 +18,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/qr_endpoint_tests.rs b/tests/qr_endpoint_tests.rs index f9a1721..4ce1764 100644 --- a/tests/qr_endpoint_tests.rs +++ b/tests/qr_endpoint_tests.rs @@ -18,8 +18,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/quota_tests.rs b/tests/quota_tests.rs index 2f052b6..4b0e6b3 100644 --- a/tests/quota_tests.rs +++ b/tests/quota_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/redirect_security_tests.rs b/tests/redirect_security_tests.rs index b97c5c7..81f4af3 100644 --- a/tests/redirect_security_tests.rs +++ b/tests/redirect_security_tests.rs @@ -29,8 +29,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/registry_repair_tests.rs b/tests/registry_repair_tests.rs index 63d9b13..9da8093 100644 --- a/tests/registry_repair_tests.rs +++ b/tests/registry_repair_tests.rs @@ -5,8 +5,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/routing_tests.rs b/tests/routing_tests.rs index 1bd272c..6324fd4 100644 --- a/tests/routing_tests.rs +++ b/tests/routing_tests.rs @@ -11,8 +11,7 @@ use std::path::PathBuf; use std::time::Instant; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/slug_namespace_tests.rs b/tests/slug_namespace_tests.rs index cc42202..187e923 100644 --- a/tests/slug_namespace_tests.rs +++ b/tests/slug_namespace_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/slug_registry_tests.rs b/tests/slug_registry_tests.rs index 96764b9..4a75ba5 100644 --- a/tests/slug_registry_tests.rs +++ b/tests/slug_registry_tests.rs @@ -6,8 +6,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/slug_transfer_tests.rs b/tests/slug_transfer_tests.rs index da96f4d..1154c19 100644 --- a/tests/slug_transfer_tests.rs +++ b/tests/slug_transfer_tests.rs @@ -5,8 +5,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/soft_delete_tests.rs b/tests/soft_delete_tests.rs index b5db7e3..cec80e6 100644 --- a/tests/soft_delete_tests.rs +++ b/tests/soft_delete_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/transaction_tests.rs b/tests/transaction_tests.rs index ff32e2e..10e1a45 100644 --- a/tests/transaction_tests.rs +++ b/tests/transaction_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/upgrade_validation_tests.rs b/tests/upgrade_validation_tests.rs index 53e1c40..1320783 100644 --- a/tests/upgrade_validation_tests.rs +++ b/tests/upgrade_validation_tests.rs @@ -19,8 +19,7 @@ fn compute_sha256(value: &str) -> String { } fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.admin_username = "admin".to_string(); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/user_deletion_tests.rs b/tests/user_deletion_tests.rs index fec6072..11cd60c 100644 --- a/tests/user_deletion_tests.rs +++ b/tests/user_deletion_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/user_isolation_tests.rs b/tests/user_isolation_tests.rs index 3e60435..40b9cdf 100644 --- a/tests/user_isolation_tests.rs +++ b/tests/user_isolation_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/user_management_tests.rs b/tests/user_management_tests.rs index 1b7f7f4..aba67d6 100644 --- a/tests/user_management_tests.rs +++ b/tests/user_management_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/tests/v08_admin_capability_boundary_tests.rs b/tests/v08_admin_capability_boundary_tests.rs index 70cb001..040ebae 100644 --- a/tests/v08_admin_capability_boundary_tests.rs +++ b/tests/v08_admin_capability_boundary_tests.rs @@ -50,8 +50,7 @@ impl TestHarness { let temp_dir = std::env::temp_dir().join(format!("bzod_admin_boundary_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/v08_admin_core_separation_tests.rs b/tests/v08_admin_core_separation_tests.rs index f23573f..d865623 100644 --- a/tests/v08_admin_core_separation_tests.rs +++ b/tests/v08_admin_core_separation_tests.rs @@ -13,8 +13,7 @@ use bzod::state::AppState; use bzod::web::create_router; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); config.base_url = Some("http://localhost:8080".to_string()); config diff --git a/tests/v08_admin_user_route_boundary_tests.rs b/tests/v08_admin_user_route_boundary_tests.rs index 3efaf74..ea3ac15 100644 --- a/tests/v08_admin_user_route_boundary_tests.rs +++ b/tests/v08_admin_user_route_boundary_tests.rs @@ -55,8 +55,7 @@ impl TestHarness { let temp_dir = std::env::temp_dir().join(format!("bzod_user_boundary_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); config.base_url = Some("http://localhost:8080".to_string()); diff --git a/tests/v08_foundation_tests.rs b/tests/v08_foundation_tests.rs index 4fca838..846dc90 100644 --- a/tests/v08_foundation_tests.rs +++ b/tests/v08_foundation_tests.rs @@ -12,8 +12,7 @@ use std::path::PathBuf; fn temp_config() -> (PathBuf, Config) { let temp_dir = std::env::temp_dir().join(format!("bzod_v08_topology_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); (temp_dir, config) } diff --git a/tests/v08_identity_migration_tests.rs b/tests/v08_identity_migration_tests.rs index a998b53..5e7c771 100644 --- a/tests/v08_identity_migration_tests.rs +++ b/tests/v08_identity_migration_tests.rs @@ -25,8 +25,7 @@ fn temp_config() -> (PathBuf, Config) { let temp_dir = std::env::temp_dir().join(format!("bzod_v08_id_migrate_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); (temp_dir, config) } diff --git a/tests/v08_phase6a_elimination_tests.rs b/tests/v08_phase6a_elimination_tests.rs index 1a50b35..858ac36 100644 --- a/tests/v08_phase6a_elimination_tests.rs +++ b/tests/v08_phase6a_elimination_tests.rs @@ -20,8 +20,7 @@ use uuid::Uuid; fn create_test_config() -> (PathBuf, Config) { let temp_dir = std::env::temp_dir().join(format!("bzod_p6a_test_{}", Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); config.base_url = Some("http://localhost:8080".to_string()); (temp_dir, config) diff --git a/tests/v08_slug_migration_tests.rs b/tests/v08_slug_migration_tests.rs index 89890d7..c7f7466 100644 --- a/tests/v08_slug_migration_tests.rs +++ b/tests/v08_slug_migration_tests.rs @@ -25,8 +25,7 @@ fn temp_config() -> (PathBuf, Config) { let temp_dir = std::env::temp_dir().join(format!("bzod_v08_slug_migrate_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); (temp_dir, config) } diff --git a/tests/v08_tenant_boundary_tests.rs b/tests/v08_tenant_boundary_tests.rs index 69faf9a..2f433be 100644 --- a/tests/v08_tenant_boundary_tests.rs +++ b/tests/v08_tenant_boundary_tests.rs @@ -15,8 +15,7 @@ use std::time::Instant; fn temp_config() -> (PathBuf, Config) { let temp_dir = std::env::temp_dir().join(format!("bzod_v08_boundary_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.join("backups"); (temp_dir, config) } diff --git a/tests/wal_recovery_tests.rs b/tests/wal_recovery_tests.rs index d51113f..a7e030f 100644 --- a/tests/wal_recovery_tests.rs +++ b/tests/wal_recovery_tests.rs @@ -4,8 +4,7 @@ use std::fs; use std::path::PathBuf; fn create_temp_config(temp_dir: PathBuf) -> Config { - let mut config = Config::load(); - config.data_dir = temp_dir.clone(); + let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config diff --git a/www/deploy.sh b/www/deploy.sh index ea12979..8c9dd95 100755 --- a/www/deploy.sh +++ b/www/deploy.sh @@ -121,7 +121,7 @@ if [ ! -f "${ENV_FILE}" ]; then cat < "${ENV_FILE}" HOST=0.0.0.0 PORT=8654 -DATA_DIR=${DATA_DIR} +NX9_BZOD_DATA_DIR=${DATA_DIR} COOKIE_SECURE=true RUST_LOG=info SESSION_SECRET=$(openssl rand -hex 32)