summaryrefslogtreecommitdiff
path: root/crates/vcs_data/vcs_data_test
diff options
context:
space:
mode:
Diffstat (limited to 'crates/vcs_data/vcs_data_test')
-rw-r--r--crates/vcs_data/vcs_data_test/src/lib.rs3
-rw-r--r--crates/vcs_data/vcs_data_test/src/test_sheet_share_creation_and_management.rs636
2 files changed, 639 insertions, 0 deletions
diff --git a/crates/vcs_data/vcs_data_test/src/lib.rs b/crates/vcs_data/vcs_data_test/src/lib.rs
index 8ad03e1..ced2d3d 100644
--- a/crates/vcs_data/vcs_data_test/src/lib.rs
+++ b/crates/vcs_data/vcs_data_test/src/lib.rs
@@ -14,6 +14,9 @@ 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() {
diff --git a/crates/vcs_data/vcs_data_test/src/test_sheet_share_creation_and_management.rs b/crates/vcs_data/vcs_data_test/src/test_sheet_share_creation_and_management.rs
new file mode 100644
index 0000000..d5ccbc2
--- /dev/null
+++ b/crates/vcs_data/vcs_data_test/src/test_sheet_share_creation_and_management.rs
@@ -0,0 +1,636 @@
+use std::io::Error;
+
+use cfg_file::config::ConfigFile;
+use vcs_data::{
+ constants::{SERVER_FILE_VAULT, SERVER_SUFFIX_SHEET_FILE},
+ data::{
+ member::{Member, MemberId},
+ sheet::{SheetName, SheetPathBuf},
+ vault::{
+ Vault,
+ config::VaultConfig,
+ sheet_share::{Share, ShareMergeMode, SheetShareId},
+ 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 contain sharer name and file suffix
+ assert!(id1.contains("test_sharer"));
+ assert!(id1.ends_with(SERVER_SUFFIX_SHEET_FILE));
+
+ assert!(id2.contains("test_sharer"));
+ assert!(id2.ends_with(SERVER_SUFFIX_SHEET_FILE));
+
+ assert!(id3.contains("test_sharer"));
+ assert!(id3.ends_with(SERVER_SUFFIX_SHEET_FILE));
+
+ Ok(())
+}