use journey::app::{App, CliArgs};
use journey::config::{Config, VaultConfig};
use journey::config_manager::ConfigManager;
use std::collections::HashMap;
use std::path::PathBuf;
use tempfile::TempDir;
use std::env;
use chrono::{NaiveDate, Local};
use serial_test::serial;
fn ensure_test_isolation() {
env::remove_var("JOURNEY_CONFIG");
env::remove_var("JOURNEY_CONFIG_PATH"); }
fn create_app_with_config(config: Config) -> (App, impl FnOnce()) {
ensure_test_isolation();
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("journey.yaml");
let vault_temp_dir = TempDir::new().unwrap();
let mut updated_config = config;
for (i, vault) in updated_config.vaults.values_mut().enumerate() {
vault.path = vault_temp_dir.path().join(format!("vault_{}", i));
}
let config_manager = ConfigManager { config_path: config_path.clone() };
config_manager.save_config(&updated_config).unwrap();
env::set_var("JOURNEY_CONFIG", config_path.to_str().unwrap());
let app = App::new().unwrap();
let cleanup = move || {
env::remove_var("JOURNEY_CONFIG");
let _temp_dir = temp_dir;
let _vault_temp_dir = vault_temp_dir;
};
(app, cleanup)
}
#[test]
#[serial]
fn test_app_creation() {
let mut config = Config::new();
let vault_config = VaultConfig::test_config("test-vault", "/tmp/test-vault");
config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(config);
assert!(app.get_vault(None).is_ok());
}
#[test]
#[serial]
fn test_single_vault_auto_selection() {
let mut config = Config::new();
let vault_config = VaultConfig::test_config("test-vault", "/tmp/test-vault");
config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(config);
let vault = app.get_vault(None);
assert!(vault.is_ok());
let vault = app.get_vault(Some("test-vault"));
assert!(vault.is_ok());
}
#[test]
#[serial]
fn test_multiple_vaults_require_specification() {
let mut config = Config::new();
let vault1 = VaultConfig::test_config("vault1", "/tmp/vault1");
let vault2 = VaultConfig::test_config("vault2", "/tmp/vault2");
config.add_vault(vault1);
config.add_vault(vault2);
let (app, _cleanup) = create_app_with_config(config);
let vault = app.get_vault(None);
assert!(vault.is_err());
let vault = app.get_vault(Some("vault1"));
assert!(vault.is_ok());
let vault = app.get_vault(Some("vault2"));
assert!(vault.is_ok());
}
#[test]
#[serial]
fn test_no_vaults_configured() {
let config = Config::new();
let (app, _cleanup) = create_app_with_config(config);
let vault = app.get_vault(None);
assert!(vault.is_err());
let vault = app.get_vault(Some("non-existent"));
assert!(vault.is_err());
}
#[test]
#[serial]
fn test_no_config_file_exists() {
ensure_test_isolation();
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("journey.yaml");
env::set_var("JOURNEY_CONFIG", config_path.to_str().unwrap());
let app = App::new();
assert!(app.is_err(), "Expected App::new() to fail when config file doesn't exist. Got: {:?}", app.as_ref().err());
if let Err(journey::errors::JourneyError::Config(msg)) = app {
assert!(msg.contains("No configuration file found"));
assert!(msg.contains("journey --init"));
} else {
panic!("Expected Config error");
}
let _temp_dir = temp_dir;
env::remove_var("JOURNEY_CONFIG");
}
#[test]
#[serial]
fn test_init_vault_with_name() {
ensure_test_isolation();
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("journey.yaml");
env::set_var("JOURNEY_CONFIG", config_path.to_str().unwrap());
let mut app = App::new_for_init().unwrap();
let vault_temp_dir = TempDir::new().unwrap();
let result = app.init_vault(vault_temp_dir.path().to_path_buf(), Some("test-vault".to_string()), false);
assert!(result.is_ok());
let _temp_dir = temp_dir;
let _vault_temp_dir = vault_temp_dir;
env::remove_var("JOURNEY_CONFIG");
}
#[test]
#[serial]
fn test_init_vault_without_name() {
ensure_test_isolation();
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("journey.yaml");
env::set_var("JOURNEY_CONFIG", config_path.to_str().unwrap());
let mut app = App::new_for_init().unwrap();
let vault_temp_dir = TempDir::new().unwrap();
let result = app.init_vault(vault_temp_dir.path().to_path_buf(), None, false);
assert!(result.is_ok());
let _temp_dir = temp_dir;
let _vault_temp_dir = vault_temp_dir;
}
#[test]
#[serial]
fn test_init_vault_invalid_path() {
ensure_test_isolation();
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("journey.yaml");
env::set_var("JOURNEY_CONFIG", config_path.to_str().unwrap());
let mut app = App::new_for_init().unwrap();
let result = app.init_vault(PathBuf::from(""), None, false);
assert!(result.is_err());
if let Err(journey::errors::JourneyError::Config(msg)) = result {
assert!(msg.contains("Invalid path: cannot extract basename"));
} else {
panic!("Expected Config error");
}
}
#[test]
#[serial]
fn test_production_config_isolation() {
ensure_test_isolation();
let mut test_config = Config::new();
let vault_config = VaultConfig::test_config("test-vault", "/tmp/test-vault");
test_config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(test_config);
let vault = app.get_vault(Some("test-vault"));
assert!(vault.is_ok());
}
#[test]
#[serial]
fn test_add_note_future_date_absolute() {
let mut config = Config::new();
let vault_config = VaultConfig::test_config("test", "/tmp/test-vault");
config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(config);
let cli_args = CliArgs {
vault: None,
date: Some("2025-12-25".to_string()),
relative_date: None,
time: None,
time_format: None,
};
let result = app.parse_date(&cli_args);
assert!(result.is_ok());
let date = result.unwrap();
assert_eq!(date, NaiveDate::from_ymd_opt(2025, 12, 25).unwrap());
}
#[test]
#[serial]
fn test_add_note_future_date_relative() {
let mut config = Config::new();
let vault_config = VaultConfig::test_config("test", "/tmp/test-vault");
config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(config);
let cli_args = CliArgs {
vault: None,
date: None,
relative_date: Some(-7), time: None,
time_format: None,
};
let result = app.parse_date(&cli_args);
assert!(result.is_ok());
let date = result.unwrap();
let expected_date = Local::now().date_naive() + chrono::Duration::days(7);
assert_eq!(date, expected_date);
}
#[test]
#[serial]
fn test_add_note_intuitive_relative_dates() {
let mut config = Config::new();
let vault_config = VaultConfig::test_config("test", "/tmp/test-vault");
config.add_vault(vault_config);
let (app, _cleanup) = create_app_with_config(config);
let cli_args = CliArgs {
vault: None,
date: None,
relative_date: Some(1), time: None,
time_format: None,
};
let result = app.parse_date(&cli_args);
assert!(result.is_ok());
let date = result.unwrap();
let expected_date = Local::now().date_naive() - chrono::Duration::days(1);
assert_eq!(date, expected_date);
let cli_args = CliArgs {
vault: None,
date: None,
relative_date: Some(-1), time: None,
time_format: None,
};
let result = app.parse_date(&cli_args);
assert!(result.is_ok());
let date = result.unwrap();
let expected_date = Local::now().date_naive() + chrono::Duration::days(1);
assert_eq!(date, expected_date);
}
#[test]
#[serial]
fn test_unlist_vault_success() {
let mut config = Config::new();
let vault1 = VaultConfig::test_config("vault1", "/tmp/vault1");
let vault2 = VaultConfig::test_config("vault2", "/tmp/vault2");
config.add_vault(vault1);
config.add_vault(vault2);
let (mut app, _cleanup) = create_app_with_config(config);
assert!(app.get_config().vaults.contains_key("vault1"));
assert!(app.get_config().vaults.contains_key("vault2"));
let result = app.unlist_vault("vault1");
assert!(result.is_ok());
assert!(!app.get_config().vaults.contains_key("vault1"));
assert!(app.get_config().vaults.contains_key("vault2"));
}
#[test]
#[serial]
fn test_unlist_vault_not_found() {
let mut config = Config::new();
let vault = VaultConfig::test_config("vault1", "/tmp/vault1");
config.add_vault(vault);
let (mut app, _cleanup) = create_app_with_config(config);
let result = app.unlist_vault("non-existent");
assert!(result.is_err());
assert!(app.get_config().vaults.contains_key("vault1"));
}
#[test]
#[serial]
fn test_unlist_default_vault() {
let mut config = Config::new();
let vault1 = VaultConfig::test_config("vault1", "/tmp/vault1");
let vault2 = VaultConfig::test_config("vault2", "/tmp/vault2");
config.add_vault(vault1);
config.add_vault(vault2);
config.set_default_vault("vault1").unwrap();
let (mut app, _cleanup) = create_app_with_config(config);
assert_eq!(app.get_config().default_vault, Some("vault1".to_string()));
let result = app.unlist_vault("vault1");
assert!(result.is_ok());
assert!(app.get_config().default_vault.is_none());
assert!(!app.get_config().vaults.contains_key("vault1"));
assert!(app.get_config().vaults.contains_key("vault2"));
}