use dedup::{Config, DedupTransformer, LshIndex, MinHasher};
use dedup::tenshift::Sample;
use tenshift_core::sample::Tensor;
fn create_text_sample(text: impl Into<String>, idx: u64) -> Sample {
let text = text.into();
Sample::new()
.with("text", Tensor::bytes(text.into_bytes()))
.with_metadata("test", idx)
}
#[test]
fn test_end_to_end_deduplication() {
let config = Config::default()
.with_similarity_threshold(0.8)
.with_num_bands(8);
let mut transformer = DedupTransformer::new(config).unwrap();
let docs = vec![
"The quick brown fox jumps over the lazy dog",
"The quick brown fox jumps over the lazy dog", "The quick brown fox jumps over the lazy cat", "Completely different content about machine learning",
"Another unique document about rust programming",
];
for (i, doc) in docs.iter().enumerate() {
transformer.push(create_text_sample(*doc, i as u64));
}
let unique = transformer.finish_batch();
let clusters = transformer.clusters();
assert_eq!(unique.len(), 4, "Expected 4 unique documents");
assert_eq!(clusters.len(), 1);
assert_eq!(clusters[0].len(), 2);
}
#[test]
fn test_exact_duplicate_detection() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
for i in 0..10 {
transformer.push(create_text_sample("identical content", i));
}
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 1);
assert_eq!(transformer.duplicate_count(), 9);
}
#[test]
fn test_similarity_threshold_effect() {
let high_threshold = Config::default()
.with_similarity_threshold(0.95)
.with_num_bands(20);
let low_threshold = Config::default()
.with_similarity_threshold(0.5)
.with_num_bands(8);
let docs = vec![
"hello world test content here",
"hello world test content there", ];
let mut transformer_high = DedupTransformer::new(high_threshold).unwrap();
for (i, doc) in docs.iter().enumerate() {
transformer_high.push(create_text_sample(*doc, i as u64));
}
let unique_high = transformer_high.finish_batch();
let mut transformer_low = DedupTransformer::new(low_threshold).unwrap();
for (i, doc) in docs.iter().enumerate() {
transformer_low.push(create_text_sample(*doc, i as u64));
}
let unique_low = transformer_low.finish_batch();
assert!(unique_high.len() >= unique_low.len());
}
#[test]
fn test_empty_dataset() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
let unique = transformer.finish_batch();
assert!(unique.is_empty());
}
#[test]
fn test_single_document() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
transformer.push(create_text_sample("only document", 0));
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 1);
assert_eq!(transformer.duplicate_count(), 0);
}
#[test]
fn test_all_unique_documents() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
for i in 0..50 {
let doc = format!("unique document number {} with distinct content", i);
transformer.push(create_text_sample(doc, i));
}
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 50);
assert_eq!(transformer.duplicate_count(), 0);
}
#[test]
fn test_large_scale_deduplication() {
let config = Config::default()
.with_signature_size(128)
.with_num_bands(16);
let mut transformer = DedupTransformer::new(config).unwrap();
let total_docs = 1000;
let duplicate_ratio = 0.3;
let base_content = "base document content for deduplication testing";
for i in 0..total_docs {
let content = if (i as f64 / total_docs as f64) < duplicate_ratio {
base_content.to_string()
} else {
format!("unique content for document {}", i)
};
transformer.push(create_text_sample(content, i as u64));
}
let unique = transformer.finish_batch();
let stats = transformer.stats();
assert!(unique.len() < total_docs);
assert!(stats.duplicate_count > 0);
assert_eq!(stats.doc_count, total_docs);
}
#[test]
fn test_unicode_text_deduplication() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
let docs = vec![
"你好世界,这是一个测试文档",
"你好世界,这是一个测试文档", "こんにちは世界、これはテストです",
"🎉 Emoji test document 🚀",
"🎉 Emoji test document 🚀", ];
for (i, doc) in docs.iter().enumerate() {
transformer.push(create_text_sample(*doc, i as u64));
}
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 3);
}
#[test]
fn test_minhash_consistency() {
let config = Config::default();
let hasher = MinHasher::new(&config).unwrap();
let doc = "consistent document for testing";
let sig1 = hasher.compute_str(doc, 0).unwrap();
let sig2 = hasher.compute_str(doc, 1).unwrap();
let sig3 = hasher.compute_str(doc, 2).unwrap();
assert_eq!(sig1.values, sig2.values);
assert_eq!(sig2.values, sig3.values);
}
#[test]
fn test_lsh_candidate_generation() {
let config = Config::default();
let mut index = LshIndex::new(&config).unwrap();
let hasher = MinHasher::new(&config).unwrap();
let doc1 = "the quick brown fox jumps over the lazy dog";
let doc2 = "the quick brown fox jumps over the lazy cat";
let sig1 = hasher.compute_str(doc1, 0).unwrap();
let sig2 = hasher.compute_str(doc2, 1).unwrap();
let candidates1 = index.insert(sig1).unwrap();
assert!(candidates1.is_empty());
let candidates2 = index.insert(sig2).unwrap();
assert!(candidates2.contains(&0));
}
#[test]
fn test_config_variations() {
let configs = vec![
Config::new(64, 8, 3, 0.9).unwrap(),
Config::new(128, 16, 5, 0.85).unwrap(),
Config::new(256, 32, 7, 0.8).unwrap(),
];
for config in configs {
let mut transformer = DedupTransformer::new(config).unwrap();
transformer.push(create_text_sample("test document one", 0));
transformer.push(create_text_sample("test document two", 1));
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 2);
}
}
#[test]
fn test_cluster_info() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
let cluster_docs = vec![
"document in cluster A",
"document in cluster A",
"document in cluster A",
];
for (i, doc) in cluster_docs.iter().enumerate() {
transformer.push(create_text_sample(*doc, i as u64));
}
transformer.finish_batch();
let clusters = transformer.clusters();
assert_eq!(clusters.len(), 1);
assert_eq!(clusters[0].len(), 3);
assert_eq!(clusters[0].representative, 0);
}
#[test]
fn test_streaming_mode() {
let config = Config::default();
let transformer = DedupTransformer::new(config)
.unwrap()
.with_streaming(true);
assert!(transformer.streaming);
}
#[test]
fn test_memory_estimation() {
let config = Config::default();
let transformer = DedupTransformer::new(config).unwrap();
let stats = transformer.stats();
assert_eq!(stats.doc_count, 0);
}
#[test]
fn test_shingle_size_variations() {
let configs = vec![
Config::default().with_shingle_size(3),
Config::default().with_shingle_size(5),
Config::default().with_shingle_size(7),
];
for config in configs {
let mut transformer = DedupTransformer::new(config).unwrap();
transformer.push(create_text_sample("test document content", 0));
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 1);
}
}
#[test]
fn test_different_text_fields() {
let config = Config::default();
let _transformer1 = DedupTransformer::new(config).unwrap();
let _transformer2 = DedupTransformer::new(config.clone())
.unwrap()
.with_text_field("content");
}
#[test]
fn test_edge_cases() {
let config = Config::default();
let mut transformer = DedupTransformer::new(config).unwrap();
transformer.push(create_text_sample("hi", 0));
let long_doc = "word ".repeat(10000);
transformer.push(create_text_sample(&long_doc, 1));
transformer.push(create_text_sample("!@#$%^&*()_+{}|:<>?~`-=[]\\;',./", 2));
let unique = transformer.finish_batch();
assert_eq!(unique.len(), 3);
}
#[test]
fn test_reproducibility() {
let config = Config::default().with_seed(12345);
let mut transformer1 = DedupTransformer::new(config).unwrap();
let mut transformer2 = DedupTransformer::new(config).unwrap();
let docs = vec![
"document one",
"document two",
"document one", ];
for (i, doc) in docs.iter().enumerate() {
transformer1.push(create_text_sample(*doc, i as u64));
transformer2.push(create_text_sample(*doc, i as u64));
}
let unique1 = transformer1.finish_batch();
let unique2 = transformer2.finish_batch();
assert_eq!(unique1.len(), unique2.len());
assert_eq!(transformer1.duplicate_count(), transformer2.duplicate_count());
}