#[cfg(test)]
use super::session::{Session, SessionId, SessionManager, StorageBackend, SessionStorage, FileStorage, MemoryStorage};
#[cfg(test)]
use crate::Result;
#[cfg(test)]
use tempfile::TempDir;
#[cfg(test)]
use std::path::PathBuf;
#[cfg(test)]
fn create_test_session(id: &str) -> Session {
let mut session = Session::new(SessionId::new(id))
.with_system_prompt("Test system prompt");
session.metadata.insert(
"test_key".to_string(),
serde_json::json!("test_value")
);
session.metadata.insert(
"counter".to_string(),
serde_json::json!(42)
);
session
}
#[tokio::test]
async fn test_memory_storage_basic_operations() -> Result<()> {
let manager = SessionManager::new();
let session = manager.create_session()
.with_system_prompt("Memory test")
.with_metadata("key", serde_json::json!("value"))
.build()
.await?;
let session_id = session.id().clone();
let retrieved = manager.get(&session_id).await?;
assert!(retrieved.is_some());
let retrieved_session = retrieved.unwrap();
assert_eq!(retrieved_session.id(), &session_id);
assert_eq!(retrieved_session.system_prompt, Some("Memory test".to_string()));
assert_eq!(
retrieved_session.metadata.get("key"),
Some(&serde_json::json!("value"))
);
let ids = manager.list().await?;
assert_eq!(ids.len(), 1);
assert_eq!(ids[0], session_id);
manager.delete(&session_id).await?;
assert!(manager.get(&session_id).await?.is_none());
Ok(())
}
#[tokio::test]
async fn test_file_storage_persistence() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path().to_path_buf();
let manager = SessionManager::with_storage(
StorageBackend::File(storage_path.clone())
);
let stored_session = manager.create_session()
.with_system_prompt("File storage test")
.with_metadata("persistent", serde_json::json!(true))
.build()
.await?;
let stored_id = stored_session.id().clone();
let new_manager = SessionManager::with_storage(
StorageBackend::File(storage_path)
);
let retrieved = new_manager.get(&stored_id).await?;
assert!(retrieved.is_some());
let retrieved_session = retrieved.unwrap();
assert_eq!(retrieved_session.system_prompt, Some("File storage test".to_string()));
assert_eq!(
retrieved_session.metadata.get("persistent"),
Some(&serde_json::json!(true))
);
Ok(())
}
#[tokio::test]
async fn test_file_storage_multiple_sessions() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path().to_path_buf();
let manager = SessionManager::with_storage(
StorageBackend::File(storage_path)
);
let mut session_ids = Vec::new();
for i in 0..5 {
let session = manager.create_session()
.with_system_prompt(format!("Session {}", i))
.with_metadata("index", serde_json::json!(i))
.build()
.await?;
session_ids.push(session.id().clone());
}
let listed_ids = manager.list().await?;
assert_eq!(listed_ids.len(), 5);
for (i, id) in session_ids.iter().enumerate() {
let session = manager.get(id).await?.unwrap();
assert_eq!(session.system_prompt, Some(format!("Session {}", i)));
assert_eq!(
session.metadata.get("index"),
Some(&serde_json::json!(i))
);
}
manager.clear().await?;
assert_eq!(manager.list().await?.len(), 0);
Ok(())
}
#[tokio::test]
async fn test_file_storage_update_session() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path().to_path_buf();
let manager = SessionManager::with_storage(
StorageBackend::File(storage_path.clone())
);
let session = manager.create_session()
.with_system_prompt("Initial prompt")
.with_metadata("version", serde_json::json!(1))
.build()
.await?;
let session_id = session.id().clone();
let mut updated_session = session.clone();
updated_session.system_prompt = Some("Updated prompt".to_string());
updated_session.metadata.insert("version".to_string(), serde_json::json!(2));
let file_storage = FileStorage::new(storage_path.clone());
file_storage.save(&updated_session).await?;
let retrieved = manager.get(&session_id).await?.unwrap();
assert_eq!(retrieved.system_prompt, Some("Updated prompt".to_string()));
assert_eq!(
retrieved.metadata.get("version"),
Some(&serde_json::json!(2))
);
assert!(retrieved.updated_at > session.created_at);
Ok(())
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_sqlite_storage_basic_operations() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("test_sessions.db");
let manager = SessionManager::with_storage_async(
StorageBackend::Sqlite(db_path.clone())
).await?;
let session = manager.create_session()
.with_system_prompt("SQLite test")
.with_metadata("db", serde_json::json!("sqlite"))
.build()
.await?;
let session_id = session.id().clone();
let retrieved = manager.get(&session_id).await?;
assert!(retrieved.is_some());
let retrieved_session = retrieved.unwrap();
assert_eq!(retrieved_session.system_prompt, Some("SQLite test".to_string()));
assert_eq!(
retrieved_session.metadata.get("db"),
Some(&serde_json::json!("sqlite"))
);
Ok(())
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_sqlite_storage_persistence_across_instances() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("persist_test.db");
{
let manager = SessionManager::with_storage_async(
StorageBackend::Sqlite(db_path.clone())
).await?;
for i in 0..3 {
manager.create_session()
.with_system_prompt(format!("Persistent session {}", i))
.with_metadata("order", serde_json::json!(i))
.build()
.await?;
}
}
{
let manager = SessionManager::with_storage_async(
StorageBackend::Sqlite(db_path.clone())
).await?;
let sessions = manager.list().await?;
assert_eq!(sessions.len(), 3);
for session_id in sessions.iter() {
let session = manager.get(session_id).await?.unwrap();
assert!(session.system_prompt.is_some());
assert!(session.metadata.contains_key("order"));
}
}
Ok(())
}
#[tokio::test]
async fn test_session_not_found_error() -> Result<()> {
let manager = SessionManager::new();
let non_existent_id = SessionId::new("does-not-exist");
assert!(manager.get(&non_existent_id).await?.is_none());
let result = manager.resume(&non_existent_id).await;
assert!(result.is_err());
match result {
Err(crate::Error::SessionNotFound(id)) => {
assert_eq!(id, "does-not-exist");
}
_ => panic!("Expected SessionNotFound error"),
}
Ok(())
}
#[tokio::test]
async fn test_session_builder_with_id() -> Result<()> {
use super::session::SessionBuilder;
let custom_id = "custom-session-id";
let session = SessionBuilder::with_id(custom_id)
.with_system_prompt("Custom ID session")
.build()
.await?;
assert_eq!(session.id().as_str(), custom_id);
assert_eq!(session.system_prompt, Some("Custom ID session".to_string()));
Ok(())
}
#[tokio::test]
async fn test_file_storage_invalid_characters_in_id() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path().to_path_buf();
let manager = SessionManager::with_storage(
StorageBackend::File(storage_path)
);
let stored = manager.create_session()
.with_system_prompt("Path test")
.build()
.await?;
let retrieved = manager.get(&stored.id()).await?;
assert!(retrieved.is_some());
Ok(())
}
#[tokio::test]
async fn test_concurrent_session_access() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path().to_path_buf();
let manager = SessionManager::with_storage(
StorageBackend::File(storage_path)
);
let session = manager.create_session()
.with_system_prompt("Concurrent test")
.build()
.await?;
let session_id = session.id().clone();
let mut handles = Vec::new();
for i in 0..10 {
let manager_clone = manager.clone();
let id_clone = session_id.clone();
let handle = tokio::spawn(async move {
let result = manager_clone.get(&id_clone).await;
assert!(result.is_ok());
assert!(result.unwrap().is_some());
i
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.unwrap();
assert!(result < 10);
}
Ok(())
}
#[tokio::test]
async fn test_storage_backend_selection() {
let memory_manager = SessionManager::new();
assert!(!format!("{:?}", memory_manager).is_empty());
let temp_dir = TempDir::new().unwrap();
let file_manager = SessionManager::with_storage(
StorageBackend::File(temp_dir.path().to_path_buf())
);
assert!(!format!("{:?}", file_manager).is_empty());
#[cfg(feature = "sqlite")]
{
let result = std::panic::catch_unwind(|| {
SessionManager::with_storage(
StorageBackend::Sqlite(PathBuf::from("test.db"))
)
});
assert!(result.is_err());
}
}
#[tokio::test]
async fn test_session_timestamps() -> Result<()> {
let manager = SessionManager::new();
let session = manager.create_session()
.with_system_prompt("Timestamp test")
.build()
.await?;
assert!(session.created_at <= session.updated_at);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let storage = MemoryStorage::new();
storage.save(&session).await?;
Ok(())
}