summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-09-25 17:22:05 +0800
committerGitHub <noreply@github.com>2025-09-25 17:22:05 +0800
commit3497a285c430d0390bfa074c6f9dab5c732b59a1 (patch)
tree243626035d251cf5d19deee9845ebe6af2a6582a /crates
parent647cc441eece20218d7387f37d94042e88042057 (diff)
parent06b2e2b384da34e30688d1a217859c5cf68ca3bd (diff)
Merge pull request #6 from JustEnoughVCS/jvcs_dev
Jvcs dev
Diffstat (limited to 'crates')
-rw-r--r--crates/vcs/src/constants.rs2
-rw-r--r--crates/vcs/src/data.rs (renamed from crates/vcs/src/workspace.rs)0
-rw-r--r--crates/vcs/src/data/local.rs (renamed from crates/vcs/src/workspace/local.rs)2
-rw-r--r--crates/vcs/src/data/local/config.rs (renamed from crates/vcs/src/workspace/local/config.rs)2
-rw-r--r--crates/vcs/src/data/member.rs (renamed from crates/vcs/src/workspace/member.rs)0
-rw-r--r--crates/vcs/src/data/user.rs28
-rw-r--r--crates/vcs/src/data/user/accounts.rs161
-rw-r--r--crates/vcs/src/data/vault.rs (renamed from crates/vcs/src/workspace/vault.rs)2
-rw-r--r--crates/vcs/src/data/vault/config.rs (renamed from crates/vcs/src/workspace/vault/config.rs)4
-rw-r--r--crates/vcs/src/data/vault/member.rs (renamed from crates/vcs/src/workspace/vault/member.rs)51
-rw-r--r--crates/vcs/src/data/vault/virtual_file.rs (renamed from crates/vcs/src/workspace/vault/virtual_file.rs)2
-rw-r--r--crates/vcs/src/lib.rs2
-rw-r--r--crates/vcs/src/workspace/user.rs1
-rw-r--r--crates/vcs/src/workspace/user/accounts.rs1
-rw-r--r--crates/vcs/vcs_test/src/lib.rs3
-rw-r--r--crates/vcs/vcs_test/src/test_local_workspace_setup_and_account_management.rs248
-rw-r--r--crates/vcs/vcs_test/src/test_vault_setup_and_member_register.rs2
-rw-r--r--crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs2
18 files changed, 493 insertions, 20 deletions
diff --git a/crates/vcs/src/constants.rs b/crates/vcs/src/constants.rs
index a55aef5..bc30672 100644
--- a/crates/vcs/src/constants.rs
+++ b/crates/vcs/src/constants.rs
@@ -46,5 +46,5 @@ pub const CLIENT_FILE_README: &str = "./README.md";
// User - Verify (Documents path)
pub const USER_FILE_ACCOUNTS: &str = "./accounts/";
-pub const USER_FILE_KEY_PUB: &str = "./accounts/{self_id}_private.pem";
+pub const USER_FILE_KEY: &str = "./accounts/{self_id}_private.pem";
pub const USER_FILE_MEMBER: &str = "./accounts/{self_id}.toml";
diff --git a/crates/vcs/src/workspace.rs b/crates/vcs/src/data.rs
index 63411a6..63411a6 100644
--- a/crates/vcs/src/workspace.rs
+++ b/crates/vcs/src/data.rs
diff --git a/crates/vcs/src/workspace/local.rs b/crates/vcs/src/data/local.rs
index 8399b6d..1c99832 100644
--- a/crates/vcs/src/workspace/local.rs
+++ b/crates/vcs/src/data/local.rs
@@ -6,7 +6,7 @@ use tokio::fs;
use crate::{
constants::{CLIENT_FILE_README, CLIENT_FILE_WORKSPACE},
current::{current_local_path, find_local_path},
- workspace::local::config::LocalConfig,
+ data::local::config::LocalConfig,
};
pub mod config;
diff --git a/crates/vcs/src/workspace/local/config.rs b/crates/vcs/src/data/local/config.rs
index d641880..e024569 100644
--- a/crates/vcs/src/workspace/local/config.rs
+++ b/crates/vcs/src/data/local/config.rs
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
use crate::constants::CLIENT_FILE_WORKSPACE;
use crate::constants::PORT;
-use crate::workspace::vault::MemberId;
+use crate::data::vault::MemberId;
#[derive(Serialize, Deserialize, ConfigFile)]
#[cfg_file(path = CLIENT_FILE_WORKSPACE)]
diff --git a/crates/vcs/src/workspace/member.rs b/crates/vcs/src/data/member.rs
index 208c78c..208c78c 100644
--- a/crates/vcs/src/workspace/member.rs
+++ b/crates/vcs/src/data/member.rs
diff --git a/crates/vcs/src/data/user.rs b/crates/vcs/src/data/user.rs
new file mode 100644
index 0000000..0abd098
--- /dev/null
+++ b/crates/vcs/src/data/user.rs
@@ -0,0 +1,28 @@
+use crate::current::current_doc_dir;
+use std::path::PathBuf;
+
+pub mod accounts;
+
+pub struct UserDirectory {
+ local_path: PathBuf,
+}
+
+impl UserDirectory {
+ /// Create a user ditectory struct from the current system's document directory
+ pub fn current_doc_dir() -> Option<Self> {
+ Some(UserDirectory {
+ local_path: current_doc_dir()?,
+ })
+ }
+
+ /// Create a user directory struct from a specified directory path
+ /// Returns None if the directory does not exist
+ pub fn from_path<P: Into<PathBuf>>(path: P) -> Option<Self> {
+ let local_path = path.into();
+ if local_path.exists() {
+ Some(UserDirectory { local_path })
+ } else {
+ None
+ }
+ }
+}
diff --git a/crates/vcs/src/data/user/accounts.rs b/crates/vcs/src/data/user/accounts.rs
new file mode 100644
index 0000000..83ebda9
--- /dev/null
+++ b/crates/vcs/src/data/user/accounts.rs
@@ -0,0 +1,161 @@
+use std::{
+ fs,
+ io::{Error, ErrorKind},
+ path::PathBuf,
+};
+
+use cfg_file::config::ConfigFile;
+
+use crate::{
+ constants::{USER_FILE_ACCOUNTS, USER_FILE_KEY, USER_FILE_MEMBER},
+ data::{member::Member, user::UserDirectory, vault::MemberId},
+};
+
+const SELF_ID: &str = "{self_id}";
+
+/// Account Management
+impl UserDirectory {
+ /// Read account from configuration file
+ pub async fn account(&self, id: &MemberId) -> Result<Member, std::io::Error> {
+ if let Some(cfg_file) = self.account_cfg(id) {
+ let member = Member::read_from(cfg_file).await?;
+ return Ok(member);
+ }
+
+ Err(Error::new(ErrorKind::NotFound, "Account not found!"))
+ }
+
+ /// List all account IDs in the user directory
+ pub fn account_ids(&self) -> Result<Vec<MemberId>, std::io::Error> {
+ let accounts_path = self
+ .local_path
+ .join(USER_FILE_ACCOUNTS.replace(SELF_ID, ""));
+
+ if !accounts_path.exists() {
+ return Ok(Vec::new());
+ }
+
+ let mut account_ids = Vec::new();
+
+ for entry in fs::read_dir(accounts_path)? {
+ let entry = entry?;
+ let path = entry.path();
+
+ if path.is_file()
+ && let Some(file_name) = path.file_stem().and_then(|s| s.to_str())
+ && path.extension().and_then(|s| s.to_str()) == Some("toml")
+ {
+ // Remove the "_private" suffix from key files if present
+ let account_id = file_name.replace("_private", "");
+ account_ids.push(account_id);
+ }
+ }
+
+ Ok(account_ids)
+ }
+
+ /// Get all accounts
+ /// This method will read and deserialize account information, please pay attention to performance issues
+ pub async fn accounts(&self) -> Result<Vec<Member>, std::io::Error> {
+ let mut accounts = Vec::new();
+
+ for account_id in self.account_ids()? {
+ if let Ok(account) = self.account(&account_id).await {
+ accounts.push(account);
+ }
+ }
+
+ Ok(accounts)
+ }
+
+ /// Update account info
+ pub async fn update_account(&self, member: Member) -> Result<(), std::io::Error> {
+ // Ensure account exist
+ if self.account_cfg(&member.id()).is_some() {
+ let account_cfg_path = self.account_cfg_path(&member.id());
+ Member::write_to(&member, account_cfg_path).await?;
+ return Ok(());
+ }
+
+ Err(Error::new(ErrorKind::NotFound, "Account not found!"))
+ }
+
+ /// Register an account to user directory
+ pub async fn register_account(&self, member: Member) -> Result<(), std::io::Error> {
+ // Ensure account not exist
+ if self.account_cfg(&member.id()).is_some() {
+ return Err(Error::new(
+ ErrorKind::DirectoryNotEmpty,
+ format!("Account `{}` already registered!", member.id()),
+ ));
+ }
+
+ // Ensure accounts directory exists
+ let accounts_dir = self
+ .local_path
+ .join(USER_FILE_ACCOUNTS.replace(SELF_ID, ""));
+ if !accounts_dir.exists() {
+ fs::create_dir_all(&accounts_dir)?;
+ }
+
+ // Write config file to accounts dir
+ let account_cfg_path = self.account_cfg_path(&member.id());
+ Member::write_to(&member, account_cfg_path).await?;
+
+ Ok(())
+ }
+
+ /// Remove account from user directory
+ pub fn remove_account(&self, id: &MemberId) -> Result<(), std::io::Error> {
+ // Remove config file if exists
+ if let Some(account_cfg_path) = self.account_cfg(id) {
+ fs::remove_file(account_cfg_path)?;
+ }
+
+ // Remove private key file if exists
+ if let Some(private_key_path) = self.account_private_key(id)
+ && private_key_path.exists()
+ {
+ fs::remove_file(private_key_path)?;
+ }
+
+ Ok(())
+ }
+
+ /// Try to get the account's configuration file to determine if the account exists
+ pub fn account_cfg(&self, id: &MemberId) -> Option<PathBuf> {
+ let cfg_file = self.account_cfg_path(id);
+ if cfg_file.exists() {
+ Some(cfg_file)
+ } else {
+ None
+ }
+ }
+
+ /// Try to get the account's private key file to determine if the account has a private key
+ pub fn account_private_key(&self, id: &MemberId) -> Option<PathBuf> {
+ let key_file = self.account_private_key_path(id);
+ if key_file.exists() {
+ Some(key_file)
+ } else {
+ None
+ }
+ }
+
+ /// Check if account has private key
+ pub fn has_private_key(&self, id: &MemberId) -> bool {
+ self.account_private_key(id).is_some()
+ }
+
+ /// Get the account's configuration file path, but do not check if the file exists
+ pub fn account_cfg_path(&self, id: &MemberId) -> PathBuf {
+ self.local_path
+ .join(USER_FILE_MEMBER.replace(SELF_ID, id.to_string().as_str()))
+ }
+
+ /// Get the account's private key file path, but do not check if the file exists
+ pub fn account_private_key_path(&self, id: &MemberId) -> PathBuf {
+ self.local_path
+ .join(USER_FILE_KEY.replace(SELF_ID, id.to_string().as_str()))
+ }
+}
diff --git a/crates/vcs/src/workspace/vault.rs b/crates/vcs/src/data/vault.rs
index 7f52c9c..9b400bb 100644
--- a/crates/vcs/src/workspace/vault.rs
+++ b/crates/vcs/src/data/vault.rs
@@ -12,7 +12,7 @@ use crate::{
SERVER_PATH_SHEETS, SERVER_PATH_VF_ROOT,
},
current::{current_vault_path, find_vault_path},
- workspace::vault::config::VaultConfig,
+ data::vault::config::VaultConfig,
};
pub mod config;
diff --git a/crates/vcs/src/workspace/vault/config.rs b/crates/vcs/src/data/vault/config.rs
index 5414e4d..11917de 100644
--- a/crates/vcs/src/workspace/vault/config.rs
+++ b/crates/vcs/src/data/vault/config.rs
@@ -2,8 +2,8 @@ use cfg_file::ConfigFile;
use serde::{Deserialize, Serialize};
use crate::constants::SERVER_FILE_VAULT;
-use crate::workspace::member::Member;
-use crate::workspace::vault::MemberId;
+use crate::data::member::Member;
+use crate::data::vault::MemberId;
#[derive(Serialize, Deserialize, ConfigFile)]
#[cfg_file(path = SERVER_FILE_VAULT)]
diff --git a/crates/vcs/src/workspace/vault/member.rs b/crates/vcs/src/data/vault/member.rs
index 2d00081..9482d30 100644
--- a/crates/vcs/src/workspace/vault/member.rs
+++ b/crates/vcs/src/data/vault/member.rs
@@ -7,8 +7,8 @@ use std::{
use cfg_file::config::ConfigFile;
use crate::{
- constants::{SERVER_FILE_MEMBER_INFO, SERVER_FILE_MEMBER_PUB},
- workspace::{
+ constants::{SERVER_FILE_MEMBER_INFO, SERVER_FILE_MEMBER_PUB, SERVER_PATH_MEMBERS},
+ data::{
member::Member,
vault::{MemberId, Vault},
},
@@ -28,6 +28,45 @@ impl Vault {
Err(Error::new(ErrorKind::NotFound, "Member not found!"))
}
+ /// List all member IDs in the vault
+ pub fn member_ids(&self) -> Result<Vec<MemberId>, std::io::Error> {
+ let members_path = self.vault_path.join(SERVER_PATH_MEMBERS);
+
+ if !members_path.exists() {
+ return Ok(Vec::new());
+ }
+
+ let mut member_ids = Vec::new();
+
+ for entry in fs::read_dir(members_path)? {
+ let entry = entry?;
+ let path = entry.path();
+
+ if path.is_file()
+ && let Some(file_name) = path.file_stem().and_then(|s| s.to_str())
+ && path.extension().and_then(|s| s.to_str()) == Some("toml")
+ {
+ member_ids.push(file_name.to_string());
+ }
+ }
+
+ Ok(member_ids)
+ }
+
+ /// Get all members
+ /// This method will read and deserialize member information, please pay attention to performance issues
+ pub async fn members(&self) -> Result<Vec<Member>, std::io::Error> {
+ let mut members = Vec::new();
+
+ for member_id in self.member_ids()? {
+ if let Ok(member) = self.member(&member_id).await {
+ members.push(member);
+ }
+ }
+
+ Ok(members)
+ }
+
/// Update member info
pub async fn update_member(&self, member: Member) -> Result<(), std::io::Error> {
// Ensure member exist
@@ -89,17 +128,13 @@ impl Vault {
/// Get the member's configuration file path, but do not check if the file exists
pub fn member_cfg_path(&self, id: &MemberId) -> PathBuf {
-
- self
- .vault_path
+ self.vault_path
.join(SERVER_FILE_MEMBER_INFO.replace(ID_PARAM, id.to_string().as_str()))
}
/// Get the member's public key file path, but do not check if the file exists
pub fn member_key_path(&self, id: &MemberId) -> PathBuf {
-
- self
- .vault_path
+ self.vault_path
.join(SERVER_FILE_MEMBER_PUB.replace(ID_PARAM, id.to_string().as_str()))
}
}
diff --git a/crates/vcs/src/workspace/vault/virtual_file.rs b/crates/vcs/src/data/vault/virtual_file.rs
index c83f700..04b5236 100644
--- a/crates/vcs/src/workspace/vault/virtual_file.rs
+++ b/crates/vcs/src/data/vault/virtual_file.rs
@@ -16,7 +16,7 @@ use crate::{
SERVER_FILE_VF_META, SERVER_FILE_VF_VERSION_INSTANCE, SERVER_PATH_VF_ROOT,
SERVER_PATH_VF_STORAGE, SERVER_PATH_VF_TEMP,
},
- workspace::vault::{MemberId, Vault},
+ data::vault::{MemberId, Vault},
};
pub type VirtualFileId = String;
diff --git a/crates/vcs/src/lib.rs b/crates/vcs/src/lib.rs
index 15a315f..1b41391 100644
--- a/crates/vcs/src/lib.rs
+++ b/crates/vcs/src/lib.rs
@@ -2,4 +2,4 @@ pub mod constants;
pub mod current;
#[allow(dead_code)]
-pub mod workspace;
+pub mod data;
diff --git a/crates/vcs/src/workspace/user.rs b/crates/vcs/src/workspace/user.rs
deleted file mode 100644
index 9bb4894..0000000
--- a/crates/vcs/src/workspace/user.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod accounts;
diff --git a/crates/vcs/src/workspace/user/accounts.rs b/crates/vcs/src/workspace/user/accounts.rs
deleted file mode 100644
index 8b13789..0000000
--- a/crates/vcs/src/workspace/user/accounts.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/crates/vcs/vcs_test/src/lib.rs b/crates/vcs/vcs_test/src/lib.rs
index 357ea3f..d9e6f94 100644
--- a/crates/vcs/vcs_test/src/lib.rs
+++ b/crates/vcs/vcs_test/src/lib.rs
@@ -8,6 +8,9 @@ pub mod test_vault_setup_and_member_register;
#[cfg(test)]
pub mod test_virtual_file_creation_and_update;
+#[cfg(test)]
+pub mod test_local_workspace_setup_and_account_management;
+
pub async fn get_test_dir(area: &str) -> Result<PathBuf, std::io::Error> {
let dir = current_dir()?.join(".temp").join("test").join(area);
if !dir.exists() {
diff --git a/crates/vcs/vcs_test/src/test_local_workspace_setup_and_account_management.rs b/crates/vcs/vcs_test/src/test_local_workspace_setup_and_account_management.rs
new file mode 100644
index 0000000..df766f7
--- /dev/null
+++ b/crates/vcs/vcs_test/src/test_local_workspace_setup_and_account_management.rs
@@ -0,0 +1,248 @@
+use std::io::Error;
+
+use cfg_file::config::ConfigFile;
+use vcs::{
+ constants::{CLIENT_FILE_README, CLIENT_FILE_WORKSPACE, USER_FILE_KEY, USER_FILE_MEMBER},
+ data::{
+ local::{LocalWorkspace, config::LocalConfig},
+ member::Member,
+ user::UserDirectory,
+ },
+};
+
+use crate::get_test_dir;
+
+#[tokio::test]
+async fn test_local_workspace_setup_and_account_management() -> Result<(), std::io::Error> {
+ let dir = get_test_dir("local_workspace_account_management").await?;
+
+ // Setup local workspace
+ LocalWorkspace::setup_local_workspace(dir.clone()).await?;
+
+ // Check if the following files are created in `dir`:
+ // Files: CLIENT_FILE_WORKSPACE, CLIENT_FILE_README
+ assert!(dir.join(CLIENT_FILE_WORKSPACE).exists());
+ assert!(dir.join(CLIENT_FILE_README).exists());
+
+ // Get local workspace
+ let config = LocalConfig::read_from(dir.join(CLIENT_FILE_WORKSPACE)).await?;
+ let Some(_local_workspace) = LocalWorkspace::init(config, &dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "Local workspace not found!",
+ ));
+ };
+
+ // Create user directory from workspace path
+ let Some(user_directory) = UserDirectory::from_path(&dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "User directory not found!",
+ ));
+ };
+
+ // Test account registration
+ let member_id = "test_account";
+ let member = Member::new(member_id);
+
+ // Register account
+ user_directory.register_account(member.clone()).await?;
+
+ // Check if the account config file exists
+ assert!(
+ dir.join(USER_FILE_MEMBER.replace("{self_id}", member_id))
+ .exists()
+ );
+
+ // Test account retrieval
+ let retrieved_member = user_directory.account(&member_id.to_string()).await?;
+ assert_eq!(retrieved_member.id(), member.id());
+
+ // Test account IDs listing
+ let account_ids = user_directory.account_ids()?;
+ assert!(account_ids.contains(&member_id.to_string()));
+
+ // Test accounts listing
+ let accounts = user_directory.accounts().await?;
+ assert_eq!(accounts.len(), 1);
+ assert_eq!(accounts[0].id(), member.id());
+
+ // Test account existence check
+ assert!(user_directory.account_cfg(&member_id.to_string()).is_some());
+
+ // Test private key check (should be false initially)
+ assert!(!user_directory.has_private_key(&member_id.to_string()));
+
+ // Test account update
+ let mut updated_member = member.clone();
+ updated_member.set_metadata("email", "test@example.com");
+ user_directory
+ .update_account(updated_member.clone())
+ .await?;
+
+ // Verify update
+ let updated_retrieved = user_directory.account(&member_id.to_string()).await?;
+ assert_eq!(
+ updated_retrieved.metadata("email"),
+ Some(&"test@example.com".to_string())
+ );
+
+ // Test account removal
+ user_directory.remove_account(&member_id.to_string())?;
+
+ // Check if the account config file no longer exists
+ assert!(
+ !dir.join(USER_FILE_MEMBER.replace("{self_id}", member_id))
+ .exists()
+ );
+
+ // Check if account is no longer in the list
+ let account_ids_after_removal = user_directory.account_ids()?;
+ assert!(!account_ids_after_removal.contains(&member_id.to_string()));
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn test_account_private_key_management() -> Result<(), std::io::Error> {
+ let dir = get_test_dir("account_private_key_management").await?;
+
+ // Create user directory
+ let Some(user_directory) = UserDirectory::from_path(&dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "User directory not found!",
+ ));
+ };
+
+ // Register account
+ let member_id = "test_account_with_key";
+ let member = Member::new(member_id);
+ user_directory.register_account(member).await?;
+
+ // Create a dummy private key file for testing
+ let private_key_path = dir.join(USER_FILE_KEY.replace("{self_id}", member_id));
+ std::fs::create_dir_all(private_key_path.parent().unwrap())?;
+ std::fs::write(&private_key_path, "dummy_private_key_content")?;
+
+ // Test private key existence check
+ assert!(user_directory.has_private_key(&member_id.to_string()));
+
+ // Test private key path retrieval
+ assert!(
+ user_directory
+ .account_private_key(&member_id.to_string())
+ .is_some()
+ );
+
+ // Remove account (should also remove private key)
+ user_directory.remove_account(&member_id.to_string())?;
+
+ // Check if private key file is also removed
+ assert!(!private_key_path.exists());
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn test_multiple_account_management() -> Result<(), std::io::Error> {
+ let dir = get_test_dir("multiple_account_management").await?;
+
+ // Create user directory
+ let Some(user_directory) = UserDirectory::from_path(&dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "User directory not found!",
+ ));
+ };
+
+ // Register multiple accounts
+ let account_names = vec!["alice", "bob", "charlie"];
+
+ for name in &account_names {
+ user_directory.register_account(Member::new(*name)).await?;
+ }
+
+ // Test account IDs listing
+ let account_ids = user_directory.account_ids()?;
+ assert_eq!(account_ids.len(), 3);
+
+ for name in &account_names {
+ assert!(account_ids.contains(&name.to_string()));
+ }
+
+ // Test accounts listing
+ let accounts = user_directory.accounts().await?;
+ assert_eq!(accounts.len(), 3);
+
+ // Remove one account
+ user_directory.remove_account(&"bob".to_string())?;
+
+ // Verify removal
+ let account_ids_after_removal = user_directory.account_ids()?;
+ assert_eq!(account_ids_after_removal.len(), 2);
+ assert!(!account_ids_after_removal.contains(&"bob".to_string()));
+ assert!(account_ids_after_removal.contains(&"alice".to_string()));
+ assert!(account_ids_after_removal.contains(&"charlie".to_string()));
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn test_account_registration_duplicate_prevention() -> Result<(), std::io::Error> {
+ let dir = get_test_dir("account_duplicate_prevention").await?;
+
+ // Create user directory
+ let Some(user_directory) = UserDirectory::from_path(&dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "User directory not found!",
+ ));
+ };
+
+ // Register account
+ let member_id = "duplicate_test";
+ user_directory
+ .register_account(Member::new(member_id))
+ .await?;
+
+ // Try to register same account again - should fail
+ let result = user_directory
+ .register_account(Member::new(member_id))
+ .await;
+ assert!(result.is_err());
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn test_nonexistent_account_operations() -> Result<(), std::io::Error> {
+ let dir = get_test_dir("nonexistent_account_operations").await?;
+
+ // Create user directory
+ let Some(user_directory) = UserDirectory::from_path(&dir) else {
+ return Err(Error::new(
+ std::io::ErrorKind::NotFound,
+ "User directory not found!",
+ ));
+ };
+
+ // Try to read non-existent account - should fail
+ let result = user_directory.account(&"nonexistent".to_string()).await;
+ assert!(result.is_err());
+
+ // Try to update non-existent account - should fail
+ let result = user_directory
+ .update_account(Member::new("nonexistent"))
+ .await;
+ assert!(result.is_err());
+
+ // Try to remove non-existent account - should succeed (idempotent)
+ let result = user_directory.remove_account(&"nonexistent".to_string());
+ assert!(result.is_ok());
+
+ // Check private key for non-existent account - should be false
+ assert!(!user_directory.has_private_key(&"nonexistent".to_string()));
+
+ Ok(())
+}
diff --git a/crates/vcs/vcs_test/src/test_vault_setup_and_member_register.rs b/crates/vcs/vcs_test/src/test_vault_setup_and_member_register.rs
index ced027d..6a30cf7 100644
--- a/crates/vcs/vcs_test/src/test_vault_setup_and_member_register.rs
+++ b/crates/vcs/vcs_test/src/test_vault_setup_and_member_register.rs
@@ -6,7 +6,7 @@ use vcs::{
SERVER_FILE_MEMBER_INFO, SERVER_FILE_README, SERVER_FILE_VAULT, SERVER_PATH_MEMBER_PUB,
SERVER_PATH_MEMBERS, SERVER_PATH_SHEETS, SERVER_PATH_VF_ROOT,
},
- workspace::{
+ data::{
member::Member,
vault::{Vault, config::VaultConfig},
},
diff --git a/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs b/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
index bfcf817..598e7be 100644
--- a/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
+++ b/crates/vcs/vcs_test/src/test_virtual_file_creation_and_update.rs
@@ -12,7 +12,7 @@ use tokio::{
};
use vcs::{
constants::SERVER_FILE_VAULT,
- workspace::{
+ data::{
member::Member,
vault::{Vault, config::VaultConfig, virtual_file::VirtualFileVersionDescription},
},