use proptest::prelude::*;
use ricecoder_storage::RelocationService;
use std::fs;
use tempfile::TempDir;
fn file_name_strategy() -> impl Strategy<Value = String> {
r"[a-zA-Z0-9_\-]{1,10}"
.prop_map(|s| format!("{}.txt", s))
.prop_filter("Name should be non-empty", |s| !s.is_empty())
}
fn file_content_strategy() -> impl Strategy<Value = String> {
r"[a-zA-Z0-9 \n\t]*".prop_filter("Content should be valid", |s| !s.is_empty())
}
fn file_structure_strategy() -> impl Strategy<Value = Vec<(String, String)>> {
prop::collection::vec((file_name_strategy(), file_content_strategy()), 1..10).prop_map(
|mut files| {
for (i, (name, _)) in files.iter_mut().enumerate() {
let base = name.trim_end_matches(".txt");
*name = format!("{}_{}.txt", base, i);
}
files
},
)
}
#[test]
fn prop_relocation_roundtrip() {
proptest!(|(files in file_structure_strategy())| {
let source_dir = TempDir::new().expect("Failed to create source temp dir");
let target_parent = TempDir::new().expect("Failed to create target parent temp dir");
for (name, content) in &files {
fs::write(source_dir.path().join(name), content)
.expect("Failed to write source file");
}
let source_path = source_dir.path().to_path_buf();
let target_path = target_parent.path().join("relocated_storage");
RelocationService::relocate(&source_path, &target_path)
.expect("Relocation should succeed");
for (name, content) in &files {
let target_file = target_path.join(name);
assert!(
target_file.exists(),
"File {} should exist in target location",
name
);
let retrieved_content = fs::read_to_string(&target_file)
.expect("Failed to read target file");
assert_eq!(
&retrieved_content, content,
"Content of {} should be identical after relocation",
name
);
}
});
}
#[test]
fn prop_relocation_preserves_structure() {
proptest!(|(files in file_structure_strategy())| {
let source_dir = TempDir::new().expect("Failed to create source temp dir");
let target_parent = TempDir::new().expect("Failed to create target parent temp dir");
fs::create_dir(source_dir.path().join("subdir1")).expect("Failed to create subdir1");
fs::create_dir(source_dir.path().join("subdir2")).expect("Failed to create subdir2");
for (i, (name, content)) in files.iter().enumerate() {
let subdir = if i % 2 == 0 { "subdir1" } else { "subdir2" };
let file_path = source_dir.path().join(subdir).join(name);
fs::write(&file_path, content)
.expect("Failed to write file in subdir");
}
let source_path = source_dir.path().to_path_buf();
let target_path = target_parent.path().join("relocated_storage");
RelocationService::relocate(&source_path, &target_path)
.expect("Relocation should succeed");
assert!(
target_path.join("subdir1").exists(),
"subdir1 should exist in target"
);
assert!(
target_path.join("subdir2").exists(),
"subdir2 should exist in target"
);
for (i, (name, content)) in files.iter().enumerate() {
let subdir = if i % 2 == 0 { "subdir1" } else { "subdir2" };
let target_file = target_path.join(subdir).join(name);
assert!(
target_file.exists(),
"File {} should exist in {} after relocation",
name,
subdir
);
let retrieved_content = fs::read_to_string(&target_file)
.expect("Failed to read target file");
assert_eq!(
&retrieved_content, content,
"Content should be preserved in subdirectory"
);
}
});
}
#[test]
fn prop_relocation_file_count_preserved() {
proptest!(|(files in file_structure_strategy())| {
let source_dir = TempDir::new().expect("Failed to create source temp dir");
let target_parent = TempDir::new().expect("Failed to create target parent temp dir");
for (name, content) in &files {
fs::write(source_dir.path().join(name), content)
.expect("Failed to write source file");
}
let source_path = source_dir.path().to_path_buf();
let target_path = target_parent.path().join("relocated_storage");
let source_count = files.len();
RelocationService::relocate(&source_path, &target_path)
.expect("Relocation should succeed");
let target_count = fs::read_dir(&target_path)
.expect("Failed to read target directory")
.count();
assert_eq!(
source_count, target_count,
"File count should be preserved after relocation"
);
});
}
#[test]
fn prop_relocation_fails_with_nonempty_target() {
proptest!(|(files in file_structure_strategy())| {
let source_dir = TempDir::new().expect("Failed to create source temp dir");
let target_dir = TempDir::new().expect("Failed to create target temp dir");
for (name, content) in &files {
fs::write(source_dir.path().join(name), content)
.expect("Failed to write source file");
}
fs::write(target_dir.path().join("existing.txt"), "existing content")
.expect("Failed to create existing file in target");
let source_path = source_dir.path().to_path_buf();
let target_path = target_dir.path().to_path_buf();
let result = RelocationService::relocate(&source_path, &target_path);
assert!(
result.is_err(),
"Relocation to non-empty target should fail"
);
});
}
#[test]
fn prop_relocation_fails_with_nonexistent_source() {
let target_parent = TempDir::new().expect("Failed to create target parent temp dir");
let source_path = target_parent.path().join("nonexistent_source");
let target_path = target_parent.path().join("target");
let result = RelocationService::relocate(&source_path, &target_path);
assert!(
result.is_err(),
"Relocation from non-existent source should fail"
);
}