use synaptic_splitters::{
CharacterTextSplitter, Document, RecursiveCharacterTextSplitter, TextSplitter,
TokenTextSplitter,
};
#[test]
fn character_splitter_empty_input() {
let splitter = CharacterTextSplitter::new(100);
let result = splitter.split_text("");
assert_eq!(result.len(), 1);
assert_eq!(result[0], "");
}
#[test]
fn character_splitter_text_shorter_than_chunk() {
let splitter = CharacterTextSplitter::new(1000);
let result = splitter.split_text("short text");
assert_eq!(result.len(), 1);
assert_eq!(result[0], "short text");
}
#[test]
fn character_splitter_separator_not_found() {
let splitter = CharacterTextSplitter::new(10).with_separator("|||");
let result = splitter.split_text("hello world this is a test");
assert!(!result.is_empty());
assert_eq!(result.len(), 1);
assert_eq!(result[0], "hello world this is a test");
}
#[test]
fn character_splitter_overlap_larger_than_splits() {
let splitter = CharacterTextSplitter::new(10)
.with_separator(" ")
.with_chunk_overlap(8);
let text = "aa bb cc dd ee";
let result = splitter.split_text(text);
assert!(!result.is_empty());
for word in &["aa", "bb", "cc", "dd", "ee"] {
assert!(
result.iter().any(|chunk| chunk.contains(word)),
"word '{}' missing from chunks: {:?}",
word,
result,
);
}
}
#[test]
fn recursive_splitter_hierarchical_fallback() {
let splitter = RecursiveCharacterTextSplitter::new(20);
let text = "first line\nsecond line\nthird line";
let result = splitter.split_text(text);
assert!(result.len() > 1);
for chunk in &result {
assert!(
chunk.len() <= 20,
"chunk too long: {} chars in '{}'",
chunk.len(),
chunk,
);
}
}
#[test]
fn recursive_splitter_custom_separators() {
let splitter = RecursiveCharacterTextSplitter::new(15).with_separators(vec![
";".to_string(),
",".to_string(),
"".to_string(),
]);
let text = "alpha;beta,gamma;delta";
let result = splitter.split_text(text);
assert!(result.len() > 1);
for chunk in &result {
assert!(
chunk.len() <= 15,
"chunk too long: {} chars in '{}'",
chunk.len(),
chunk,
);
}
}
#[test]
fn recursive_splitter_empty_input() {
let splitter = RecursiveCharacterTextSplitter::new(100);
let result = splitter.split_text("");
assert_eq!(result.len(), 1);
assert_eq!(result[0], "");
}
#[test]
fn token_splitter_empty_text() {
let splitter = TokenTextSplitter::new(100);
let result = splitter.split_text("");
assert!(result.is_empty());
}
#[test]
fn token_splitter_single_word() {
let splitter = TokenTextSplitter::new(100);
let result = splitter.split_text("hello");
assert_eq!(result.len(), 1);
assert_eq!(result[0], "hello");
}
#[test]
fn token_splitter_with_overlap() {
let splitter = TokenTextSplitter::new(5).with_chunk_overlap(2);
let text = "The quick brown fox jumped over the lazy dog and other animals in the park";
let result = splitter.split_text(text);
assert!(result.len() > 1, "should produce multiple chunks");
for window in result.windows(2) {
let words_a: Vec<&str> = window[0].split_whitespace().collect();
let words_b: Vec<&str> = window[1].split_whitespace().collect();
let has_overlap = words_a.iter().any(|w| words_b.contains(w));
assert!(
has_overlap,
"expected overlap between consecutive chunks: '{}' and '{}'",
window[0], window[1],
);
}
}
#[test]
fn split_documents_preserves_metadata() {
let splitter = CharacterTextSplitter::new(10).with_separator(" ");
let doc = Document::with_metadata(
"doc1",
"hello world how are you today",
[("source".to_string(), serde_json::json!("test.txt"))].into(),
);
let result = splitter.split_documents(vec![doc]);
assert!(result.len() > 1, "should split into multiple docs");
for d in &result {
assert_eq!(
d.metadata.get("source").unwrap(),
"test.txt",
"metadata 'source' should be preserved on chunk '{}'",
d.id,
);
assert!(
d.metadata.contains_key("chunk_index"),
"chunk_index metadata should be added",
);
}
}