diff --git a/src/action/message.rs b/src/action/message.rs index e787ddb..23a2af8 100644 --- a/src/action/message.rs +++ b/src/action/message.rs @@ -102,6 +102,7 @@ impl From for MsgAction { UserAction::Addition(data) => Self::Addition(data), UserAction::CreateDocument(data) => Self::Create(data), UserAction::Query(data) => Self::Query(data), + UserAction::Update(data) => Self::Update(data), } } } diff --git a/src/action/user.rs b/src/action/user.rs index b05b202..4f6d266 100644 --- a/src/action/user.rs +++ b/src/action/user.rs @@ -1,4 +1,4 @@ -use super::{Addition, DocDef, FieldType, Query}; +use super::{Addition, DocDef, FieldType, Query, Update}; use crate::{message::MessageAction, name::NameType}; #[derive(Clone, Debug)] @@ -6,6 +6,7 @@ pub enum UserAction { Addition(Addition), CreateDocument(DocDef), Query(Query), + Update(Update), } impl From for UserAction { @@ -26,12 +27,19 @@ impl From for UserAction { } } +impl From for UserAction { + fn from(value: Update) -> Self { + Self::Update(value) + } +} + impl MessageAction for UserAction { fn doc_name(&self) -> &NameType { match self { Self::Addition(data) => data.doc_name(), Self::CreateDocument(data) => data.doc_name(), Self::Query(data) => data.doc_name(), + Self::Update(data) => data.doc_name(), } } } diff --git a/src/document/clock.rs b/src/document/clock.rs index 3329030..607c722 100644 --- a/src/document/clock.rs +++ b/src/document/clock.rs @@ -26,7 +26,7 @@ impl Clock { vec![Name::english("clock")] } - fn gen_message() -> Message { + pub fn gen_message() -> Message { Message::new(MsgAction::OnUpdate(Records::new( Clock::doc_names(), Names::new(), diff --git a/src/document/session.rs b/src/document/session.rs index 4706765..bf50643 100644 --- a/src/document/session.rs +++ b/src/document/session.rs @@ -80,268 +80,3 @@ impl Session { queue.remove_sender(&sender_id); } } - -#[cfg(test)] -mod sessions { - use super::*; - use crate::{ - action::{Addition, MsgAction, Query, Records}, - document::{ - clock::{clock_test_support::gen_clock_message, Clock}, - create::CreateDoc, - field::Field, - }, - mtterror::{ErrorID, MTTError}, - name::{Name, NameType, Names}, - queue::data_director::{Include, Path, RegMsg, Register}, - support_tests::TIMEOUT, - }; - use chrono::{DateTime, Utc}; - use std::{ - collections::HashSet, - sync::mpsc::{Receiver, RecvTimeoutError}, - thread::sleep, - }; - use uuid::Uuid; - - struct Setup { - queue: Queue, - rx: Receiver, - sender_id: Uuid, - } - - impl Setup { - fn new() -> Self { - let mut queue = Queue::new(); - let (tx, rx) = channel(); - let id = queue.add_sender(tx); - CreateDoc::start(queue.clone()); - Clock::start(queue.clone()); - Session::start(queue.clone()); - let paths = [ - Path::new( - Include::All, - Include::Just(NameType::Name(Session::doc_names()[0].clone())), - Include::Just(Action::Records), - ), - Path::new( - Include::All, - Include::Just(NameType::Name(Session::doc_names()[0].clone())), - Include::Just(Action::Error), - ), - ]; - for path in paths.iter() { - let reg = Register::new(id.clone(), RegMsg::AddRoute(path.clone())); - queue.send(Message::new(reg)); - rx.recv_timeout(TIMEOUT).unwrap(); - } - Self { - queue: queue, - rx: rx, - sender_id: id, - } - } - - fn message(action: A) -> Message - where - A: Into, - { - Message::new(action) - } - - fn recv(&self) -> Result { - self.rx.recv_timeout(TIMEOUT) - } - - fn send(&self, msg: Message) { - self.queue.send(msg); - } - - fn send_registry_message(&self, msg: RegMsg) { - let request = Register::new(self.sender_id.clone(), msg); - self.queue.send(Message::new(request)); - } - } - - /* - #[test] - fn is_session_document_created() { - let setup = Setup::new(); - for name in Session::doc_names().iter() { - let path = Path::new(Include::All, Include::Just(name.into()), Include::All); - setup.send_registry_message(RegMsg::AddRoute(path)); - let result = setup.recv().unwrap(); - let action = result.get_action(); - match action { - MsgAction::Register(data) => match data.get_msg() { - RegMsg::RouteID(_) => {} - _ => unreachable!("got {:?} should have been route id", data), - }, - _ => unreachable!("got {:?} should have been data register", action), - } - } - } - - #[test] - fn are_session_ids_unique() { - let setup = Setup::new(); - let mut ids: HashSet = HashSet::new(); - let count = 10; - for _ in 0..count { - let msg = Setup::message(Addition::new(Session::doc_names()[0].clone())); - setup.send(msg); - let result = setup.recv().unwrap(); - match result.get_action() { - MsgAction::Records(recs) => { - assert_eq!(recs.len(), 1); - let rec = recs.iter().last().unwrap(); - let holder = rec.get(Name::english("id")).unwrap(); - let id = match holder { - Field::Uuid(data) => data, - _ => unreachable!("got {:?} should have been uuid", holder), - }; - ids.insert(id); - } - _ => unreachable!("should always return records"), - } - } - assert_eq!(ids.len(), count, "should be {} ids, got {:?}", count, ids); - } - - #[test] - fn expire_default_is_an_hour_from_now() { - let setup = Setup::new(); - let msg = Setup::message(Addition::new(Session::doc_names()[0].clone())); - let start_time = Utc::now() + Duration::from_hours(1); - setup.send(msg); - let result = setup.recv().unwrap(); - let end_time = Utc::now() + Duration::from_hours(1); - let action = result.get_action(); - match action { - MsgAction::Records(recs) => { - assert_eq!(recs.len(), 1); - let rec = recs.iter().last().unwrap(); - let holder = rec.get(Name::english("expire")).unwrap(); - match holder { - Field::DateTime(data) => { - assert!(data > start_time, "expire should be after {:?}", start_time); - assert!(data < end_time, "expire should be before {:?}", end_time); - } - _ => unreachable!("got {:?} should have been date time", holder), - }; - } - _ => unreachable!("got {:?}, should have gotten records", action), - } - } - - #[test] - #[ignore] - fn session_ids_error_when_not_unique() { - let setup = Setup::new(); - let id = Uuid::new_v4(); - let mut addition = Addition::new(Session::doc_names()[0].clone()); - addition.add_field(Name::english("id"), id); - setup.send(Setup::message(addition.clone())); - let holder = setup.recv().unwrap(); - println!("{:?}", holder); - setup.send(Setup::message(addition)); - let result = setup.recv().unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.error_id() { - ErrorID::IndexEntryAlreadyExists => {} - _ => unreachable!("got {:?}, should have been a field duplicate", err), - }, - _ => unreachable!("got {:?}, should have been an error", action), - } - } - */ - - #[test] - fn expire_should_update_on_successful_query() { - let setup = Setup::new(); - let id = Uuid::new_v4(); - let timestamp = Utc::now() + Duration::from_secs(60); - let mut addition = Addition::new(Session::doc_names()[0].clone()); - addition.add_field(Name::english("id"), id.clone()); - addition.add_field(Name::english("expire"), timestamp); - setup.send(Setup::message(addition)); - setup.recv().unwrap(); - let mut query = Query::new(Session::doc_names()[0].clone()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Uuid)) - .unwrap(); - calc.add_value(id).unwrap(); - query.add(Name::english("id"), calc.clone()); - let get_expire_datetime = || -> DateTime { - setup.send(Setup::message(query.clone())); - let result = setup.recv().unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(recs) => { - assert_eq!(recs.len(), 1); - let rec = recs.iter().last().unwrap(); - let holder = rec.get(Name::english("expire")).unwrap(); - match holder { - Field::DateTime(data) => data.clone(), - _ => unreachable!("got {:?} should have been date time", holder), - } - } - _ => unreachable!("got {:?}, should have gotten records", action), - } - }; - let start_time = Utc::now() + Duration::from_secs(3600); - let first_query = get_expire_datetime(); - let end_time = Utc::now() + Duration::from_secs(3601); - assert_eq!(first_query, timestamp); - let second_query = get_expire_datetime(); - assert!( - second_query > start_time, - "{:?} should be after {:?}", - second_query, - start_time - ); - assert!( - second_query < end_time, - "{:?} should be before {:?}", - second_query, - end_time - ); - } - - #[test] - fn clock_removes_expired_sessions() { - let setup = Setup::new(); - let id1 = Uuid::new_v4(); - let id2 = Uuid::new_v4(); - let duration = Duration::from_secs(60); - let expire1 = Utc::now() + duration; - let expire2 = Utc::now() - duration; - let mut addition1 = Addition::new(Session::doc_names()[0].clone()); - addition1.add_field(Name::english("id"), id1.clone()); - addition1.add_field(Name::english("expire"), expire1); - let mut addition2 = Addition::new(Session::doc_names()[0].clone()); - addition2.add_field(Name::english("id"), id2); - addition2.add_field(Name::english("expire"), expire2); - setup.send(Setup::message(addition1)); - setup.send(Setup::message(addition2)); - setup.recv().unwrap(); // Eat addition result. - setup.recv().unwrap(); // Eat addition result. - setup.send(gen_clock_message()); - sleep(TIMEOUT); // Allow time to react to message. - setup.send(Setup::message(Query::new(Session::doc_names()[0].clone()))); - let result = setup.recv().unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(recs) => { - assert_eq!(recs.len(), 1, "nothing was deleted"); - let rec = recs.iter().last().unwrap(); - let id = rec.get(Name::english("id")).unwrap(); - let expire = rec.get(Name::english("expire")).unwrap(); - assert_eq!(id, id1.into(), "\n\n{:?}\n{:?}", Utc::now(), recs); - assert_eq!(expire, expire1.into(), "\n\n{:?}\n{:?}", Utc::now(), recs); - } - _ => unreachable!("got {:?}, should have gotten records", action), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 9659b15..113846c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod mtterror; pub mod name; mod queue; -use action::*; +pub use action::*; use document::{Clock, CreateDoc, Session}; use message::{wrapper::Message, MessageAction}; use queue::{ @@ -127,3 +127,35 @@ impl MoreThanText { } } } + +pub struct TestMoreThanText { + mtt: MoreThanText, + queue: Queue, +} + +impl TestMoreThanText { + pub fn new() -> Self { + let mut mtt = MoreThanText::new(); + let queue = mtt.queue.clone(); + Self { + mtt: mtt, + queue: queue, + } + } + + pub fn validate_session(&mut self, session: Option) -> Uuid { + self.mtt.validate_session(session) + } + + pub fn records(&mut self, request: UA) -> Result + where + UA: Into, + { + self.mtt.records(request) + } + + pub fn send_time_pulse(&self) { + let msg = Clock::gen_message(); + self.queue.send(msg); + } +} diff --git a/tests/lib_session_test.rs b/tests/lib_session_test.rs index b95a6bd..c443677 100644 --- a/tests/lib_session_test.rs +++ b/tests/lib_session_test.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Utc}; use morethantext::{ action::{Addition, CalcValue, Calculation, Field, FieldType, Operand, Query, Record}, - MTTError, MoreThanText, Name, + MTTError, MoreThanText, Name, TestMoreThanText, Update, }; use std::time::Duration; use uuid::Uuid; @@ -90,3 +90,46 @@ fn are_session_ids_unique_on_update() { }, } } + +#[test] +fn does_expire_update_on_query() { + let mut mtt = MoreThanText::new(); + let id = mtt.validate_session(None); + let start_time = Utc::now() + Duration::from_secs(3600); + mtt.validate_session(Some(id.to_string())); + let end_time = Utc::now() + Duration::from_secs(3601); + let rec = get_session(&mut mtt, &id).unwrap(); + let holder = rec.get(Name::english("expire")).unwrap(); + match holder { + Field::DateTime(data) => { + assert!(data > start_time, "expire should be after {:?}", start_time); + assert!(data < end_time, "expire should be before {:?}", end_time); + } + _ => unreachable!("got {:?} should have been date time", holder), + } +} + +#[test] +#[ignore = "failing to update"] +fn are_expired_sessions_removed() { + let mut mtt = TestMoreThanText::new(); + let id = mtt.validate_session(None); + let mut update = Update::new(doc_name()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(CalcValue::Existing(FieldType::Uuid)) + .unwrap(); + calc.add_value(id.clone()).unwrap(); + update + .get_query_mut() + .add(Name::english("id"), calc.clone()); + let expire = Utc::now() - Duration::from_secs(10); + update + .get_values_mut() + .add_field(Name::english("expire"), expire); + mtt.records(update).unwrap(); + mtt.send_time_pulse(); + let mut qry = Query::new(doc_name()); + qry.add(Name::english("id"), calc.clone()); + let result = mtt.records(qry).unwrap(); + assert_eq!(result.len(), 0); +}