use super::*;
use crate::config::Config;
use crate::memory_types::{BatchIngestItemResult, IngestPolicy};
use crate::sqlite::Database;
#[test]
fn test_batch_ingest_empty_batch() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let mut store = MemoryStore::from_db(db, config);
let result = store
.batch_ingest("test-project", vec![], IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 0);
}
#[test]
fn test_batch_ingest_mixed_outcomes() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let embedding = super::crud::mock_embedding_for_content("Alice works at Microsoft");
db.insert("test-project", "Alice works at Microsoft", &embedding, None)
.unwrap();
let mut store = MemoryStore::from_db(db, config);
let items = vec![
("", None), ("Bob works at Google", None), ("Alice works at Microsoft", None), ("Charlie works at Amazon", None), ];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 4);
match &result.results[0] {
BatchIngestItemResult::Error { message } => {
assert!(message.contains("empty") || message.contains("EmptyInput"));
}
_ => panic!("Expected Error for item 0, got {:?}", result.results[0]),
}
match &result.results[1] {
BatchIngestItemResult::Added { id } => {
assert!(!id.is_empty());
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "Bob works at Google");
}
_ => panic!("Expected Added for item 1, got {:?}", result.results[1]),
}
match &result.results[2] {
BatchIngestItemResult::Conflicts {
proposed,
conflicts,
} => {
assert_eq!(proposed, "Alice works at Microsoft");
assert!(!conflicts.is_empty());
assert!(conflicts.iter().any(|c| c.content.contains("Alice")));
}
_ => panic!("Expected Conflicts for item 2, got {:?}", result.results[2]),
}
match &result.results[3] {
BatchIngestItemResult::Added { id } => {
assert!(!id.is_empty());
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "Charlie works at Amazon");
}
_ => panic!("Expected Added for item 3, got {:?}", result.results[3]),
}
}
#[test]
fn test_batch_ingest_deterministic_index_mapping() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let embedding = super::crud::mock_embedding_for_content("conflict with item 1");
db.insert("test-project", "conflict with item 1", &embedding, None)
.unwrap();
let mut store = MemoryStore::from_db(db, config);
let items = vec![
("unique item 0", None),
("conflict with item 1", None), ("", None), ("unique item 3", Some("metadata")),
];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 4);
assert!(matches!(
&result.results[0],
BatchIngestItemResult::Added { .. }
));
assert!(matches!(
&result.results[1],
BatchIngestItemResult::Conflicts { .. }
));
assert!(matches!(
&result.results[2],
BatchIngestItemResult::Error { .. }
));
if let BatchIngestItemResult::Added { id } = &result.results[3] {
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "unique item 3");
assert_eq!(memory.metadata, Some("metadata".to_string()));
} else {
panic!("Expected Added for index 3");
}
}
#[test]
fn test_batch_ingest_policy_force() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let embedding = super::crud::mock_embedding_for_content("Alice works at Microsoft");
db.insert("test-project", "Alice works at Microsoft", &embedding, None)
.unwrap();
let mut store = MemoryStore::from_db(db, config);
let items = vec![
("Alice works at Microsoft", None), ("Bob works at Google", None), ];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::Force)
.unwrap();
assert_eq!(result.results.len(), 2);
match &result.results[0] {
BatchIngestItemResult::Added { id } => {
assert!(!id.is_empty());
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "Alice works at Microsoft");
}
_ => panic!("Expected Added for item 0 with Force policy"),
}
match &result.results[1] {
BatchIngestItemResult::Added { id } => {
assert!(!id.is_empty());
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "Bob works at Google");
}
_ => panic!("Expected Added for item 1 with Force policy"),
}
}
#[test]
fn test_batch_ingest_invalid_inputs() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let mut store = MemoryStore::from_db(db, config);
let too_long = "a".repeat(100_001);
let whitespace = " \t\n ";
let items = vec![
("", None), (" ", None), (&too_long, None), ("valid input", None), (whitespace, None), ];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 5);
for &idx in &[0_usize, 1, 2, 4] {
match &result.results[idx] {
BatchIngestItemResult::Error { .. } => {
}
other => panic!(
"Expected Error for invalid input at index {}, got {:?}",
idx, other
),
}
}
match &result.results[3] {
BatchIngestItemResult::Added { id } => {
assert!(!id.is_empty());
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.content, "valid input");
}
_ => panic!("Expected Added for valid input at index 3"),
}
}
#[test]
fn test_batch_ingest_all_errors() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let mut store = MemoryStore::from_db(db, config);
let items = vec![
("", None), (" ", None), ];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 2);
for (idx, item_result) in result.results.iter().enumerate() {
match item_result {
BatchIngestItemResult::Error { .. } => {
}
_ => panic!("Expected Error for all items at index {}", idx),
}
}
}
#[test]
fn test_batch_ingest_metadata_preservation() {
use tempfile::TempDir;
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.db");
std::mem::forget(dir);
let db = Database::open(&path).unwrap();
let config = Config::default();
let mut store = MemoryStore::from_db(db, config);
let items = vec![
("content with metadata", Some(r#"{"tag": "important"}"#)),
("content without metadata", None),
(
"json metadata",
Some(r#"{"source": "user", "priority": 1}"#),
),
];
let result = store
.batch_ingest("test-project", items.clone(), IngestPolicy::ConflictAware)
.unwrap();
assert_eq!(result.results.len(), 3);
if let BatchIngestItemResult::Added { id } = &result.results[0] {
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.metadata, Some(r#"{"tag": "important"}"#.to_string()));
} else {
panic!("Expected Added for item 0");
}
if let BatchIngestItemResult::Added { id } = &result.results[1] {
let memory = store.get(id).unwrap().unwrap();
assert_eq!(memory.metadata, None);
} else {
panic!("Expected Added for item 1");
}
if let BatchIngestItemResult::Added { id } = &result.results[2] {
let memory = store.get(id).unwrap().unwrap();
assert_eq!(
memory.metadata,
Some(r#"{"source": "user", "priority": 1}"#.to_string())
);
} else {
panic!("Expected Added for item 2");
}
}