use std::cell::RefCell;
use std::rc::Rc;
use crate::IdTargeted;
use crate::pool_item;
#[derive(Debug, Clone)]
pub struct HistoryTracker {
log: Rc<RefCell<Vec<String>>>,
}
impl HistoryTracker {
pub fn add_entry(&self, entry: String) {
self.log.borrow_mut().push(entry);
}
}
#[derive(Debug)]
pub struct UserSession {
id: u64,
log: Rc<RefCell<Vec<String>>>,
tracker: HistoryTracker,
}
impl IdTargeted for UserSession {
fn id(&self) -> u64 {
self.id
}
}
#[pool_item]
impl UserSession {
pub fn new(id: u64) -> Self {
let log = Rc::new(RefCell::new(Vec::new()));
let tracker = HistoryTracker { log: log.clone() };
Self { id, log, tracker }
}
#[messaging(LogActionRequest, LogActionResponse)]
pub fn log_action(&self, action: String) -> usize {
self.tracker.add_entry(format!("Action: {}", action));
self.log.borrow().len()
}
#[messaging(GetLogRequest, GetLogResponse)]
pub fn get_log(&self) -> Vec<String> {
self.log.borrow().clone()
}
}
#[cfg(test)]
mod tests {
use crate::ThreadPool;
use super::*;
#[test]
fn given_user_session_when_logging_actions_then_count_increments() {
let thread_pool = ThreadPool::<UserSession>::new(2);
thread_pool
.send_and_receive(vec![UserSessionInit(1)].into_iter())
.expect("session creation")
.for_each(|_| {});
let counts: Vec<usize> = thread_pool
.send_and_receive(
vec![
LogActionRequest(1, "Login".to_string()),
LogActionRequest(1, "ViewProfile".to_string()),
LogActionRequest(1, "Logout".to_string()),
]
.into_iter(),
)
.expect("actions")
.map(|resp| resp.result)
.collect();
assert_eq!(counts, vec![1, 2, 3]);
}
#[test]
fn given_user_session_when_getting_log_then_returns_all_entries() {
let thread_pool = ThreadPool::<UserSession>::new(2);
thread_pool
.send_and_receive(vec![UserSessionInit(1)].into_iter())
.expect("session creation")
.for_each(|_| {});
thread_pool
.send_and_receive(
vec![
LogActionRequest(1, "Login".to_string()),
LogActionRequest(1, "Logout".to_string()),
]
.into_iter(),
)
.expect("actions")
.for_each(|_| {});
let log = thread_pool
.send_and_receive(vec![GetLogRequest(1)].into_iter())
.expect("get log")
.next()
.unwrap()
.result;
assert_eq!(log.len(), 2);
assert_eq!(log[0], "Action: Login");
assert_eq!(log[1], "Action: Logout");
}
#[test]
fn given_multiple_sessions_when_logging_actions_then_each_has_independent_state() {
let thread_pool = ThreadPool::<UserSession>::new(2);
thread_pool
.send_and_receive(vec![UserSessionInit(1), UserSessionInit(2)].into_iter())
.expect("session creation")
.for_each(|_| {});
thread_pool
.send_and_receive(vec![LogActionRequest(1, "Action1".to_string())].into_iter())
.expect("action")
.for_each(|_| {});
thread_pool
.send_and_receive(
vec![
LogActionRequest(2, "ActionA".to_string()),
LogActionRequest(2, "ActionB".to_string()),
]
.into_iter(),
)
.expect("actions")
.for_each(|_| {});
let log1 = thread_pool
.send_and_receive(vec![GetLogRequest(1)].into_iter())
.expect("get log")
.next()
.unwrap()
.result;
let log2 = thread_pool
.send_and_receive(vec![GetLogRequest(2)].into_iter())
.expect("get log")
.next()
.unwrap()
.result;
assert_eq!(log1.len(), 1);
assert_eq!(log2.len(), 2);
}
}