#![cfg(feature = "serde")]
use std::path::PathBuf;
use aa_core::config::{ConfigError, DeploymentMode, GatewayConfig};
#[test]
fn ac_1_deployment_mode_exported_from_config_module() {
let _: DeploymentMode = DeploymentMode::Local;
let _: DeploymentMode = DeploymentMode::Remote;
}
#[test]
fn ac_2_full_epic_example_yaml_round_trips() {
let yaml = r#"
mode: remote
local:
port: 7391
dashboard: true
storage_path: ~/.aasm/local.db
remote:
listen_addr: "0.0.0.0:7391"
tls:
cert_file: /etc/aasm/tls.crt
key_file: /etc/aasm/tls.key
database_url: "postgres://aasm@db.internal/aasm"
redis_url: "redis://redis.internal:6379"
agent:
gateway_url: "http://localhost:7391"
api_key: "secret"
"#;
let cfg = GatewayConfig::from_yaml_str(yaml).expect("Epic example YAML must parse");
assert_eq!(cfg.mode, DeploymentMode::Remote);
assert!(cfg.remote.tls.is_some());
assert!(cfg.remote.database_url.is_some());
}
#[test]
fn ac_3_missing_yaml_file_returns_default() {
let missing = std::env::temp_dir().join("aasm-config-AAASM-1693-missing.yaml");
let _ = std::fs::remove_file(&missing);
let cfg = GatewayConfig::load_from_path(&missing).expect("NotFound must not error");
assert_eq!(cfg, GatewayConfig::default());
}
#[test]
fn ac_4_aa_mode_env_overrides_yaml_mode() {
let mut cfg = GatewayConfig::from_yaml_str("mode: local").unwrap();
assert_eq!(cfg.mode, DeploymentMode::Local, "precondition: YAML wins to start");
let restore = ScopedEnv::set("AA_MODE", "remote");
cfg.apply_env_overrides().expect("env override should succeed");
drop(restore);
assert_eq!(cfg.mode, DeploymentMode::Remote);
}
#[test]
fn ac_5_aasm_database_url_env_overrides_yaml_value() {
let yaml = r#"
storage:
postgres:
database_url: "postgres://yaml-default/aasm"
"#;
let mut cfg = GatewayConfig::from_yaml_str(yaml).unwrap();
assert_eq!(
cfg.storage.postgres.database_url.as_deref(),
Some("postgres://yaml-default/aasm"),
);
let restore = ScopedEnv::set("AAASM_DATABASE_URL", "postgres://env-override/aasm");
cfg.apply_env_overrides().unwrap();
drop(restore);
assert_eq!(
cfg.storage.postgres.database_url.as_deref(),
Some("postgres://env-override/aasm"),
);
}
#[test]
fn ac_6_tilde_in_storage_path_expanded_to_real_home() {
let mut cfg = GatewayConfig::default();
assert_eq!(cfg.local.storage_path, PathBuf::from("~/.aasm/local.db"));
cfg.expand_paths();
let expanded = cfg.local.storage_path.to_string_lossy();
if let Some(home) = dirs::home_dir() {
let expected = home.join(".aasm").join("local.db");
assert_eq!(cfg.local.storage_path, expected, "expanded path should match real home");
}
assert!(!expanded.is_empty(), "storage_path must remain a valid PathBuf");
}
#[test]
fn ac_7_invalid_aa_mode_returns_clear_error() {
let mut cfg = GatewayConfig::default();
let restore = ScopedEnv::set("AA_MODE", "foobar");
let err = cfg.apply_env_overrides().expect_err("AA_MODE=foobar must Err");
drop(restore);
let msg = format!("{err}");
assert!(matches!(err, ConfigError::InvalidMode { ref raw } if raw == "foobar"));
assert!(msg.contains("AA_MODE"), "message must name the var: {msg}");
assert!(msg.contains("foobar"), "message must include the value: {msg}");
}
#[test]
fn ac_8_crate_suite_smoke() {
let cfg = GatewayConfig::load_from_path("/tmp/aasm-config-does-not-exist-AAASM-1693.yaml").unwrap();
assert_eq!(cfg, GatewayConfig::default());
}
struct ScopedEnv {
key: &'static str,
prior: Option<String>,
_guard: std::sync::MutexGuard<'static, ()>,
}
fn env_lock() -> &'static std::sync::Mutex<()> {
static LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
LOCK.get_or_init(|| std::sync::Mutex::new(()))
}
impl ScopedEnv {
fn set(key: &'static str, value: &str) -> Self {
let guard = env_lock().lock().unwrap_or_else(|e| e.into_inner());
let prior = std::env::var(key).ok();
unsafe {
std::env::set_var(key, value);
}
Self {
key,
prior,
_guard: guard,
}
}
}
impl Drop for ScopedEnv {
fn drop(&mut self) {
unsafe {
match &self.prior {
Some(value) => std::env::set_var(self.key, value),
None => std::env::remove_var(self.key),
}
}
}
}