use uuid::Uuid;
use crate::session::model::Session;
use crate::utils::error::ChainError;
pub trait SessionStore: Send + Sync {
fn get(&self, id: Uuid) -> Result<Session, ChainError>;
fn save(&self, session: Session) -> Result<(), ChainError>;
fn delete(&self, id: Uuid) -> Result<bool, ChainError>;
fn cleanup(&self) -> Result<usize, ChainError>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::session::model::Session;
use crate::utils::error::ChainError;
use mockall::mock;
use optionstratlib::simulation::WalkType;
use optionstratlib::utils::TimeFrame;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use uuid::Uuid;
mock! {
pub SessionStore {}
impl SessionStore for SessionStore {
fn get(&self, id: Uuid) -> Result<Session, ChainError>;
fn save(&self, session: Session) -> Result<(), ChainError>;
fn delete(&self, id: Uuid) -> Result<bool, ChainError>;
fn cleanup(&self) -> Result<usize, ChainError>;
}
}
struct TestSessionStore {
sessions: Arc<Mutex<HashMap<Uuid, Session>>>,
}
impl TestSessionStore {
fn new() -> Self {
TestSessionStore {
sessions: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl SessionStore for TestSessionStore {
fn get(&self, id: Uuid) -> Result<Session, ChainError> {
let sessions = self.sessions.lock().unwrap();
match sessions.get(&id) {
Some(session) => Ok(session.clone()),
None => Err(ChainError::NotFound(format!(
"Session with id {} not found",
id
))),
}
}
fn save(&self, session: Session) -> Result<(), ChainError> {
let mut sessions = self.sessions.lock().unwrap();
sessions.insert(session.id, session);
Ok(())
}
fn delete(&self, id: Uuid) -> Result<bool, ChainError> {
let mut sessions = self.sessions.lock().unwrap();
Ok(sessions.remove(&id).is_some())
}
fn cleanup(&self) -> Result<usize, ChainError> {
let now = SystemTime::now();
let mut sessions = self.sessions.lock().unwrap();
let old_count = sessions.len();
sessions.retain(|_, session| {
match now.duration_since(session.updated_at) {
Ok(duration) => duration.as_secs() < 3600, Err(_) => true, }
});
Ok(old_count - sessions.len())
}
}
fn create_test_session(id: Option<Uuid>) -> Session {
use crate::session::model::{SessionState, SimulationParameters};
Session {
id: id.unwrap_or_else(Uuid::new_v4),
created_at: SystemTime::now(),
updated_at: SystemTime::now(),
parameters: SimulationParameters {
symbol: "".to_string(),
steps: 0,
initial_price: Default::default(),
days_to_expiration: Default::default(),
volatility: Default::default(),
risk_free_rate: Default::default(),
dividend_yield: Default::default(),
method: WalkType::Brownian {
dt: Default::default(),
drift: Default::default(),
volatility: Default::default(),
},
time_frame: TimeFrame::Microsecond,
chain_size: None,
strike_interval: None,
skew_slope: None,
smile_curve: None,
spread: None,
seed: None,
},
current_step: 0,
total_steps: 100,
state: SessionState::Initialized,
}
}
#[test]
fn test_get_existing_session() {
let store = TestSessionStore::new();
let session = create_test_session(None);
let session_id = session.id;
store.save(session.clone()).unwrap();
let result = store.get(session_id);
assert!(result.is_ok());
let retrieved_session = result.unwrap();
assert_eq!(retrieved_session.id, session_id);
assert_eq!(retrieved_session.current_step, session.current_step);
assert_eq!(retrieved_session.total_steps, session.total_steps);
}
#[test]
fn test_get_non_existing_session() {
let store = TestSessionStore::new();
let non_existent_id = Uuid::new_v4();
let result = store.get(non_existent_id);
assert!(result.is_err());
match result {
Err(ChainError::NotFound(_)) => {} _ => panic!("Expected NotFound error"),
}
}
#[test]
fn test_save_new_session() {
let store = TestSessionStore::new();
let session = create_test_session(None);
let session_id = session.id;
let save_result = store.save(session);
assert!(save_result.is_ok());
let get_result = store.get(session_id);
assert!(get_result.is_ok());
}
#[test]
fn test_save_existing_session() {
let store = TestSessionStore::new();
let mut session = create_test_session(None);
let session_id = session.id;
store.save(session.clone()).unwrap();
session.current_step = 50;
let save_result = store.save(session.clone());
assert!(save_result.is_ok());
let get_result = store.get(session_id).unwrap();
assert_eq!(get_result.current_step, 50);
}
#[test]
fn test_delete_existing_session() {
let store = TestSessionStore::new();
let session = create_test_session(None);
let session_id = session.id;
store.save(session).unwrap();
let delete_result = store.delete(session_id);
assert!(delete_result.is_ok());
assert!(delete_result.unwrap());
let get_result = store.get(session_id);
assert!(get_result.is_err());
}
#[test]
fn test_delete_non_existing_session() {
let store = TestSessionStore::new();
let non_existent_id = Uuid::new_v4();
let delete_result = store.delete(non_existent_id);
assert!(delete_result.is_ok());
assert!(!delete_result.unwrap()); }
#[test]
fn test_cleanup_with_no_expired_sessions() {
let store = TestSessionStore::new();
for _ in 0..5 {
store.save(create_test_session(None)).unwrap();
}
let cleanup_result = store.cleanup();
assert!(cleanup_result.is_ok());
assert_eq!(cleanup_result.unwrap(), 0); }
}