use proptest::prelude::*;
use wlk::{DeltaOperation, ShadowFile};
proptest! {
#[test]
fn test_insert_never_panics(
base in "[a-zA-Z0-9 ]{0,100}",
index in 0usize..100,
value in "[a-zA-Z0-9 ]{1,20}"
) {
let mut shadow = ShadowFile::new("test.txt", base.clone());
let actual_index = index.min(base.len());
let result = shadow.apply_delta(
DeltaOperation::Insert {
index: actual_index,
value: value.clone(),
},
None,
);
let _ = result;
}
#[test]
fn test_delete_never_panics(
base in "[a-zA-Z0-9 ]{1,100}",
index in 0usize..100,
length in 1usize..50
) {
let mut shadow = ShadowFile::new("test.txt", base.clone());
let actual_index = index.min(base.len().saturating_sub(1));
let actual_length = length.min(base.len() - actual_index);
let result = shadow.apply_delta(
DeltaOperation::Delete {
index: actual_index,
length: actual_length,
},
None,
);
let _ = result;
}
#[test]
fn test_replace_never_panics(
base in "[a-zA-Z0-9 ]{1,100}",
index in 0usize..100,
length in 1usize..50,
value in "[a-zA-Z0-9 ]{1,20}"
) {
let mut shadow = ShadowFile::new("test.txt", base.clone());
let actual_index = index.min(base.len().saturating_sub(1));
let actual_length = length.min(base.len() - actual_index);
let result = shadow.apply_delta(
DeltaOperation::Replace {
index: actual_index,
length: actual_length,
value: value.clone(),
},
None,
);
let _ = result;
}
#[test]
fn test_snapshot_always_works(base in "[a-zA-Z0-9 ]{0,1000}") {
let mut shadow = ShadowFile::new("test.txt", base.clone());
let result = shadow.apply_delta(
DeltaOperation::Snapshot { content: base.clone() },
None,
);
prop_assert!(result.is_ok());
}
#[test]
fn test_reconstruction_is_consistent(
base in "[a-zA-Z0-9 ]{10,50}",
insert_value in "[a-zA-Z0-9 ]{5,15}"
) {
let mut shadow = ShadowFile::new("test.txt", base.clone());
shadow.apply_delta(
DeltaOperation::Insert {
index: base.len() / 2,
value: insert_value.clone(),
},
Some("test".to_string()),
).unwrap();
let delta_id = shadow.current_head.clone();
let content1 = shadow.reconstruct_at(&delta_id).unwrap();
let content2 = shadow.reconstruct_at(&delta_id).unwrap();
let content3 = shadow.reconstruct_at(&delta_id).unwrap();
prop_assert_eq!(&content1, &content2);
prop_assert_eq!(&content2, &content3);
}
#[test]
fn test_multiple_operations_compose(
base in "[a-zA-Z ]{20,50}"
) {
let mut shadow = ShadowFile::new("test.txt", base.clone());
for i in 0..5 {
shadow.apply_delta(
DeltaOperation::Insert {
index: shadow.current_content().unwrap().len(),
value: format!(" {}", i),
},
Some(format!("Add {}", i)),
).unwrap();
}
let history = shadow.get_history();
for delta in history {
let content = shadow.reconstruct_at(&delta.id);
prop_assert!(content.is_ok());
}
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
use wlk::ShadowFile;
#[test]
fn test_empty_file() {
let shadow = ShadowFile::new("empty.txt", String::new());
assert_eq!(shadow.current_content().unwrap(), "");
}
#[test]
fn test_very_long_file() {
let content = "x".repeat(100_000);
let shadow = ShadowFile::new("long.txt", content.clone());
assert_eq!(shadow.current_content().unwrap(), content);
}
#[test]
fn test_unicode_content() {
let content = "Hello 世界 🌍 Rust!";
let mut shadow = ShadowFile::new("unicode.txt", content.to_string());
shadow
.apply_delta(
DeltaOperation::Insert {
index: 6,
value: "नमस्ते ".to_string(),
},
None,
)
.unwrap();
let result = shadow.current_content().unwrap();
assert!(result.contains("नमस्ते"));
}
#[test]
fn test_newlines_and_special_chars() {
let content = "Line 1\nLine 2\r\nLine 3\tTabbed";
let shadow = ShadowFile::new("special.txt", content.to_string());
assert_eq!(shadow.current_content().unwrap(), content);
}
#[test]
fn test_deep_history() {
let mut shadow = ShadowFile::new("deep.txt", "start".to_string());
for i in 0..1000 {
shadow
.apply_delta(
DeltaOperation::Insert {
index: shadow.current_content().unwrap().len(),
value: "x".to_string(),
},
Some(format!("Delta {}", i)),
)
.unwrap();
}
let history = shadow.get_history();
assert_eq!(history.len(), 1000);
let mid_delta = &history[500];
let content = shadow.reconstruct_at(&mid_delta.id).unwrap();
assert!(content.len() > 500);
}
}