summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-09-26 11:19:10 +0800
committerGitHub <noreply@github.com>2025-09-26 11:19:10 +0800
commit0fcdf40d82b448e21949b981c14dcecf70ec4cc6 (patch)
treed71490fc662508674a2927e9558bc89e91e070b6
parent87448666c38fcfaa7ee381ee966fa925db7279e1 (diff)
parent9c44621f13f27e7f2a82fa5ab2fc8e27381f3e39 (diff)
Merge pull request #8 from JustEnoughVCS/jvcs_dev
Jvcs dev
-rw-r--r--crates/vcs/src/data/sheet.rs22
-rw-r--r--crates/vcs/src/data/vault/sheets.rs16
-rw-r--r--crates/vcs/vcs_test/src/lib.rs3
-rw-r--r--crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs260
4 files changed, 286 insertions, 15 deletions
diff --git a/crates/vcs/src/data/sheet.rs b/crates/vcs/src/data/sheet.rs
index 3acc8ff..edf307a 100644
--- a/crates/vcs/src/data/sheet.rs
+++ b/crates/vcs/src/data/sheet.rs
@@ -14,8 +14,15 @@ use crate::{
pub type SheetName = String;
pub type SheetPathBuf = PathBuf;
pub type InputName = String;
-pub type InputPackage = (InputName, Vec<(InputRaltivePathBuf, VirtualFileId)>);
-pub type InputRaltivePathBuf = PathBuf;
+pub type InputRelativePathBuf = PathBuf;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct InputPackage {
+ /// Name of the input package
+ pub name: InputName,
+ /// Files in this input package with their relative paths and virtual file IDs
+ pub files: Vec<(InputRelativePathBuf, VirtualFileId)>,
+}
const SHEET_NAME: &str = "{sheet-name}";
@@ -62,9 +69,12 @@ impl<'a> Sheet<'a> {
pub fn add_input(
&mut self,
input_name: InputName,
- files: Vec<(InputRaltivePathBuf, VirtualFileId)>,
+ files: Vec<(InputRelativePathBuf, VirtualFileId)>,
) {
- self.data.inputs.push((input_name, files));
+ self.data.inputs.push(InputPackage {
+ name: input_name,
+ files,
+ });
}
/// Remove an input package from the sheet
@@ -72,7 +82,7 @@ impl<'a> Sheet<'a> {
self.data
.inputs
.iter()
- .position(|(name, _)| name == input_name)
+ .position(|input| input.name == *input_name)
.map(|pos| self.data.inputs.remove(pos))
}
@@ -92,7 +102,7 @@ impl<'a> Sheet<'a> {
/// Because I don't want a second instance of the sheet to be kept in memory.
/// If needed, please deserialize and reload it.
pub async fn persist(self) -> Result<(), std::io::Error> {
- SheetData::write_to(&self.data, &self.sheet_path()).await
+ SheetData::write_to(&self.data, self.sheet_path()).await
}
/// Get the path to the sheet file
diff --git a/crates/vcs/src/data/vault/sheets.rs b/crates/vcs/src/data/vault/sheets.rs
index ede4077..dfad862 100644
--- a/crates/vcs/src/data/vault/sheets.rs
+++ b/crates/vcs/src/data/vault/sheets.rs
@@ -53,12 +53,11 @@ impl Vault {
let path = entry.path();
// Check if it's a YAML file
- if path.is_file() && path.extension().map_or(false, |ext| ext == "yaml") {
- if let Some(_file_stem) = path.file_stem().and_then(|s| s.to_str()) {
+ if path.is_file() && path.extension().is_some_and(|ext| ext == "yaml")
+ && let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) {
// Create a new SheetName and add it to the result list
- sheet_names.push(SheetName::new());
+ sheet_names.push(file_stem.to_string());
}
- }
}
Ok(sheet_names)
@@ -128,7 +127,7 @@ impl Vault {
// Create the sheet file
let sheet_data = SheetData {
- holder: sheet_name.clone(),
+ holder: holder.clone(),
inputs: Vec::new(),
mapping: HashMap::new(),
};
@@ -220,7 +219,7 @@ impl Vault {
if !trash_dir.exists() {
return Err(Error::new(
std::io::ErrorKind::NotFound,
- format!("Trash directory does not exist!"),
+ "Trash directory does not exist!".to_string(),
));
}
@@ -229,15 +228,14 @@ impl Vault {
let entry = entry?;
let path = entry.path();
- if path.is_file() {
- if let Some(file_name) = path.file_stem().and_then(|s| s.to_str()) {
+ if path.is_file()
+ && let Some(file_name) = path.file_stem().and_then(|s| s.to_str()) {
// Check if the filename starts with the sheet name
if file_name.starts_with(&sheet_name) {
found_path = Some(path);
break;
}
}
- }
}
let trash_path = found_path.ok_or_else(|| {
diff --git a/crates/vcs/vcs_test/src/lib.rs b/crates/vcs/vcs_test/src/lib.rs
index d9e6f94..8ad03e1 100644
--- a/crates/vcs/vcs_test/src/lib.rs
+++ b/crates/vcs/vcs_test/src/lib.rs
@@ -11,6 +11,9 @@ 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;
+
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_sheet_creation_management_and_persistence.rs b/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs
new file mode 100644
index 0000000..7fe6955
--- /dev/null
+++ b/crates/vcs/vcs_test/src/test_sheet_creation_management_and_persistence.rs
@@ -0,0 +1,260 @@
+use std::io::Error;
+
+use cfg_file::config::ConfigFile;
+use vcs::{
+ constants::{SERVER_FILE_SHEET, SERVER_FILE_VAULT},
+ data::{
+ member::{Member, MemberId},
+ sheet::{InputRelativePathBuf, SheetName},
+ 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()).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(), &member_id);
+ assert_eq!(sheet.holder(), &member_id);
+ assert!(sheet.inputs().is_empty());
+ assert!(sheet.mapping().is_empty());
+
+ // Verify sheet file was created
+ const SHEET_NAME_PARAM: &str = "{sheet-name}";
+ let sheet_path = dir.join(SERVER_FILE_SHEET.replace(SHEET_NAME_PARAM, &sheet_name));
+ assert!(sheet_path.exists());
+
+ // Test 2: Add input packages to the sheet
+ let input_name = "source_files".to_string();
+ let files = vec![
+ (
+ InputRelativePathBuf::from("src/main.rs"),
+ VirtualFileId::new(),
+ ),
+ (
+ InputRelativePathBuf::from("src/lib.rs"),
+ VirtualFileId::new(),
+ ),
+ ];
+
+ // Need to get a mutable reference to the sheet
+ let mut sheet = vault.sheet(&sheet_name).await?;
+ sheet.add_input(input_name.clone(), files.clone());
+
+ // Verify input was added
+ assert_eq!(sheet.inputs().len(), 1);
+ let added_input = &sheet.inputs()[0];
+ assert_eq!(added_input.name, input_name);
+ assert_eq!(added_input.files.len(), 2);
+ assert_eq!(
+ added_input.files[0].0,
+ InputRelativePathBuf::from("src/main.rs")
+ );
+ assert_eq!(
+ added_input.files[1].0,
+ InputRelativePathBuf::from("src/lib.rs")
+ );
+
+ // Test 3: Add mapping entries
+ let mapping_path = vcs::data::sheet::SheetPathBuf::from("output/build.exe");
+ let virtual_file_id = VirtualFileId::new();
+ sheet.add_mapping(mapping_path.clone(), virtual_file_id.clone());
+
+ // Verify mapping was added
+ assert_eq!(sheet.mapping().len(), 1);
+ assert_eq!(sheet.mapping().get(&mapping_path), 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(), &member_id);
+ assert_eq!(reloaded_sheet.inputs().len(), 1);
+ assert_eq!(reloaded_sheet.mapping().len(), 1);
+
+ // Test 5: Remove input package
+ let mut sheet_for_removal = vault.sheet(&sheet_name).await?;
+ let removed_input = sheet_for_removal.remove_input(&input_name);
+ assert!(removed_input.is_some());
+ let removed_input = removed_input.unwrap();
+ assert_eq!(removed_input.name, input_name);
+ assert_eq!(removed_input.files.len(), 2);
+ assert_eq!(sheet_for_removal.inputs().len(), 0);
+
+ // Test 6: Remove mapping entry
+ let removed_virtual_file_id = sheet_for_removal.remove_mapping(&mapping_path);
+ assert_eq!(removed_virtual_file_id, Some(virtual_file_id));
+ assert_eq!(sheet_for_removal.mapping().len(), 0);
+
+ // Test 7: List all sheets in vault
+ let sheet_names = vault.sheet_names()?;
+ assert_eq!(sheet_names.len(), 1);
+ assert_eq!(sheet_names[0], sheet_name);
+
+ let all_sheets = vault.sheets().await?;
+ assert_eq!(all_sheets.len(), 1);
+ assert_eq!(all_sheets[0].holder(), &member_id);
+
+ // Test 8: 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(), 0);
+
+ // Test 9: Restore sheet from trash
+ let restored_sheet = vault.sheet(&sheet_name).await?;
+ assert_eq!(restored_sheet.holder(), &member_id);
+ assert_eq!(restored_sheet.holder(), &member_id);
+
+ // Verify sheet is back in normal listing
+ let sheet_names_after_restore = vault.sheet_names()?;
+ assert_eq!(sheet_names_after_restore.len(), 1);
+
+ // Test 10: 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(), 0);
+
+ // 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()).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()).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 inputs
+ let input_name = "source_files".to_string();
+ let files = vec![
+ (
+ InputRelativePathBuf::from("src/main.rs"),
+ VirtualFileId::new(),
+ ),
+ (
+ InputRelativePathBuf::from("src/lib.rs"),
+ VirtualFileId::new(),
+ ),
+ ];
+ sheet.add_input(input_name, files);
+
+ // Add some mappings
+ sheet.add_mapping(
+ vcs::data::sheet::SheetPathBuf::from("output/build.exe"),
+ VirtualFileId::new(),
+ );
+
+ // Persist the sheet
+ sheet.persist().await?;
+
+ // Verify the sheet file was created
+ const SHEET_NAME_PARAM: &str = "{sheet-name}";
+ let sheet_path = dir.join(SERVER_FILE_SHEET.replace(SHEET_NAME_PARAM, &sheet_name));
+ assert!(sheet_path.exists());
+
+ // Clean up
+ vault.remove_member_from_vault(&member_id)?;
+
+ Ok(())
+}