diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-02-05 22:35:05 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-02-05 22:35:05 +0800 |
| commit | 27f6414ad1ff451feb0044af62f37dc2a6255ffa (patch) | |
| tree | cb5693bc014cc8579dcf02a730fd4d2a5dfcf1a5 /legacy_data/tests/src | |
| parent | ade2fcb9302a4ab759795820dbde3b2b269490ee (diff) | |
Remove examples and legacy code, update .gitignore
- Delete examples directory and its example action system
- Rename actions/ to legacy_actions/ and data/ to legacy_data/
- Update Cargo.toml license file reference
- Move setup scripts to scripts/dev/ directory
- Add todo.txt patterns to .gitignore
Diffstat (limited to 'legacy_data/tests/src')
6 files changed, 1413 insertions, 0 deletions
diff --git a/legacy_data/tests/src/lib.rs b/legacy_data/tests/src/lib.rs new file mode 100644 index 0000000..ced2d3d --- /dev/null +++ b/legacy_data/tests/src/lib.rs @@ -0,0 +1,30 @@ +use std::{env::current_dir, path::PathBuf}; + +use tokio::fs; + +#[cfg(test)] +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; + +#[cfg(test)] +pub mod test_sheet_creation_management_and_persistence; + +#[cfg(test)] +pub mod test_sheet_share_creation_and_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() { + std::fs::create_dir_all(&dir)?; + } else { + // Regenerate existing directory + fs::remove_dir_all(&dir).await?; + fs::create_dir_all(&dir).await?; + } + Ok(dir) +} diff --git a/legacy_data/tests/src/test_local_workspace_setup_and_account_management.rs b/legacy_data/tests/src/test_local_workspace_setup_and_account_management.rs new file mode 100644 index 0000000..edb9295 --- /dev/null +++ b/legacy_data/tests/src/test_local_workspace_setup_and_account_management.rs @@ -0,0 +1,248 @@ +use std::io::Error; + +use cfg_file::config::ConfigFile; +use vcs_data::{ + constants::{CLIENT_FILE_TODOLIST, CLIENT_FILE_WORKSPACE, USER_FILE_KEY, USER_FILE_MEMBER}, + data::{ + local::{LocalWorkspace, workspace_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_TODOLIST).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/legacy_data/tests/src/test_sheet_creation_management_and_persistence.rs b/legacy_data/tests/src/test_sheet_creation_management_and_persistence.rs new file mode 100644 index 0000000..adff1bd --- /dev/null +++ b/legacy_data/tests/src/test_sheet_creation_management_and_persistence.rs @@ -0,0 +1,275 @@ +use std::io::Error; + +use cfg_file::config::ConfigFile; +use vcs_data::{ + constants::{SERVER_FILE_SHEET, SERVER_FILE_VAULT}, + data::{ + member::{Member, MemberId}, + sheet::SheetName, + vault::{Vault, vault_config::VaultConfig, virtual_file::VirtualFileId}, + }, +}; + +use crate::get_test_dir; + +#[tokio::test] +async fn test_sheet_creation_management_and_persistence() -> Result<(), std::io::Error> { + let dir = get_test_dir("sheet_management").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add a member to use as sheet holder + let member_id: MemberId = "test_member".to_string(); + vault + .register_member_to_vault(Member::new(&member_id)) + .await?; + + // Test 1: Create a new sheet + let sheet_name: SheetName = "test_sheet".to_string(); + let sheet = vault.create_sheet(&sheet_name, &member_id).await?; + + // Verify sheet properties + assert_eq!(sheet.holder(), Some(&member_id)); + assert_eq!(sheet.holder(), Some(&member_id)); + assert!(sheet.mapping().is_empty()); + + // Verify sheet file was created + let sheet_path = dir.join(SERVER_FILE_SHEET.replace("{sheet_name}", &sheet_name)); + assert!(sheet_path.exists()); + + // Test 2: Add mapping entries to the sheet + let mut sheet = vault.sheet(&sheet_name).await?; + + // Add mapping entries for the files + let main_rs_path = vcs_data::data::sheet::SheetPathBuf::from("src/main.rs"); + let lib_rs_path = vcs_data::data::sheet::SheetPathBuf::from("src/lib.rs"); + let main_rs_id = VirtualFileId::new(); + let lib_rs_id = VirtualFileId::new(); + + sheet + .add_mapping( + main_rs_path.clone(), + main_rs_id.clone(), + "1.0.0".to_string(), + ) + .await?; + sheet + .add_mapping(lib_rs_path.clone(), lib_rs_id.clone(), "1.0.0".to_string()) + .await?; + + // Verify mappings were added + assert_eq!(sheet.mapping().len(), 2); + + // Test 3: Add more mapping entries + let mapping_path = vcs_data::data::sheet::SheetPathBuf::from("output/build.exe"); + let virtual_file_id = VirtualFileId::new(); + + sheet + .add_mapping( + mapping_path.clone(), + virtual_file_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + // Verify mapping was added + assert_eq!(sheet.mapping().len(), 3); + assert_eq!( + sheet.mapping().get(&mapping_path).map(|meta| &meta.id), + Some(&virtual_file_id) + ); + + // Test 4: Persist sheet to disk + sheet.persist().await?; + + // Verify persistence by reloading the sheet + let reloaded_sheet = vault.sheet(&sheet_name).await?; + assert_eq!(reloaded_sheet.holder(), Some(&member_id)); + assert_eq!(reloaded_sheet.mapping().len(), 3); + + // Test 5: Remove mapping entry + let mut sheet_for_removal = vault.sheet(&sheet_name).await?; + let _removed_virtual_file_id = sheet_for_removal.remove_mapping(&mapping_path).await; + // Don't check the return value since it depends on virtual file existence + assert_eq!(sheet_for_removal.mapping().len(), 2); + + // Test 6: List all sheets in vault + let sheet_names = vault.sheet_names()?; + assert_eq!(sheet_names.len(), 2); + assert!(sheet_names.contains(&sheet_name)); + assert!(sheet_names.contains(&"ref".to_string())); + + let all_sheets = vault.sheets().await?; + assert_eq!(all_sheets.len(), 2); + // One sheet should be the test sheet, the other should be the ref sheet with host as holder + let test_sheet_holder = all_sheets + .iter() + .find(|s| s.holder() == Some(&member_id)) + .map(|s| s.holder()) + .unwrap(); + let ref_sheet_holder = all_sheets + .iter() + .find(|s| s.holder() == Some(&"host".to_string())) + .map(|s| s.holder()) + .unwrap(); + assert_eq!(test_sheet_holder, Some(&member_id)); + assert_eq!(ref_sheet_holder, Some(&"host".to_string())); + + // Test 7: Safe deletion (move to trash) + vault.delete_sheet_safely(&sheet_name).await?; + + // Verify sheet is not in normal listing but can be restored + let sheet_names_after_deletion = vault.sheet_names()?; + assert_eq!(sheet_names_after_deletion.len(), 1); + assert_eq!(sheet_names_after_deletion[0], "ref"); + + // Test 8: Restore sheet from trash + let restored_sheet = vault.sheet(&sheet_name).await?; + assert_eq!(restored_sheet.holder(), Some(&member_id)); + assert_eq!(restored_sheet.holder(), Some(&member_id)); + + // Verify sheet is back in normal listing + let sheet_names_after_restore = vault.sheet_names()?; + assert_eq!(sheet_names_after_restore.len(), 2); + assert!(sheet_names_after_restore.contains(&sheet_name)); + assert!(sheet_names_after_restore.contains(&"ref".to_string())); + + // Test 9: Permanent deletion + vault.delete_sheet(&sheet_name).await?; + + // Verify sheet is permanently gone + let sheet_names_final = vault.sheet_names()?; + assert_eq!(sheet_names_final.len(), 1); + assert_eq!(sheet_names_final[0], "ref"); + + // Attempt to access deleted sheet should fail + let result = vault.sheet(&sheet_name).await; + assert!(result.is_err()); + + // Clean up: Remove member + vault.remove_member_from_vault(&member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_sheet_error_conditions() -> Result<(), std::io::Error> { + let dir = get_test_dir("sheet_error_conditions").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Test 1: Create sheet with non-existent member should fail + let non_existent_member: MemberId = "non_existent_member".to_string(); + let sheet_name: SheetName = "test_sheet".to_string(); + + let result = vault.create_sheet(&sheet_name, &non_existent_member).await; + assert!(result.is_err()); + + // Add a member first + let member_id: MemberId = "test_member".to_string(); + vault + .register_member_to_vault(Member::new(&member_id)) + .await?; + + // Test 2: Create duplicate sheet should fail + vault.create_sheet(&sheet_name, &member_id).await?; + let result = vault.create_sheet(&sheet_name, &member_id).await; + assert!(result.is_err()); + + // Test 3: Delete non-existent sheet should fail + let non_existent_sheet: SheetName = "non_existent_sheet".to_string(); + let result = vault.delete_sheet(&non_existent_sheet).await; + assert!(result.is_err()); + + // Test 4: Safe delete non-existent sheet should fail + let result = vault.delete_sheet_safely(&non_existent_sheet).await; + assert!(result.is_err()); + + // Test 5: Restore non-existent sheet from trash should fail + let result = vault.restore_sheet(&non_existent_sheet).await; + assert!(result.is_err()); + + // Clean up + vault.remove_member_from_vault(&member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_sheet_data_serialization() -> Result<(), std::io::Error> { + let dir = get_test_dir("sheet_serialization").await?; + + // Test serialization by creating a sheet through the vault + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add a member + let member_id: MemberId = "test_member".to_string(); + vault + .register_member_to_vault(Member::new(&member_id)) + .await?; + + // Create a sheet + let sheet_name: SheetName = "test_serialization_sheet".to_string(); + let mut sheet = vault.create_sheet(&sheet_name, &member_id).await?; + + // Add some mappings + let main_rs_path = vcs_data::data::sheet::SheetPathBuf::from("src/main.rs"); + let lib_rs_path = vcs_data::data::sheet::SheetPathBuf::from("src/lib.rs"); + let main_rs_id = VirtualFileId::new(); + let lib_rs_id = VirtualFileId::new(); + + sheet + .add_mapping( + main_rs_path.clone(), + main_rs_id.clone(), + "1.0.0".to_string(), + ) + .await?; + sheet + .add_mapping(lib_rs_path.clone(), lib_rs_id.clone(), "1.0.0".to_string()) + .await?; + + // Add more mappings + let build_exe_id = VirtualFileId::new(); + + sheet + .add_mapping( + vcs_data::data::sheet::SheetPathBuf::from("output/build.exe"), + build_exe_id, + "1.0.0".to_string(), + ) + .await?; + + // Persist the sheet + sheet.persist().await?; + + // Verify the sheet file was created + let sheet_path = dir.join(SERVER_FILE_SHEET.replace("{sheet_name}", &sheet_name)); + assert!(sheet_path.exists()); + + // Clean up + vault.remove_member_from_vault(&member_id)?; + + Ok(()) +} diff --git a/legacy_data/tests/src/test_sheet_share_creation_and_management.rs b/legacy_data/tests/src/test_sheet_share_creation_and_management.rs new file mode 100644 index 0000000..5123360 --- /dev/null +++ b/legacy_data/tests/src/test_sheet_share_creation_and_management.rs @@ -0,0 +1,631 @@ +use std::io::Error; + +use cfg_file::config::ConfigFile; +use vcs_data::{ + constants::SERVER_FILE_VAULT, + data::{ + member::{Member, MemberId}, + sheet::{SheetName, SheetPathBuf}, + vault::{ + Vault, + mapping_share::{Share, ShareMergeMode, SheetShareId}, + vault_config::VaultConfig, + virtual_file::VirtualFileId, + }, + }, +}; + +use crate::get_test_dir; + +#[tokio::test] +async fn test_share_creation_and_retrieval() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_creation").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add members + let sharer_id: MemberId = "sharer_member".to_string(); + let target_member_id: MemberId = "target_member".to_string(); + + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + vault + .register_member_to_vault(Member::new(&target_member_id)) + .await?; + + // Create source sheet for sharer + let source_sheet_name: SheetName = "source_sheet".to_string(); + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + + // Create target sheet for target member + let target_sheet_name: SheetName = "target_sheet".to_string(); + let _target_sheet = vault + .create_sheet(&target_sheet_name, &target_member_id) + .await?; + + // Add mappings to source sheet + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + + let main_rs_path = SheetPathBuf::from("src/main.rs"); + let lib_rs_path = SheetPathBuf::from("src/lib.rs"); + let main_rs_id = VirtualFileId::from("main_rs_id_1"); + let lib_rs_id = VirtualFileId::from("lib_rs_id_1"); + + source_sheet + .add_mapping( + main_rs_path.clone(), + main_rs_id.clone(), + "1.0.0".to_string(), + ) + .await?; + source_sheet + .add_mapping(lib_rs_path.clone(), lib_rs_id.clone(), "1.0.0".to_string()) + .await?; + + // Persist source sheet + source_sheet.persist().await?; + + // Test 1: Share mappings from source sheet to target sheet + let description = "Test share of main.rs and lib.rs".to_string(); + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + + source_sheet + .share_mappings( + &target_sheet_name, + vec![main_rs_path.clone(), lib_rs_path.clone()], + &sharer_id, + description.clone(), + ) + .await?; + + // Test 2: Get shares from target sheet + let target_sheet = vault.sheet(&target_sheet_name).await?; + + let shares = target_sheet.get_shares().await?; + + assert_eq!(shares.len(), 1, "Expected 1 share, found {}", shares.len()); + let share = &shares[0]; + + assert_eq!(share.sharer, sharer_id); + assert_eq!(share.description, description); + assert_eq!(share.from_sheet, source_sheet_name); + assert_eq!(share.mappings.len(), 2); + assert!(share.mappings.contains_key(&main_rs_path)); + assert!(share.mappings.contains_key(&lib_rs_path)); + assert!(share.path.is_some()); + + // Test 3: Get specific share by ID + let share_id = Share::gen_share_id(&sharer_id); + let _specific_share = target_sheet.get_share(&share_id).await; + + // Note: The share ID might not match exactly due to random generation, + // but we can verify the share exists by checking the shares list + assert!(shares.iter().any(|s| s.sharer == sharer_id)); + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + vault.remove_member_from_vault(&target_member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_merge_modes() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_merge_modes").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add members + let sharer_id: MemberId = "sharer".to_string(); + let target_member_id: MemberId = "target".to_string(); + + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + vault + .register_member_to_vault(Member::new(&target_member_id)) + .await?; + + // Create source and target sheets + let source_sheet_name: SheetName = "source".to_string(); + let target_sheet_name: SheetName = "target".to_string(); + + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + let _target_sheet = vault + .create_sheet(&target_sheet_name, &target_member_id) + .await?; + + // Add mappings to source sheet + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + + let file1_path = SheetPathBuf::from("src/file1.rs"); + let file2_path = SheetPathBuf::from("src/file2.rs"); + let file1_id = VirtualFileId::from("file1_id_1"); + let file2_id = VirtualFileId::from("file2_id_1"); + + source_sheet + .add_mapping(file1_path.clone(), file1_id.clone(), "1.0.0".to_string()) + .await?; + source_sheet + .add_mapping(file2_path.clone(), file2_id.clone(), "1.0.0".to_string()) + .await?; + + source_sheet.persist().await?; + + // Share mappings + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + source_sheet + .share_mappings( + &target_sheet_name, + vec![file1_path.clone(), file2_path.clone()], + &sharer_id, + "Test share".to_string(), + ) + .await?; + + // Get the share + let target_sheet = vault.sheet(&target_sheet_name).await?; + let shares = target_sheet.get_shares().await?; + assert_eq!(shares.len(), 1); + let share = shares[0].clone(); + + // Test 4: Safe mode merge (should succeed with no conflicts) + let result = target_sheet + .merge_share(share.clone(), ShareMergeMode::Safe) + .await; + + assert!( + result.is_ok(), + "Safe mode should succeed with no conflicts " + ); + + // Verify mappings were added to target sheet + let updated_target_sheet = vault.sheet(&target_sheet_name).await?; + assert_eq!(updated_target_sheet.mapping().len(), 2); + assert!(updated_target_sheet.mapping().contains_key(&file1_path)); + assert!(updated_target_sheet.mapping().contains_key(&file2_path)); + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + vault.remove_member_from_vault(&target_member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_merge_conflicts() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_conflicts").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add members + let sharer_id: MemberId = "sharer".to_string(); + let target_member_id: MemberId = "target".to_string(); + + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + vault + .register_member_to_vault(Member::new(&target_member_id)) + .await?; + + // Create source and target sheets + let source_sheet_name: SheetName = "source".to_string(); + let target_sheet_name: SheetName = "target".to_string(); + + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + let _target_sheet = vault + .create_sheet(&target_sheet_name, &target_member_id) + .await?; + + // Add conflicting mappings to both sheets + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + let mut target_sheet_mut = vault.sheet(&target_sheet_name).await?; + + let conflicting_path = SheetPathBuf::from("src/conflicting.rs"); + let source_file_id = VirtualFileId::from("source_file_id_1"); + let target_file_id = VirtualFileId::from("target_file_id_1"); + + // Add same path with different IDs to both sheets (conflict) + source_sheet + .add_mapping( + conflicting_path.clone(), + source_file_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + target_sheet_mut + .add_mapping( + conflicting_path.clone(), + target_file_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + source_sheet.persist().await?; + target_sheet_mut.persist().await?; + + // Share the conflicting mapping + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + source_sheet + .share_mappings( + &target_sheet_name, + vec![conflicting_path.clone()], + &sharer_id, + "Conflicting share".to_string(), + ) + .await?; + + // Get the share + let target_sheet = vault.sheet(&target_sheet_name).await?; + let shares = target_sheet.get_shares().await?; + assert_eq!(shares.len(), 1); + let share = shares[0].clone(); + + // Test 5: Safe mode merge with conflict (should fail) + let target_sheet_clone = vault.sheet(&target_sheet_name).await?; + let result = target_sheet_clone + .merge_share(share.clone(), ShareMergeMode::Safe) + .await; + + assert!(result.is_err(), "Safe mode should fail with conflicts"); + + // Test 6: Overwrite mode merge with conflict (should succeed) + let target_sheet_clone = vault.sheet(&target_sheet_name).await?; + let result = target_sheet_clone + .merge_share(share.clone(), ShareMergeMode::Overwrite) + .await; + + assert!( + result.is_ok(), + "Overwrite mode should succeed with conflicts" + ); + + // Verify the mapping was overwritten + let updated_target_sheet = vault.sheet(&target_sheet_name).await?; + let mapping = updated_target_sheet.mapping().get(&conflicting_path); + assert!(mapping.is_some()); + assert_eq!(mapping.unwrap().id, source_file_id); // Should be source's ID, not target's + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + vault.remove_member_from_vault(&target_member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_skip_mode() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_skip_mode").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add members + let sharer_id: MemberId = "sharer".to_string(); + let target_member_id: MemberId = "target".to_string(); + + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + vault + .register_member_to_vault(Member::new(&target_member_id)) + .await?; + + // Create source and target sheets + let source_sheet_name: SheetName = "source".to_string(); + let target_sheet_name: SheetName = "target".to_string(); + + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + let _target_sheet = vault + .create_sheet(&target_sheet_name, &target_member_id) + .await?; + + // Add mappings to both sheets + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + let mut target_sheet_mut = vault.sheet(&target_sheet_name).await?; + + let conflicting_path = SheetPathBuf::from("src/conflicting.rs"); + let non_conflicting_path = SheetPathBuf::from("src/non_conflicting.rs"); + + let source_file_id = VirtualFileId::from("source_file_id_2"); + let target_file_id = VirtualFileId::from("target_file_id_2"); + let non_conflicting_id = VirtualFileId::from("non_conflicting_id_1"); + + // Add conflicting mapping to both sheets + source_sheet + .add_mapping( + conflicting_path.clone(), + source_file_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + target_sheet_mut + .add_mapping( + conflicting_path.clone(), + target_file_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + // Add non-conflicting mapping only to source + source_sheet + .add_mapping( + non_conflicting_path.clone(), + non_conflicting_id.clone(), + "1.0.0".to_string(), + ) + .await?; + + source_sheet.persist().await?; + target_sheet_mut.persist().await?; + + // Share both mappings + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + source_sheet + .share_mappings( + &target_sheet_name, + vec![conflicting_path.clone(), non_conflicting_path.clone()], + &sharer_id, + "Mixed share".to_string(), + ) + .await?; + + // Get the share + let target_sheet = vault.sheet(&target_sheet_name).await?; + let shares = target_sheet.get_shares().await?; + assert_eq!(shares.len(), 1); + let share = shares[0].clone(); + + // Test 7: Skip mode merge with conflict (should skip conflicting, add non-conflicting) + let result = target_sheet + .merge_share(share.clone(), ShareMergeMode::Skip) + .await; + + assert!(result.is_ok(), "Skip mode should succeed"); + + // Verify only non-conflicting mapping was added + let updated_target_sheet = vault.sheet(&target_sheet_name).await?; + + // Conflicting mapping should still have target's ID + let conflicting_mapping = updated_target_sheet.mapping().get(&conflicting_path); + assert!(conflicting_mapping.is_some()); + assert_eq!(conflicting_mapping.unwrap().id, target_file_id); + + // Non-conflicting mapping should be added + let non_conflicting_mapping = updated_target_sheet.mapping().get(&non_conflicting_path); + assert!(non_conflicting_mapping.is_some()); + assert_eq!(non_conflicting_mapping.unwrap().id, non_conflicting_id); + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + vault.remove_member_from_vault(&target_member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_removal() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_removal").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add members + let sharer_id: MemberId = "sharer".to_string(); + let target_member_id: MemberId = "target".to_string(); + + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + vault + .register_member_to_vault(Member::new(&target_member_id)) + .await?; + + // Create source and target sheets + let source_sheet_name: SheetName = "source".to_string(); + let target_sheet_name: SheetName = "target".to_string(); + + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + let _target_sheet = vault + .create_sheet(&target_sheet_name, &target_member_id) + .await?; + + // Add mapping to source sheet + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + + let file_path = SheetPathBuf::from("src/file.rs"); + let file_id = VirtualFileId::from("file_id_1"); + + source_sheet + .add_mapping(file_path.clone(), file_id.clone(), "1.0.0".to_string()) + .await?; + + source_sheet.persist().await?; + + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + // Share mapping + source_sheet + .share_mappings( + &target_sheet_name, + vec![file_path.clone()], + &sharer_id, + "Test share for removal".to_string(), + ) + .await?; + + // Get the share + let target_sheet = vault.sheet(&target_sheet_name).await?; + let shares = target_sheet.get_shares().await?; + assert_eq!(shares.len(), 1); + let share = shares[0].clone(); + + // Test 8: Remove share + let result = share.remove().await; + + // Check if removal succeeded or failed gracefully + match result { + Ok(_) => { + // Share was successfully removed + let shares_after_removal = target_sheet.get_shares().await?; + assert_eq!(shares_after_removal.len(), 0); + } + Err((returned_share, _error)) => { + // Share removal failed, but we got the share backZ + // Error message may vary, just check that we got an error + // The share should be returned in the error + assert_eq!(returned_share.sharer, sharer_id); + } + } + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + vault.remove_member_from_vault(&target_member_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_error_conditions() -> Result<(), std::io::Error> { + let dir = get_test_dir("share_errors").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add member + let sharer_id: MemberId = "sharer".to_string(); + vault + .register_member_to_vault(Member::new(&sharer_id)) + .await?; + + // Create source sheet + let source_sheet_name: SheetName = "source".to_string(); + let _source_sheet = vault.create_sheet(&source_sheet_name, &sharer_id).await?; + + // Add mapping to source sheet + let mut source_sheet = vault.sheet(&source_sheet_name).await?; + + let file_path = SheetPathBuf::from("src/file.rs"); + let file_id = VirtualFileId::from("file_id_2"); + + source_sheet + .add_mapping(file_path.clone(), file_id.clone(), "1.0.0".to_string()) + .await?; + + source_sheet.persist().await?; + + // Test 9: Share to non-existent sheet should fail + let non_existent_sheet: SheetName = "non_existent".to_string(); + // Need to get the sheet again after persist + let source_sheet = vault.sheet(&source_sheet_name).await?; + let result = source_sheet + .share_mappings( + &non_existent_sheet, + vec![file_path.clone()], + &sharer_id, + "Test".to_string(), + ) + .await; + + assert!(result.is_err()); + + // Test 10: Share non-existent mapping should fail + let target_sheet_name: SheetName = "target".to_string(); + let _target_sheet = vault.create_sheet(&target_sheet_name, &sharer_id).await?; + + let non_existent_path = SheetPathBuf::from("src/non_existent.rs"); + let result = source_sheet + .share_mappings( + &target_sheet_name, + vec![non_existent_path], + &sharer_id, + "Test".to_string(), + ) + .await; + + assert!(result.is_err()); + + // Test 11: Merge non-existent share should fail + let target_sheet = vault.sheet(&target_sheet_name).await?; + let non_existent_share_id: SheetShareId = "non_existent_share".to_string(); + let result = target_sheet + .merge_share_by_id(&non_existent_share_id, ShareMergeMode::Safe) + .await; + + assert!(result.is_err()); + + // Clean up + vault.remove_member_from_vault(&sharer_id)?; + + Ok(()) +} + +#[tokio::test] +async fn test_share_id_generation() -> Result<(), std::io::Error> { + // Test 12: Share ID generation + let sharer_id: MemberId = "test_sharer".to_string(); + + // Generate multiple IDs to ensure they're different + let id1 = Share::gen_share_id(&sharer_id); + let id2 = Share::gen_share_id(&sharer_id); + let id3 = Share::gen_share_id(&sharer_id); + + // IDs should be different due to random component + assert_ne!(id1, id2); + assert_ne!(id1, id3); + assert_ne!(id2, id3); + + // IDs should start with sharer name + assert!(id1.starts_with(&format!("test_sharer@"))); + assert!(id2.starts_with(&format!("test_sharer@"))); + assert!(id3.starts_with(&format!("test_sharer@"))); + + Ok(()) +} diff --git a/legacy_data/tests/src/test_vault_setup_and_member_register.rs b/legacy_data/tests/src/test_vault_setup_and_member_register.rs new file mode 100644 index 0000000..d34e48e --- /dev/null +++ b/legacy_data/tests/src/test_vault_setup_and_member_register.rs @@ -0,0 +1,67 @@ +use std::io::Error; + +use cfg_file::config::ConfigFile; +use vcs_data::{ + constants::{ + SERVER_FILE_MEMBER_INFO, SERVER_FILE_README, SERVER_FILE_VAULT, SERVER_PATH_MEMBER_PUB, + SERVER_PATH_MEMBERS, SERVER_PATH_SHEETS, SERVER_PATH_VF_ROOT, + }, + data::{ + member::Member, + vault::{Vault, vault_config::VaultConfig}, + }, +}; + +use crate::get_test_dir; + +#[tokio::test] +async fn test_vault_setup_and_member_register() -> Result<(), std::io::Error> { + let dir = get_test_dir("member_register").await?; + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await?; + + // Check if the following files and directories are created in `dir`: + // Files: SERVER_FILE_VAULT, SERVER_FILE_README + // Directories: SERVER_PATH_SHEETS, + // SERVER_PATH_MEMBERS, + // SERVER_PATH_MEMBER_PUB, + // SERVER_PATH_VIRTUAL_FILE_ROOT + assert!(dir.join(SERVER_FILE_VAULT).exists()); + assert!(dir.join(SERVER_FILE_README).exists()); + assert!(dir.join(SERVER_PATH_SHEETS).exists()); + assert!(dir.join(SERVER_PATH_MEMBERS).exists()); + assert!(dir.join(SERVER_PATH_MEMBER_PUB).exists()); + assert!(dir.join(SERVER_PATH_VF_ROOT).exists()); + + // Get vault + let config = VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)).await?; + let Some(vault) = Vault::init(config, &dir) else { + return Err(Error::new(std::io::ErrorKind::NotFound, "Vault not found!")); + }; + + // Add member + let member_id = "test_member"; + vault + .register_member_to_vault(Member::new(member_id)) + .await?; + + const ID_PARAM: &str = "{member_id}"; + + // Check if the member info file exists + assert!( + dir.join(SERVER_FILE_MEMBER_INFO.replace(ID_PARAM, member_id)) + .exists() + ); + + // Remove member + vault.remove_member_from_vault(&member_id.to_string())?; + + // Check if the member info file not exists + assert!( + !dir.join(SERVER_FILE_MEMBER_INFO.replace(ID_PARAM, member_id)) + .exists() + ); + + Ok(()) +} diff --git a/legacy_data/tests/src/test_virtual_file_creation_and_update.rs b/legacy_data/tests/src/test_virtual_file_creation_and_update.rs new file mode 100644 index 0000000..a09f7dc --- /dev/null +++ b/legacy_data/tests/src/test_virtual_file_creation_and_update.rs @@ -0,0 +1,162 @@ +use std::time::Duration; + +use cfg_file::config::ConfigFile; +use tcp_connection_test::{ + handle::{ClientHandle, ServerHandle}, + target::TcpServerTarget, + target_configure::ServerTargetConfig, +}; +use tokio::{ + join, + time::{sleep, timeout}, +}; +use vcs_data::{ + constants::SERVER_FILE_VAULT, + data::{ + member::Member, + vault::{Vault, vault_config::VaultConfig, virtual_file::VirtualFileVersionDescription}, + }, +}; + +use crate::get_test_dir; + +struct VirtualFileCreateClientHandle; +struct VirtualFileCreateServerHandle; + +impl ClientHandle<VirtualFileCreateServerHandle> for VirtualFileCreateClientHandle { + async fn process(mut instance: tcp_connection::instance::ConnectionInstance) { + let dir = get_test_dir("virtual_file_creation_and_update_2") + .await + .unwrap(); + // Create first test file for virtual file creation + let test_content_1 = b"Test file content for virtual file creation"; + let temp_file_path_1 = dir.join("test_virtual_file_1.txt"); + + tokio::fs::write(&temp_file_path_1, test_content_1) + .await + .unwrap(); + + // Send the first file to server for virtual file creation + instance.write_file(&temp_file_path_1).await.unwrap(); + + // Create second test file for virtual file update + let test_content_2 = b"Updated test file content for virtual file"; + let temp_file_path_2 = dir.join("test_virtual_file_2.txt"); + + tokio::fs::write(&temp_file_path_2, test_content_2) + .await + .unwrap(); + + // Send the second file to server for virtual file update + instance.write_file(&temp_file_path_2).await.unwrap(); + } +} + +impl ServerHandle<VirtualFileCreateClientHandle> for VirtualFileCreateServerHandle { + async fn process(mut instance: tcp_connection::instance::ConnectionInstance) { + let dir = get_test_dir("virtual_file_creation_and_update") + .await + .unwrap(); + + // Setup vault + Vault::setup_vault(dir.clone(), "TestVault").await.unwrap(); + + // Read vault + let Some(vault) = Vault::init( + VaultConfig::read_from(dir.join(SERVER_FILE_VAULT)) + .await + .unwrap(), + &dir, + ) else { + panic!("No vault found!"); + }; + + // Register member + let member_id = "test_member"; + vault + .register_member_to_vault(Member::new(member_id)) + .await + .unwrap(); + + // Create visual file + let virtual_file_id = vault + .create_virtual_file_from_connection(&mut instance, &member_id.to_string()) + .await + .unwrap(); + + // Grant edit right to member + vault + .grant_virtual_file_edit_right(&member_id.to_string(), &virtual_file_id) + .await + .unwrap(); + + // Update visual file + vault + .update_virtual_file_from_connection( + &mut instance, + &member_id.to_string(), + &virtual_file_id, + &"2".to_string(), + VirtualFileVersionDescription { + creator: member_id.to_string(), + description: "Update".to_string(), + }, + ) + .await + .unwrap(); + } +} + +#[tokio::test] +async fn test_virtual_file_creation_and_update() -> Result<(), std::io::Error> { + let host = "localhost:5009"; + + // Server setup + let Ok(server_target) = TcpServerTarget::< + VirtualFileCreateClientHandle, + VirtualFileCreateServerHandle, + >::from_domain(host) + .await + else { + panic!("Test target built failed from a domain named `{}`", host); + }; + + // Client setup + let Ok(client_target) = TcpServerTarget::< + VirtualFileCreateClientHandle, + VirtualFileCreateServerHandle, + >::from_domain(host) + .await + else { + panic!("Test target built failed from a domain named `{}`", host); + }; + + let future_server = async move { + // Only process once + let configured_server = server_target.server_cfg(ServerTargetConfig::default().once()); + + // Listen here + let _ = configured_server.listen().await; + }; + + let future_client = async move { + // Wait for server start + let _ = sleep(Duration::from_secs_f32(1.5)).await; + + // Connect here + let _ = client_target.connect().await; + }; + + let test_timeout = Duration::from_secs(15); + + timeout(test_timeout, async { join!(future_client, future_server) }) + .await + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::TimedOut, + format!("Test timed out after {:?}", test_timeout), + ) + })?; + + Ok(()) +} |
