use color_eyre::Result;
use color_eyre::eyre::eyre;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VaultInfo {
pub id: String,
pub name: String,
pub path: PathBuf,
pub created_at: OffsetDateTime,
pub last_accessed: OffsetDateTime,
pub description: Option<String>,
pub category: VaultCategory,
pub is_active: bool,
pub is_favorite: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum VaultCategory {
Personal,
Work,
Team,
Project,
Testing,
Archive,
Custom(String),
}
impl std::fmt::Display for VaultCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VaultCategory::Personal => write!(f, "Personal"),
VaultCategory::Work => write!(f, "Work"),
VaultCategory::Team => write!(f, "Team"),
VaultCategory::Project => write!(f, "Project"),
VaultCategory::Testing => write!(f, "Testing"),
VaultCategory::Archive => write!(f, "Archive"),
VaultCategory::Custom(name) => write!(f, "{name}"),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VaultRegistry {
pub vaults: HashMap<String, VaultInfo>,
pub active_vault_id: Option<String>,
#[serde(skip)]
pub registry_path: PathBuf,
}
impl VaultRegistry {
pub fn load() -> Result<Self> {
let registry_path = Self::default_registry_path()?;
if registry_path.exists() {
let content = fs::read_to_string(®istry_path)?;
let mut registry: VaultRegistry = serde_json::from_str(&content)?;
registry.registry_path = registry_path;
Ok(registry)
} else {
let mut registry = Self {
vaults: HashMap::new(),
active_vault_id: None,
registry_path,
};
if let Ok(default_vault_path) = default_db_path() {
if default_vault_path.exists() {
registry.register_existing_vault(default_vault_path);
} else {
registry.create_default_vault();
}
} else {
registry.create_default_vault();
}
registry.save()?;
Ok(registry)
}
}
fn register_existing_vault(&mut self, vault_path: PathBuf) {
let vault_id = "main".to_string();
let vault_info = VaultInfo {
id: vault_id.clone(),
name: "Main Vault".to_string(),
path: vault_path,
created_at: OffsetDateTime::now_utc(),
last_accessed: OffsetDateTime::now_utc(),
description: Some("Main vault".to_string()),
category: VaultCategory::Personal,
is_active: true,
is_favorite: false,
};
self.vaults.insert(vault_id.clone(), vault_info);
self.active_vault_id = Some(vault_id);
}
fn default_registry_path() -> Result<PathBuf> {
let base = dirs::config_dir().ok_or_else(|| eyre!("No config directory found"))?;
Ok(base.join("chamber").join("registry.json"))
}
fn create_default_vault(&mut self) {
let vault_id = "main".to_string();
let vault_path = default_db_path().expect("Could not get default DB");
let vault_info = VaultInfo {
id: vault_id.clone(),
name: "Main Vault".to_string(),
path: vault_path,
created_at: OffsetDateTime::now_utc(),
last_accessed: OffsetDateTime::now_utc(),
description: Some("Main vault".to_string()),
category: VaultCategory::Personal,
is_active: true,
is_favorite: false,
};
self.vaults.insert(vault_id.clone(), vault_info);
self.active_vault_id = Some(vault_id);
}
fn default_vault_path(vault_id: &str) -> Result<PathBuf> {
let base = dirs::config_dir().ok_or_else(|| eyre!("No config directory found"))?;
let chamber_dir = base.join("chamber");
std::fs::create_dir_all(&chamber_dir)?;
Ok(chamber_dir.join(format!("{vault_id}.db")))
}
pub fn create_vault(
&mut self,
name: String,
path: Option<PathBuf>,
category: VaultCategory,
description: Option<String>,
) -> Result<String> {
let vault_id = Self::generate_vault_id();
if self.vaults.contains_key(&vault_id) {
return Err(eyre!("Vault with ID '{}' already exists", vault_id));
}
let vault_path = if let Some(custom_path) = path {
custom_path
} else {
Self::default_vault_path(&vault_id)?
};
if let Some(parent) = vault_path.parent() {
fs::create_dir_all(parent)?;
}
let vault_info = VaultInfo {
id: vault_id.clone(),
name,
path: vault_path,
created_at: OffsetDateTime::now_utc(),
last_accessed: OffsetDateTime::now_utc(),
description,
category,
is_active: false,
is_favorite: false,
};
self.vaults.insert(vault_id.clone(), vault_info);
self.save()?;
Ok(vault_id)
}
fn generate_vault_id() -> String {
Uuid::new_v4().to_string()
}
#[must_use]
pub fn list_vaults(&self) -> Vec<&VaultInfo> {
let mut vaults: Vec<&VaultInfo> = self.vaults.values().collect();
vaults.sort_by(|a, b| {
match (a.is_favorite, b.is_favorite) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => b.last_accessed.cmp(&a.last_accessed),
}
});
vaults
}
#[must_use]
pub fn get_vault(&self, vault_id: &str) -> Option<&VaultInfo> {
self.vaults.get(vault_id)
}
pub fn set_active_vault(&mut self, vault_id: &str) -> Result<()> {
if !self.vaults.contains_key(vault_id) {
return Err(eyre!("Vault with ID '{}' not found", vault_id));
}
if let Some(prev_id) = &self.active_vault_id {
if let Some(prev_vault) = self.vaults.get_mut(prev_id) {
prev_vault.is_active = false;
}
}
if let Some(vault) = self.vaults.get_mut(vault_id) {
vault.is_active = true;
vault.last_accessed = OffsetDateTime::now_utc();
}
self.active_vault_id = Some(vault_id.to_string());
self.save()?;
Ok(())
}
#[must_use]
pub fn get_active_vault(&self) -> Option<&VaultInfo> {
self.active_vault_id.as_ref().and_then(|id| self.vaults.get(id))
}
pub fn update_vault(
&mut self,
vault_id: &str,
name: Option<String>,
description: Option<String>,
category: Option<VaultCategory>,
is_favorite: Option<bool>,
) -> Result<()> {
let vault = self
.vaults
.get_mut(vault_id)
.ok_or_else(|| eyre!("Vault with ID '{}' not found", vault_id))?;
if let Some(new_name) = name {
vault.name = new_name;
}
if let Some(new_desc) = description {
vault.description = Some(new_desc);
}
if let Some(new_category) = category {
vault.category = new_category;
}
if let Some(favorite) = is_favorite {
vault.is_favorite = favorite;
}
vault.last_accessed = OffsetDateTime::now_utc();
self.save()?;
Ok(())
}
pub fn delete_vault(&mut self, vault_id: &str, delete_file: bool) -> Result<()> {
let vault = self
.vaults
.get(vault_id)
.ok_or_else(|| eyre!("Vault with ID '{}' not found", vault_id))?;
if delete_file && vault.path.exists() {
fs::remove_file(&vault.path)?;
}
if self.active_vault_id.as_ref() == Some(&String::from(vault_id)) {
if self.vaults.len() > 1 {
let next_vault_id = self
.vaults
.keys()
.find(|&id| id != vault_id)
.ok_or_else(|| eyre!("No alternative vault found"))?
.clone();
self.set_active_vault(&next_vault_id)?;
} else {
self.active_vault_id = None;
}
}
self.vaults.remove(vault_id);
self.save()?;
Ok(())
}
fn save(&self) -> Result<()> {
if let Some(parent) = self.registry_path.parent() {
fs::create_dir_all(parent)?;
}
let content = serde_json::to_string_pretty(self)?;
fs::write(&self.registry_path, content)?;
Ok(())
}
pub fn import_vault(
&mut self,
vault_file: &Path,
name: String,
category: VaultCategory,
copy_file: bool,
) -> Result<String> {
if !vault_file.exists() {
return Err(eyre!("Vault file does not exist: {}", vault_file.display()));
}
let vault_id = Self::generate_vault_id();
let vault_path = if copy_file {
let new_path = Self::default_vault_path(&vault_id)?;
if let Some(parent) = new_path.parent() {
fs::create_dir_all(parent)?;
}
fs::copy(vault_file, &new_path)?;
new_path
} else {
vault_file.to_path_buf()
};
let vault_info = VaultInfo {
id: vault_id.clone(),
name,
path: vault_path,
created_at: OffsetDateTime::now_utc(),
last_accessed: OffsetDateTime::now_utc(),
description: Some("Imported vault".to_string()),
category,
is_active: false,
is_favorite: false,
};
self.vaults.insert(vault_id.clone(), vault_info);
self.save()?;
Ok(vault_id)
}
}
fn default_db_path() -> Result<PathBuf> {
let base = dirs::config_dir().ok_or_else(|| eyre!("No config dir"))?;
let dir = base.join("chamber");
std::fs::create_dir_all(&dir)?;
Ok(dir.join("vault.sqlite3"))
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
use super::*;
use std::collections::HashMap;
use tempfile::TempDir;
use time::OffsetDateTime;
use uuid::Uuid;
fn create_temp_registry() -> (VaultRegistry, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let registry_path = temp_dir.path().join("test_registry.json");
let registry = VaultRegistry {
vaults: HashMap::new(),
active_vault_id: None,
registry_path,
};
(registry, temp_dir)
}
fn create_test_vault_info(id: &str, name: &str, category: VaultCategory) -> VaultInfo {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let vault_path = temp_dir.path().join("test_vault.db");
VaultInfo {
id: id.to_string(),
name: name.to_string(),
path: vault_path,
created_at: OffsetDateTime::now_utc(),
last_accessed: OffsetDateTime::now_utc(),
description: None,
category,
is_active: false,
is_favorite: false,
}
}
#[test]
fn test_vault_registry_new_empty() {
let (registry, _temp_dir) = create_temp_registry();
assert!(registry.vaults.is_empty());
assert!(registry.active_vault_id.is_none());
assert!(!registry.registry_path.as_os_str().is_empty());
}
#[test]
fn test_list_vaults_empty() {
let (registry, _temp_dir) = create_temp_registry();
let vaults = registry.list_vaults();
assert!(vaults.is_empty());
}
#[test]
fn test_list_vaults_with_data() {
let (mut registry, _temp_dir) = create_temp_registry();
let vault1 = create_test_vault_info(
"550e8400-e29b-41d4-a716-446655440000",
"Test Vault 1",
VaultCategory::Personal,
);
let vault2 = create_test_vault_info(
"6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"Test Vault 2",
VaultCategory::Work,
);
registry
.vaults
.insert("550e8400-e29b-41d4-a716-446655440000".to_string(), vault1);
registry
.vaults
.insert("6ba7b810-9dad-11d1-80b4-00c04fd430c8".to_string(), vault2);
let vaults = registry.list_vaults();
assert_eq!(vaults.len(), 2);
let vault_names: Vec<&str> = vaults.iter().map(|v| v.name.as_str()).collect();
assert!(vault_names.contains(&"Test Vault 1"));
assert!(vault_names.contains(&"Test Vault 2"));
}
#[test]
fn test_get_vault_exists() {
let (mut registry, _temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault = create_test_vault_info(test_id, "Test Vault", VaultCategory::Personal);
registry.vaults.insert(test_id.to_string(), vault);
let result = registry.get_vault(test_id);
assert!(result.is_some());
assert_eq!(result.unwrap().name, "Test Vault");
assert_eq!(result.unwrap().id, test_id);
}
#[test]
fn test_get_vault_not_exists() {
let (registry, _temp_dir) = create_temp_registry();
let result = registry.get_vault("550e8400-e29b-41d4-a716-446655440000");
assert!(result.is_none());
}
#[test]
fn test_get_active_vault_none_set() {
let (registry, _temp_dir) = create_temp_registry();
let result = registry.get_active_vault();
assert!(result.is_none());
}
#[test]
fn test_get_active_vault_exists() {
let (mut registry, _temp_dir) = create_temp_registry();
let active_id = "550e8400-e29b-41d4-a716-446655440000";
let vault = create_test_vault_info(active_id, "Active Vault", VaultCategory::Personal);
registry.vaults.insert(active_id.to_string(), vault);
registry.active_vault_id = Some(active_id.to_string());
let result = registry.get_active_vault();
assert!(result.is_some());
assert_eq!(result.unwrap().name, "Active Vault");
}
#[test]
fn test_get_active_vault_id_invalid() {
let (mut registry, _temp_dir) = create_temp_registry();
registry.active_vault_id = Some("550e8400-e29b-41d4-a716-446655440000".to_string());
let result = registry.get_active_vault();
assert!(result.is_none());
}
#[test]
fn test_generate_vault_id_returns_uuid() {
let (_, _temp_dir) = create_temp_registry();
let id1 = VaultRegistry::generate_vault_id();
let id2 = VaultRegistry::generate_vault_id();
assert!(Uuid::parse_str(&id1).is_ok());
assert!(Uuid::parse_str(&id2).is_ok());
assert_ne!(id1, id2);
}
#[test]
fn test_generate_vault_id_uniqueness() {
let (_, _temp_dir) = create_temp_registry();
let mut ids = Vec::new();
for _ in 0..100 {
ids.push(VaultRegistry::generate_vault_id());
}
let mut unique_ids = ids.clone();
unique_ids.sort();
unique_ids.dedup();
assert_eq!(ids.len(), unique_ids.len());
for id in ids {
assert!(Uuid::parse_str(&id).is_ok());
}
}
#[test]
fn test_generate_vault_id_ignores_name() {
let (_, _temp_dir) = create_temp_registry();
let id1 = VaultRegistry::generate_vault_id();
let id2 = VaultRegistry::generate_vault_id();
let id3 = VaultRegistry::generate_vault_id();
let id4 = VaultRegistry::generate_vault_id();
assert!(Uuid::parse_str(&id1).is_ok());
assert!(Uuid::parse_str(&id2).is_ok());
assert!(Uuid::parse_str(&id3).is_ok());
assert!(Uuid::parse_str(&id4).is_ok());
let ids = vec![&id1, &id2, &id3, &id4];
let mut unique_ids = ids.clone();
unique_ids.sort();
unique_ids.dedup();
assert_eq!(ids.len(), unique_ids.len());
}
#[test]
fn test_vault_category_display() {
assert_eq!(VaultCategory::Personal.to_string(), "Personal");
assert_eq!(VaultCategory::Work.to_string(), "Work");
assert_eq!(VaultCategory::Team.to_string(), "Team");
assert_eq!(VaultCategory::Project.to_string(), "Project");
assert_eq!(VaultCategory::Testing.to_string(), "Testing");
assert_eq!(VaultCategory::Archive.to_string(), "Archive");
assert_eq!(
VaultCategory::Custom("MyCategory".to_string()).to_string(),
"MyCategory"
);
}
#[test]
fn test_vault_category_equality() {
assert_eq!(VaultCategory::Personal, VaultCategory::Personal);
assert_ne!(VaultCategory::Personal, VaultCategory::Work);
assert_eq!(
VaultCategory::Custom("test".to_string()),
VaultCategory::Custom("test".to_string())
);
assert_ne!(
VaultCategory::Custom("test1".to_string()),
VaultCategory::Custom("test2".to_string())
);
}
#[test]
fn test_vault_info_serialization() {
let vault = create_test_vault_info(
"550e8400-e29b-41d4-a716-446655440000",
"Test Vault",
VaultCategory::Personal,
);
let serialized = serde_json::to_string(&vault).expect("Failed to serialize");
let deserialized: VaultInfo = serde_json::from_str(&serialized).expect("Failed to deserialize");
assert_eq!(vault.id, deserialized.id);
assert_eq!(vault.name, deserialized.name);
assert_eq!(vault.category, deserialized.category);
}
#[test]
fn test_vault_registry_serialization() {
let (mut registry, _temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault = create_test_vault_info(test_id, "Test Vault", VaultCategory::Work);
registry.vaults.insert(test_id.to_string(), vault);
registry.active_vault_id = Some(test_id.to_string());
let serialized = serde_json::to_string(®istry).expect("Failed to serialize");
let mut deserialized: VaultRegistry = serde_json::from_str(&serialized).expect("Failed to deserialize");
deserialized.registry_path = registry.registry_path.clone();
assert_eq!(registry.vaults.len(), deserialized.vaults.len());
assert_eq!(registry.active_vault_id, deserialized.active_vault_id);
assert!(deserialized.get_vault(test_id).is_some());
}
#[test]
fn test_create_vault_basic() {
let (mut registry, temp_dir) = create_temp_registry();
let vault_path = temp_dir.path().join("custom_vault.db");
let result = registry.create_vault(
"My Test Vault".to_string(),
Some(vault_path.clone()),
VaultCategory::Personal,
Some("Test description".to_string()),
);
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id);
assert!(vault.is_some());
let vault = vault.unwrap();
assert_eq!(vault.name, "My Test Vault");
assert_eq!(vault.path, vault_path);
assert_eq!(vault.category, VaultCategory::Personal);
assert_eq!(vault.description, Some("Test description".to_string()));
assert!(!vault.is_active);
assert!(!vault.is_favorite);
}
#[test]
fn test_create_vault_default_path() {
let (mut registry, _temp_dir) = create_temp_registry();
let result = registry.create_vault("Default Path Vault".to_string(), None, VaultCategory::Work, None);
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id);
assert!(vault.is_some());
let vault = vault.unwrap();
assert_eq!(vault.name, "Default Path Vault");
assert_eq!(vault.category, VaultCategory::Work);
assert!(vault.description.is_none());
}
#[test]
fn test_set_active_vault_success() {
let (mut registry, _temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault = create_test_vault_info(test_id, "Test Vault", VaultCategory::Personal);
registry.vaults.insert(test_id.to_string(), vault);
let result = registry.set_active_vault(test_id);
assert!(result.is_ok());
assert_eq!(registry.active_vault_id, Some(test_id.to_string()));
}
#[test]
fn test_set_active_vault_nonexistent() {
let (mut registry, _temp_dir) = create_temp_registry();
let result = registry.set_active_vault("550e8400-e29b-41d4-a716-446655440000");
assert!(result.is_err());
assert!(registry.active_vault_id.is_none());
}
#[test]
fn test_update_vault_all_fields() {
let (mut registry, _temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault = create_test_vault_info(test_id, "Original Name", VaultCategory::Personal);
registry.vaults.insert(test_id.to_string(), vault);
let result = registry.update_vault(
test_id,
Some("New Name".to_string()),
Some("New Description".to_string()),
Some(VaultCategory::Work),
Some(true),
);
assert!(result.is_ok());
let updated_vault = registry.get_vault(test_id).unwrap();
assert_eq!(updated_vault.name, "New Name");
assert_eq!(updated_vault.description, Some("New Description".to_string()));
assert_eq!(updated_vault.category, VaultCategory::Work);
assert!(updated_vault.is_favorite);
}
#[test]
fn test_update_vault_partial_fields() {
let (mut registry, _temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let mut vault = create_test_vault_info(test_id, "Original Name", VaultCategory::Personal);
vault.description = Some("Original Description".to_string());
registry.vaults.insert(test_id.to_string(), vault);
let result = registry.update_vault(test_id, Some("New Name".to_string()), None, None, None);
assert!(result.is_ok());
let updated_vault = registry.get_vault(test_id).unwrap();
assert_eq!(updated_vault.name, "New Name");
assert_eq!(updated_vault.description, Some("Original Description".to_string()));
assert_eq!(updated_vault.category, VaultCategory::Personal);
assert!(!updated_vault.is_favorite);
}
#[test]
fn test_update_vault_nonexistent() {
let (mut registry, _temp_dir) = create_temp_registry();
let result = registry.update_vault(
"550e8400-e29b-41d4-a716-446655440000",
Some("New Name".to_string()),
None,
None,
None,
);
assert!(result.is_err());
}
#[test]
fn test_delete_vault_exists() {
let (mut registry, temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault_path = temp_dir.path().join("test_vault.db");
std::fs::write(&vault_path, b"test vault data").expect("Failed to create test vault file");
let mut vault = create_test_vault_info(test_id, "Test Vault", VaultCategory::Personal);
vault.path = vault_path;
registry.vaults.insert(test_id.to_string(), vault);
let registry_content = serde_json::to_string(®istry).unwrap_or_else(|_| "{}".to_string());
std::fs::write(®istry.registry_path, registry_content).ok();
let result = registry.delete_vault(test_id, false);
match result {
Ok(()) => {
assert!(registry.get_vault(test_id).is_none());
}
Err(e) => {
if e.to_string().contains("not implemented") || e.to_string().contains("todo") {
println!("Skipping test - delete_vault not implemented yet");
return;
}
panic!("delete_vault failed unexpectedly: {e}");
}
}
}
#[test]
fn test_delete_vault_with_file_removal() {
let (mut registry, temp_dir) = create_temp_registry();
let test_id = "550e8400-e29b-41d4-a716-446655440000";
let vault_path = temp_dir.path().join("vault_to_delete.db");
std::fs::write(&vault_path, b"test vault data").expect("Failed to create test vault file");
assert!(vault_path.exists());
let mut vault = create_test_vault_info(test_id, "Vault To Delete", VaultCategory::Personal);
vault.path = vault_path.clone();
registry.vaults.insert(test_id.to_string(), vault);
let result = registry.delete_vault(test_id, true);
if let Err(ref e) = result {
eprintln!("Delete vault with file error: {e}");
}
assert!(result.is_ok());
assert!(registry.get_vault(test_id).is_none());
assert!(!vault_path.exists()); }
#[test]
fn test_delete_vault_nonexistent() {
let (mut registry, _temp_dir) = create_temp_registry();
let result = registry.delete_vault("550e8400-e29b-41d4-a716-446655440000", false);
assert!(result.is_err());
}
#[test]
fn test_import_vault_copy_file() {
let (mut registry, temp_dir) = create_temp_registry();
let source_file = temp_dir.path().join("source_vault.db");
std::fs::write(&source_file, b"test vault content").expect("Failed to create test file");
let result = registry.import_vault(
&source_file,
"Imported Vault".to_string(),
VaultCategory::Project,
true, );
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id).unwrap();
assert_eq!(vault.name, "Imported Vault");
assert_eq!(vault.category, VaultCategory::Project);
assert_ne!(vault.path, source_file);
let copied_file_path = vault.path.clone();
let cleanup_result = registry.delete_vault(&vault_id, true);
if cleanup_result.is_ok() {
assert!(registry.get_vault(&vault_id).is_none());
if !copied_file_path.starts_with(temp_dir.path()) {
assert!(
!copied_file_path.exists(),
"Production file should be cleaned up: {copied_file_path:?}"
);
}
} else {
eprintln!("Warning: delete_vault failed, attempting manual cleanup: {cleanup_result:?}");
registry.vaults.remove(&vault_id);
if !copied_file_path.starts_with(temp_dir.path()) && copied_file_path.exists() {
if let Err(e) = std::fs::remove_file(&copied_file_path) {
eprintln!("Warning: Failed to clean up test file {copied_file_path:?}: {e}");
}
}
}
assert!(
registry.get_vault(&vault_id).is_none(),
"Vault should be cleaned up from registry"
);
}
#[test]
fn test_import_vault_move_file() {
let (mut registry, temp_dir) = create_temp_registry();
let source_file = temp_dir.path().join("source_vault.db");
std::fs::write(&source_file, b"test vault content").expect("Failed to create test file");
let result = registry.import_vault(
&source_file,
"Imported Vault".to_string(),
VaultCategory::Archive,
false, );
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id).unwrap();
assert_eq!(vault.name, "Imported Vault");
assert_eq!(vault.category, VaultCategory::Archive);
assert_eq!(vault.path, source_file); }
#[test]
fn test_import_vault_nonexistent_file() {
let (mut registry, temp_dir) = create_temp_registry();
let nonexistent_file = temp_dir.path().join("nonexistent.db");
let result = registry.import_vault(
&nonexistent_file,
"Imported Vault".to_string(),
VaultCategory::Personal,
true,
);
assert!(result.is_err());
}
#[test]
fn test_multiple_vaults_management() {
let (mut registry, _temp_dir) = create_temp_registry();
let vault1_result = registry.create_vault("Vault One".to_string(), None, VaultCategory::Personal, None);
let vault2_result = registry.create_vault(
"Vault Two".to_string(),
None,
VaultCategory::Work,
Some("Work vault".to_string()),
);
let vault3_result = registry.create_vault("Vault Three".to_string(), None, VaultCategory::Team, None);
assert!(vault1_result.is_ok());
assert!(vault2_result.is_ok());
assert!(vault3_result.is_ok());
let vault1_id = vault1_result.unwrap();
let vault2_id = vault2_result.unwrap();
let vault3_id = vault3_result.unwrap();
assert!(Uuid::parse_str(&vault1_id).is_ok());
assert!(Uuid::parse_str(&vault2_id).is_ok());
assert!(Uuid::parse_str(&vault3_id).is_ok());
assert_ne!(vault1_id, vault2_id);
assert_ne!(vault2_id, vault3_id);
assert_ne!(vault1_id, vault3_id);
let vaults = registry.list_vaults();
assert_eq!(vaults.len(), 3);
assert!(registry.set_active_vault(&vault2_id).is_ok());
assert_eq!(registry.active_vault_id, Some(vault2_id.clone()));
assert!(registry.update_vault(&vault1_id, None, None, None, Some(true)).is_ok());
assert!(registry.get_vault(&vault1_id).unwrap().is_favorite);
assert!(registry.delete_vault(&vault3_id, false).is_ok());
assert_eq!(registry.list_vaults().len(), 2);
}
#[test]
fn test_vault_timestamps() {
let vault = create_test_vault_info(
"550e8400-e29b-41d4-a716-446655440000",
"Test Vault",
VaultCategory::Personal,
);
let now = OffsetDateTime::now_utc();
let created_diff = (vault.created_at - now).abs();
let accessed_diff = (vault.last_accessed - now).abs();
assert!(created_diff.whole_seconds() <= 1);
assert!(accessed_diff.whole_seconds() <= 1);
}
#[test]
fn test_vault_category_custom_variants() {
let categories = vec![
VaultCategory::Personal,
VaultCategory::Work,
VaultCategory::Team,
VaultCategory::Project,
VaultCategory::Testing,
VaultCategory::Archive,
VaultCategory::Custom("CustomType".to_string()),
];
for category in categories {
let vault = create_test_vault_info("550e8400-e29b-41d4-a716-446655440000", "Test Vault", category.clone());
let serialized = serde_json::to_string(&vault).expect("Failed to serialize");
let deserialized: VaultInfo = serde_json::from_str(&serialized).expect("Failed to deserialize");
assert_eq!(vault.category, deserialized.category);
}
}
#[test]
fn test_edge_case_empty_vault_name() {
let (mut registry, _temp_dir) = create_temp_registry();
let result = registry.create_vault(String::new(), None, VaultCategory::Personal, None);
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id).unwrap();
assert_eq!(vault.name, "");
}
#[test]
fn test_edge_case_very_long_vault_name() {
let (mut registry, _temp_dir) = create_temp_registry();
let long_name = "A".repeat(1000);
let result = registry.create_vault(long_name.clone(), None, VaultCategory::Personal, None);
assert!(result.is_ok());
let vault_id = result.unwrap();
assert!(Uuid::parse_str(&vault_id).is_ok());
let vault = registry.get_vault(&vault_id).unwrap();
assert_eq!(vault.name, long_name);
}
#[test]
fn test_concurrent_vault_operations() {
let (mut registry, _temp_dir) = create_temp_registry();
let mut vault_ids = Vec::new();
for i in 0..10 {
let result = registry.create_vault(format!("Vault {i}"), None, VaultCategory::Testing, None);
assert!(result.is_ok());
vault_ids.push(result.unwrap());
}
for vault_id in &vault_ids {
assert!(Uuid::parse_str(vault_id).is_ok());
}
let mut unique_ids = vault_ids.clone();
unique_ids.sort();
unique_ids.dedup();
assert_eq!(vault_ids.len(), unique_ids.len());
for vault_id in &vault_ids {
assert!(registry.get_vault(vault_id).is_some());
assert!(registry.set_active_vault(vault_id).is_ok());
assert!(registry.update_vault(vault_id, None, None, None, Some(true)).is_ok());
}
assert_eq!(registry.list_vaults().len(), 10);
}
#[test]
fn test_uuid_collision_resistance() {
let (_, _temp_dir) = create_temp_registry();
let mut ids = std::collections::HashSet::new();
for _ in 0..10000 {
let id = VaultRegistry::generate_vault_id();
assert!(Uuid::parse_str(&id).is_ok());
assert!(ids.insert(id), "UUID collision detected!");
}
}
#[test]
fn test_uuid_format_consistency() {
let (_, _temp_dir) = create_temp_registry();
for _ in 0..100 {
let id = VaultRegistry::generate_vault_id();
let uuid = Uuid::parse_str(&id).expect("Should be valid UUID");
assert_eq!(uuid.get_version_num(), 4);
assert_eq!(id.len(), 36); assert_eq!(id.chars().filter(|&c| c == '-').count(), 4); }
}
}