use crate::{ data_director::{Include, Path, RegMsg, Register, Route}, document::{ create::IndexType, definition::{DocDef, DocFuncType}, field::{Field, FieldType}, }, mtterror::MTTError, name::{Name, NameType, Names}, router::Queue, }; use chrono::prelude::*; use std::{ collections::{HashMap, HashSet}, sync::mpsc::{channel, Receiver}, thread::spawn, time::Duration, }; use uuid::Uuid; #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Action { Addition, Create, Delete, Error, GetLog, Log, OnAddition, OnDelete, OnQuery, OnUpdate, Query, Records, Register, Reply, Show, Update, } impl From for Action { fn from(value: MsgAction) -> Self { match value { MsgAction::Addition(_) => Action::Addition, MsgAction::Create(_) => Action::Create, MsgAction::Delete(_) => Action::Delete, MsgAction::Error(_) => Action::Error, MsgAction::GetLog(_) => Action::GetLog, MsgAction::Log(_) => Action::Log, MsgAction::OnAddition(_) => Action::OnAddition, MsgAction::OnDelete(_) => Action::OnDelete, MsgAction::OnQuery(_) => Action::OnQuery, MsgAction::OnUpdate(_) => Action::OnUpdate, MsgAction::Query(_) => Action::Query, MsgAction::Records(_) => Action::Records, MsgAction::Register(_) => Action::Register, MsgAction::Reply(_) => Action::Reply, MsgAction::Show => Action::Show, MsgAction::Update(_) => Action::Update, } } } impl From<&MsgAction> for Action { fn from(value: &MsgAction) -> Self { let action = value.clone(); Self::from(action) } } #[derive(Clone, Debug)] pub enum MsgAction { Addition(Addition), Create(DocDef), // Alter // Remove Error(MTTError), GetLog(Uuid), Log(Vec), OnAddition(Records), OnDelete(Records), OnQuery(Records), OnUpdate(Records), Query(Query), Records(Records), Register(Register), Reply(Reply), Show, Delete(Delete), Update(Update), } impl From for MsgAction { fn from(value: Addition) -> Self { MsgAction::Addition(value) } } impl From for MsgAction { fn from(value: Delete) -> Self { MsgAction::Delete(value) } } impl From for MsgAction { fn from(value: DocDef) -> Self { MsgAction::Create(value) } } impl From for MsgAction { fn from(value: MTTError) -> Self { MsgAction::Error(value) } } impl From for MsgAction { fn from(value: Query) -> Self { MsgAction::Query(value) } } impl From for MsgAction { fn from(value: Records) -> Self { MsgAction::Records(value) } } impl From for MsgAction { fn from(value: Register) -> Self { MsgAction::Register(value) } } impl From for MsgAction { fn from(value: Reply) -> Self { MsgAction::Reply(value) } } impl From for MsgAction { fn from(value: Update) -> Self { MsgAction::Update(value) } } impl From for MsgAction { fn from(value: Uuid) -> Self { MsgAction::GetLog(value) } } impl From<&Uuid> for MsgAction { fn from(value: &Uuid) -> Self { Self::from(value.clone()) } } #[cfg(test)] mod msgactions { use super::*; #[test] fn turn_document_definition_into_action() { let name = Name::english(Uuid::new_v4().to_string().as_str()); let value = DocDef::new(name.clone()); let result: MsgAction = value.into(); match result { MsgAction::Create(def) => assert_eq!(def.get_document_names(), &[name].to_vec()), _ => unreachable!("Got {:?}: dhould have been create", result), } } #[test] fn turn_error_into_action() { let data = "data".to_string(); let value = MTTError::DocumentAlreadyExists(data.clone()); let result: MsgAction = value.into(); match result { MsgAction::Error(result) => match result { MTTError::DocumentAlreadyExists(output) => assert_eq!(output, data), _ => unreachable!("Got {:?}: dhould have been create", result), }, _ => unreachable!("Got {:?}: dhould have been create", result), } let value = MTTError::DocumentNotFound(data.clone()); let result: MsgAction = value.into(); match result { MsgAction::Error(result) => match result { MTTError::DocumentNotFound(output) => assert_eq!(output, data), _ => unreachable!("Got {:?}: dhould have been create", result), }, _ => unreachable!("Got {:?}: dhould have been create", result), } } #[test] fn turn_query_into_action() { let value = Query::new(); let result: MsgAction = value.into(); match result { MsgAction::Query(_) => {} _ => unreachable!("Got {:?}: dhould have been query", result), } } #[test] fn turn_reply_into_action() { let value = Reply::new(); let result: MsgAction = value.into(); match result { MsgAction::Reply(_) => {} _ => unreachable!("Got {:?}: dhould have been reply", result), } } } #[derive(Clone, Debug)] pub struct Message { msg_id: Uuid, document_id: NameType, action: MsgAction, route: Route, // session: Option } impl Message { pub fn new(doc_id: D, action: A) -> Self where D: Into, A: Into, { Self { msg_id: Uuid::new_v4(), document_id: doc_id.into(), action: action.into(), route: Route::default(), } } pub fn get_message_id(&self) -> &Uuid { &self.msg_id } pub fn get_action(&self) -> &MsgAction { &self.action } pub fn get_path(&self) -> Path { Path::new( Include::Just(self.msg_id.clone()), Include::Just(self.document_id.clone()), Include::Just(self.action.clone().into()), ) } pub fn get_route(&self) -> Route { self.route.clone() } pub fn set_route(&mut self, route: Route) { self.route = route; } pub fn response(&self, action: A) -> Self where A: Into, { Self { msg_id: self.msg_id.clone(), document_id: self.document_id.clone(), action: action.into(), route: Route::default(), } } pub fn forward(&self, doc_id: D, action: A) -> Self where D: Into, A: Into, { Self { msg_id: self.msg_id.clone(), document_id: doc_id.into(), action: action.into(), route: Route::default(), } } } #[cfg(test)] mod messages { use super::*; #[test] fn can_the_document_be_a_named_reference() { let dts = [Name::english("one"), Name::english("two")]; for document in dts.into_iter() { let msg = Message::new( document.clone(), MsgAction::Create(DocDef::new(document.clone())), ); match &msg.document_id { NameType::Name(data) => assert_eq!(data, &document), _ => unreachable!("should have been a string id"), } match msg.get_action() { MsgAction::Create(_) => {} _ => unreachable!("should have been a create document"), } } } #[test] fn can_the_document_be_an_id() { let document = Uuid::new_v4(); let msg = Message::new(document.clone(), Query::new()); match msg.document_id { NameType::ID(data) => assert_eq!(data, document), _ => unreachable!("should have been an id"), } match msg.get_action() { MsgAction::Query(_) => {} _ => unreachable!("should have been an access query"), } } #[test] fn do_messages_contain_routes() { let mut msg = Message::new(Name::english("whatever"), Query::new()); let default_route = msg.get_route(); match default_route.msg_id { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } match default_route.doc_id { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } match default_route.action { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } let doc_id = Uuid::new_v4(); let route = Route::new( Include::Just(msg.get_message_id().clone()), Include::Just(doc_id.clone()), Include::Just(msg.get_action().into()), ); msg.set_route(route); let result = msg.get_route(); match result.msg_id { Include::Just(data) => assert_eq!(&data, msg.get_message_id()), Include::All => unreachable!("should have message id"), } match result.doc_id { Include::Just(data) => assert_eq!(data, doc_id), Include::All => unreachable!("should have document id"), } match result.action { Include::Just(data) => assert_eq!(data, msg.get_action().into()), Include::All => unreachable!("should have action"), } } #[test] fn is_the_message_id_random() { let mut ids: Vec = Vec::new(); for _ in 0..5 { let msg = Message::new(Name::english("tester"), Query::new()); let id = msg.get_message_id().clone(); assert!(!ids.contains(&id), "{:?} containts {}", ids, id); ids.push(id); } } #[test] fn can_make_reply_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let responce = Reply::new(); let reply = msg.response(responce); assert_eq!(reply.get_message_id(), msg.get_message_id()); match &reply.document_id { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match reply.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("should have been a reply"), } } #[test] fn can_make_error_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let err_msg = Uuid::new_v4().to_string(); let result = msg.response(MTTError::DocumentNotFound(err_msg.clone())); assert_eq!(result.get_message_id(), msg.get_message_id()); match &result.document_id { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match result.get_action() { MsgAction::Error(data) => match data { MTTError::DocumentNotFound(txt) => assert_eq!(txt, &err_msg), _ => unreachable!("got {:?}, should have received not found", data), }, _ => unreachable!("should have been a reply"), } } #[test] fn can_make_a_response_message() { let doc_id = Uuid::new_v4(); let msg = Message::new(doc_id.clone(), Query::new()); let data = Uuid::new_v4().to_string(); let result1 = msg.response(MTTError::DocumentNotFound(data.clone())); let result2 = msg.response(Reply::new()); assert_eq!(result1.get_message_id(), msg.get_message_id()); assert_eq!(result2.get_message_id(), msg.get_message_id()); assert_eq!(result1.document_id, msg.document_id); assert_eq!(result2.document_id, msg.document_id); let action1 = result1.get_action(); match action1 { MsgAction::Error(err) => match err { MTTError::DocumentNotFound(output) => assert_eq!(output, &data), _ => unreachable!("got {:?}: should have received document not found", err), }, _ => unreachable!("got {:?}: should have received error", action1), } let action2 = result2.get_action(); match action2 { MsgAction::Reply(data) => assert_eq!(data.len(), 0), _ => unreachable!("got {:?}: should have received a reply", action2), } } } #[derive(Clone, Debug)] pub struct Addition { data: Document, } impl Addition { pub fn new() -> Self { Self { data: Document::new(), } } #[allow(dead_code)] pub fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.add_field(name, field); } #[allow(dead_code)] fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { self.data.get_field(name) } #[allow(dead_code)] fn get_document(&self) -> Document { self.data.clone() } pub fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod additions { use super::*; #[test] fn can_add_static_string() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(result) => match result { Field::StaticString(output) => assert_eq!(output, &data), _ => unreachable!("got {:?}, should have been a string", result), }, _ => unreachable!("got {:?}: should have received value", result), } } #[test] fn can_add_uuid() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let output = add.get_field(&name); match output { CalcValue::Value(result) => match result { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received uuid", result), }, _ => unreachable!("got {:?}: should have received value", output), } } #[test] fn can_get_document() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let doc = add.get_document(); let output = doc.get_field(&name); match output { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("should have received uuid"), }, _ => unreachable!("got {:?}: should have received value", output), } } } #[derive(Clone, Debug)] pub enum Operand { Add, Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, } impl Operand { #[allow(dead_code)] fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => x == y, Self::GreaterThan => x > y, Self::GreaterThanEqual => x >= y, Self::LessThan => x < y, Self::LessThanEqual => x <= y, _ => false, } } } #[cfg(test)] mod operands { use super::*; #[test] fn equals_true() { let data: Field = Uuid::new_v4().into(); assert!(Operand::Equal.validate(&data, &data)); } #[test] fn equals_false() { let x: Field = Uuid::new_v4().into(); let mut y: Field = Uuid::new_v4().into(); while x == y { y = Uuid::new_v4().into(); } assert!(!Operand::Equal.validate(&x, &y)); } #[test] fn does_greater() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThan.validate(&data[0], &data[1])); assert!(!Operand::GreaterThan.validate(&data[1], &data[1])); assert!(Operand::GreaterThan.validate(&data[2], &data[1])); } #[test] fn does_greater_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThanEqual.validate(&data[0], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[1], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[2], &data[1])); } #[test] fn does_lesser() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThan.validate(&data[0], &data[1])); assert!(!Operand::LessThan.validate(&data[1], &data[1])); assert!(!Operand::LessThan.validate(&data[2], &data[1])); } #[test] fn does_lesser_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThanEqual.validate(&data[0], &data[1])); assert!(Operand::LessThanEqual.validate(&data[1], &data[1])); assert!(!Operand::LessThanEqual.validate(&data[2], &data[1])); } } #[derive(Clone, Debug)] pub enum CalcValue { Calculate(Calculation), Existing(FieldType), FType(FieldType), None, Value(Field), } impl CalcValue { pub fn get(&self, existing: &Field) -> Field { match self { Self::Calculate(calc) => calc.calculate(existing), Self::Existing(_) => existing.clone(), Self::FType(ftype) => ftype.get_default(), Self::None => Field::None, Self::Value(field) => field.clone(), } } fn get_type(&self) -> FieldType { match self { Self::Calculate(calc) => calc.get_type(), Self::Existing(ftype) => ftype.clone(), Self::FType(ftype) => ftype.clone(), Self::None => FieldType::None, Self::Value(field) => field.into(), } } } impl From for CalcValue { fn from(value: Calculation) -> Self { Self::Calculate(value) } } impl From for CalcValue { fn from(value: Field) -> Self { Self::Value(value) } } impl From<&Field> for CalcValue { fn from(value: &Field) -> Self { Self::from(value.clone()) } } impl From for CalcValue { fn from(value: FieldType) -> Self { Self::FType(value) } } impl From for CalcValue { fn from(value: bool) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From> for CalcValue { fn from(value: DateTime) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Duration) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: i128) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From<&str> for CalcValue { fn from(value: &str) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: String) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Uuid) -> Self { let output: Field = value.into(); Self::from(output).into() } } #[cfg(test)] mod calcvalues { use super::*; #[test] fn from_uuid() { let value = Uuid::new_v4(); let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_str() { let value = "something"; let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_string() { let value = "data".to_string(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_boolean() { let value = true; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_datetime() { let value = Utc::now(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_duration() { let value = Duration::from_secs(5); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_integer() { let value: i128 = 5; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_calculation() { let duration = Duration::from_secs(300); let start = Utc::now() + duration; let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime).unwrap(); calc.add_value(duration.clone()).unwrap(); let result: CalcValue = calc.into(); let data = match result.get(&Field::None) { Field::DateTime(data) => data, _ => unreachable!(), }; let stop = Utc::now() + duration; assert!( data > start && data < stop, "should be about 5 minutes ahead" ); } } #[derive(Clone, Debug)] pub struct Calculation { operation: Operand, values: Vec, } impl Calculation { pub fn new(operand: Operand) -> Self { Self { operation: operand, values: Vec::new(), } } #[allow(dead_code)] fn operation(&self) -> &Operand { &self.operation } #[allow(dead_code)] fn get_fields(&self, existing: Field) -> Vec { let mut output = Vec::new(); for item in self.values.iter() { output.push(item.get(&existing)); } output } pub fn get_type(&self) -> FieldType { if self.values.is_empty() { FieldType::None } else { self.values[0].get_type() } } pub fn add_value(&mut self, data: CV) -> Result<(), MTTError> where CV: Into, { let holder: CalcValue = data.into(); if self.values.is_empty() { self.values.push(holder); return Ok(()); } let mut base = self.get_type(); match self.operation { Operand::Add => match base { FieldType::DateTime => base = FieldType::Duration, _ => {} }, _ => {} } let ftype = holder.get_type(); if base == ftype { self.values.push(holder); } else { return Err(MTTError::DocumentFieldWrongDataType(base, ftype)); } Ok(()) } #[allow(dead_code)] fn validate_value(&self, value: CV) -> Result<(), MTTError> where CV: Into, { if self.values.is_empty() { return Ok(()); } let holder = value.into(); let mut base = self.get_type(); match self.operation { Operand::Add => { if base == FieldType::DateTime { base = FieldType::Duration; } } _ => {} } let ftype = holder.get_type(); if base == ftype { Ok(()) } else { Err(MTTError::DocumentFieldWrongDataType(base, ftype)) } } pub fn calculate(&self, existing: &Field) -> Field { let mut result = Field::None; match self.operation { Operand::Add => { let mut first = true; for value in self.values.iter() { let data = value.get(existing); if first { result = data; first = false; } else { result += data; } } } Operand::Equal => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .equal(&self.values[1].get(existing)); } } Operand::GreaterThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater(&self.values[1].get(existing)); } } Operand::GreaterThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater_equal(&self.values[1].get(existing)); } } Operand::LessThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser(&self.values[1].get(existing)); } } Operand::LessThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser_equal(&self.values[1].get(existing)); } } } result } } #[cfg(test)] mod calculations { use super::*; use rand::random; #[test] fn errors_on_different_field_types() { let mut calc = Calculation::new(Operand::Equal); calc.add_value(Uuid::nil()).unwrap(); match calc.add_value("other") { Ok(_) => unreachable!("should have errored with wrong type"), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(expected, FieldType::Uuid); assert_eq!(got, FieldType::StaticString); } _ => unreachable!("got {:?}, expected wrong field type", err), }, } } #[test] fn returns_reference_to_operand() { let calc = Calculation::new(Operand::Add); match calc.operation() { Operand::Add => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } let calc = Calculation::new(Operand::Equal); match calc.operation() { Operand::Equal => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } } #[test] fn can_equal_true() { let mut calc = Calculation::new(Operand::Equal); let data: Field = Uuid::new_v4().into(); calc.add_value(data.clone()).unwrap(); calc.add_value(data.clone()).unwrap(); let expected: Field = true.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_equal_false() { let mut calc = Calculation::new(Operand::Equal); let value1: Field = "fred".into(); let value2: Field = "barney".into(); calc.add_value(value1).unwrap(); calc.add_value(value2).unwrap(); let expected: Field = false.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_greater_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), false.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThan); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_greater_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), true.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThanEqual); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), false.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThan); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), true.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThanEqual); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_add_numbers() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()).unwrap(); calc.add_value(value2.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn can_use_existing_values() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()).unwrap(); calc.add_value(CalcValue::Existing(FieldType::Integer)) .unwrap(); let result = calc.calculate(&value2); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn returns_error_on_mismatch() { let mut calc = Calculation::new(Operand::Add); calc.add_value(Uuid::nil()).unwrap(); match calc.add_value("mismatch") { Ok(_) => unreachable!("should have returned an error"), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(got, FieldType::StaticString); assert_eq!(expected, FieldType::Uuid); } _ => unreachable!("got {:?}, expected wrong field type", err), }, } } #[test] fn datetime_accepts_duration() { let mut calc = Calculation::new(Operand::Add); let duration = Duration::from_secs(3600); let start = Utc::now() + duration; calc.add_value(FieldType::DateTime).unwrap(); match calc.add_value(duration.clone()) { Ok(_) => {} Err(err) => unreachable!("got {:?}, should have returned normally", err), } let result = calc.calculate(&Field::None); let stop = Utc::now() + duration; match result { Field::DateTime(data) => { assert!(data > start); assert!(data < stop); } _ => unreachable!("got {:?}, should have been datetime", result), } } } #[allow(dead_code)] #[derive(Clone, Debug)] pub struct Operation { field_name: String, operation: Operand, value: Field, } #[allow(dead_code)] impl Operation { pub fn new(name: String, op: Operand, value: F) -> Self where F: Into, { Self { field_name: name, operation: op, value: value.into(), } } fn which_field(&self) -> String { self.field_name.clone() } pub fn validate(&self, field: &Field) -> bool { self.operation.validate(field, &self.value) } } #[allow(dead_code)] #[derive(Clone, Debug)] enum QueryType { Query(Query), Oids(HashSet), } impl From for QueryType { fn from(value: Query) -> Self { QueryType::Query(value) } } impl From> for QueryType { fn from(value: HashSet) -> Self { QueryType::Oids(value) } } #[derive(Clone, Debug)] pub struct Query { data: HashMap, } impl Query { pub fn new() -> Self { Self { data: HashMap::new(), } } pub fn add(&mut self, name: NT, operation: Calculation) where NT: Into, { self.data.insert(name.into(), operation); } #[allow(dead_code)] fn get(&self, name: NT) -> Option where NT: Into, { match self.data.get(&name.into()) { Some(calc) => Some(calc.clone()), None => None, } } #[allow(dead_code)] fn field_ids(&self) -> HashSet<&NameType> { self.data.keys().collect() } pub fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod queries { use super::*; #[test] fn holds_calculation_to_run_query() { let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); let mut bad_data = data.clone(); while bad_data == data { bad_data = Uuid::new_v4(); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(data.clone()).unwrap(); query.add(name.clone(), calc); match query.get(&name) { Some(op) => { let expected: Field = true.into(); let mut holder = op.clone(); holder.add_value(data).unwrap(); assert_eq!(holder.calculate(&Field::None), expected); } None => unreachable!("should have returned a calculation"), } match query.get(&name) { Some(op) => { let expected: Field = false.into(); let mut holder = op.clone(); holder.add_value(bad_data).unwrap(); assert_eq!(holder.calculate(&Field::None), expected); } None => unreachable!("should have returned a calculation"), } } } #[allow(dead_code)] #[derive(Clone, Debug)] pub struct Reply { data: Vec, } #[allow(dead_code)] impl Reply { pub fn new() -> Self { Self { data: Vec::new() } } fn add(&mut self, doc: Document) { self.data.push(doc); } pub fn len(&self) -> usize { self.data.len() } fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod replies { use super::*; #[test] fn is_new_empty() { let reply = Reply::new(); assert_eq!(reply.len(), 0, "should have no records"); } #[test] fn can_add_documents() { let mut reply = Reply::new(); let doc = Document::new(); reply.add(doc.clone()); assert_eq!(reply.len(), 1); reply.add(doc.clone()); assert_eq!(reply.len(), 2); } #[test] fn can_retrieve_documents() { let fieldname = Name::english("field"); let mut doc1 = Document::new(); doc1.add_field(fieldname.clone(), "one"); let mut doc2 = Document::new(); doc2.add_field(fieldname.clone(), "two"); let mut reply = Reply::new(); reply.add(doc1); reply.add(doc2); let mut reply_iter = reply.iter(); let result1 = reply_iter.next().unwrap(); match result1.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "one"), _ => unreachable!("got {:?}: should have been static string", result1), }, _ => unreachable!("got {:?}, should have been value", result1), } let result2 = reply_iter.next().unwrap(); match result2.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "two"), _ => unreachable!("got {:?}: should have been static string", result2), }, _ => unreachable!("got {:?}, should have been value", result2), } match reply_iter.next() { None => {} Some(_) => unreachable!("should be out of data"), } } } #[derive(Clone, Debug)] pub struct InternalRecord { data: HashMap, } impl InternalRecord { pub fn new() -> Self { Self { data: HashMap::new(), } } pub fn insert(&mut self, id: Uuid, data: F) -> Field where F: Into, { match self.data.insert(id, data.into()) { Some(data) => data.clone(), None => Field::None, } } pub fn get(&self, id: &Uuid) -> Option<&Field> { self.data.get(id) } pub fn is_empty(&self) -> bool { self.data.is_empty() } } #[derive(Clone, Debug)] pub struct InternalRecords { data: HashMap, } impl InternalRecords { pub fn new() -> Self { Self { data: HashMap::new(), } } pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } pub fn get(&self, oid: &Oid) -> Option<&InternalRecord> { self.data.get(oid) } pub fn remove(&mut self, oid: &Oid) -> Option { self.data.remove(oid) } pub fn iter(&self) -> impl Iterator { self.data.iter() } pub fn keys(&self) -> impl Iterator { self.data.keys() } fn values(&self) -> impl Iterator { self.data.values() } pub fn contains_key(&self, oid: &Oid) -> bool { self.data.contains_key(oid) } fn len(&self) -> usize { self.data.len() } } #[derive(Clone, Debug)] pub struct Record { names: Names, data: InternalRecord, } impl Record { fn with_data(names: Names, rec: InternalRecord) -> Self { Self { names: names, data: rec, } } pub fn get(&self, field_id: NT) -> Result where NT: Into, { let id = match self.names.get_id(field_id) { Ok(data) => data, Err(err) => return Err(err), }; match self.data.get(&id) { Some(data) => Ok(data.clone()), None => Err(MTTError::FieldMissingData), } } } #[derive(Clone, Debug)] pub struct Records { names: Names, data: InternalRecords, } impl Records { pub fn new(names: Names) -> Self { Self { names: names, data: InternalRecords::new(), } } pub fn with_data(names: Names, records: InternalRecords) -> Self { Self { names: names, data: records, } } pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } pub fn len(&self) -> usize { self.data.len() } pub fn iter(&self) -> impl Iterator { RecordIter::new(self) } pub fn get_internal_records(&self) -> &InternalRecords { &self.data } } struct RecordIter { names: Names, recs: Vec, } impl RecordIter { fn new(records: &Records) -> Self { Self { names: records.names.clone(), recs: records.data.values().cloned().collect(), } } } impl Iterator for RecordIter { type Item = Record; fn next(&mut self) -> Option { match self.recs.pop() { Some(rec) => Some(Record::with_data(self.names.clone(), rec.clone())), None => None, } } } #[derive(Clone, Debug)] pub struct Document { data: HashMap, } impl Document { fn new() -> Self { Self { data: HashMap::new(), } } pub fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.insert(name.into(), field.into()); } fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { match self.data.get(&name.into()) { Some(data) => data, None => &CalcValue::None, } } #[allow(dead_code)] fn get_all(&self) -> Vec<(NameType, Field)> { let mut output = Vec::new(); for (key, value) in self.data.iter() { output.push((key.clone(), value.get(&Field::None))); } output } pub fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod documents { use super::*; #[test] fn can_add_static_string() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::StaticString(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } #[test] fn can_add_uuid() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } } #[derive(Clone, Debug)] pub struct Delete { query: Query, } impl Delete { pub fn new(query: Query) -> Self { Self { query: query.into(), } } pub fn get_query(&self) -> &Query { &self.query } } #[derive(Clone, Debug)] pub struct Update { query: Query, values: Document, } impl Update { pub fn new(query: Query) -> Self { Self { query: query.into(), values: Document::new(), } } pub fn get_query(&self) -> &Query { &self.query } pub fn get_values(&self) -> &Document { &self.values } pub fn get_values_mut(&mut self) -> &mut Document { &mut self.values } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Oid { oid: Uuid, } impl Oid { pub fn new() -> Self { Self { oid: Uuid::new_v4(), } } } #[cfg(test)] mod oids { use super::*; #[test] fn are_oids_random() { let count = 10; let mut holder: Vec = Vec::new(); while holder.len() < count { let result = Oid::new(); assert!(!holder.contains(&result)); holder.push(result); } } } #[allow(dead_code)] #[derive(Clone, Debug)] pub struct MsgEntry { timestamp: DateTime, message: Message, } #[allow(dead_code)] impl MsgEntry { fn new(msg: Message) -> Self { Self { timestamp: Utc::now(), message: msg, } } fn get_timestamp(&self) -> &DateTime { &self.timestamp } fn get_message(&self) -> &Message { &self.message } fn get_message_id(&self) -> &Uuid { self.message.get_message_id() } } #[cfg(test)] mod msg_entries { use super::*; #[test] fn creates_message_entry() { let msg = Message::new(Name::english("holder"), Query::new()); let start = Utc::now(); let entry = MsgEntry::new(msg.clone()); let end = Utc::now(); assert!( entry.get_timestamp() > &start, "timestamp should be between start and end times" ); assert!( entry.get_timestamp() < &end, "timestamp should be between start and end times" ); assert_eq!(entry.get_message_id(), msg.get_message_id()); } } #[allow(dead_code)] #[derive(Clone, Debug)] struct MsgLogs { data: HashMap>, } #[allow(dead_code)] impl MsgLogs { fn new() -> Self { Self { data: HashMap::new(), } } fn add(&mut self, msg: Message) { let entry = MsgEntry::new(msg); let id = entry.get_message_id(); let entries = match self.data.get_mut(id) { Some(data) => data, None => { self.data.insert(id.clone(), Vec::new()); self.data.get_mut(id).unwrap() } }; entries.push(entry); } fn get(&self, msg_id: &Uuid) -> Option<&Vec> { self.data.get(msg_id) } } #[cfg(test)] mod msg_logs { use super::*; #[test] fn can_add_message_to_log() { let mut logs = MsgLogs::new(); let msg = Message::new(Name::english("something"), Query::new()); logs.add(msg.clone()); let result = logs.get(msg.get_message_id()).unwrap(); assert_eq!(result.len(), 1, "should be one entry"); } #[test] fn returns_none_when_no_logs_found() { let logs = MsgLogs::new(); match logs.get(&Uuid::nil()) { Some(data) => unreachable!("got {:?}, should return none", data), None => (), } } #[test] fn stores_messages_with_responses() { let mut logs = MsgLogs::new(); let msg1 = Message::new(Name::english("something"), Query::new()); let msg2 = msg1.response(Records::new(Names::new())); logs.add(msg1.clone()); logs.add(msg2.clone()); let result = logs.get(msg1.get_message_id()).unwrap(); assert_eq!(result.len(), 2, "should be two entry"); let action1: Action = result[0].get_message().get_action().clone().into(); let action2: Action = result[1].get_message().get_action().clone().into(); assert_eq!(action1, Action::Query); assert_eq!(action2, Action::Records); } #[test] fn messages_are_stored_by_ids() { let mut logs = MsgLogs::new(); let msg1 = Message::new(Name::english("something"), Query::new()); let msg2 = Message::new(Name::english("something"), Query::new()); logs.add(msg1.clone()); logs.add(msg2.clone()); let result1 = logs.get(msg1.get_message_id()).unwrap(); let result2 = logs.get(msg2.get_message_id()).unwrap(); assert_eq!(result1.len(), 1, "should be one entry"); assert_eq!(result2.len(), 1, "should be one entry"); assert_eq!(result1[0].get_message_id(), msg1.get_message_id()); assert_eq!(result2[0].get_message_id(), msg2.get_message_id()); } } #[allow(dead_code)] struct MessageLog { data: MsgLogs, queue: Queue, rx: Receiver, } #[allow(dead_code)] impl MessageLog { fn new(queue: Queue, rx: Receiver) -> Self { Self { data: MsgLogs::new(), queue: queue, rx: rx, } } fn start(mut queue: Queue) { let (tx, rx) = channel(); let mut logs = MessageLog::new(queue.clone(), rx); let id = queue.add_sender(tx); let reg_msg = Register::new( id, RegMsg::AddRoute(Path::new(Include::All, Include::All, Include::All)), ); let rmsg = Message::new(NameType::None, reg_msg); queue.send(rmsg.clone()).unwrap(); spawn(move || { logs.listen(); }); } fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); match msg.get_action() { MsgAction::GetLog(id) => match self.data.get(id) { Some(data) => self .queue .send(msg.response(MsgAction::Log(data.clone()))) .unwrap(), None => self .queue .send(msg.response(MsgAction::Log(Vec::new()))) .unwrap(), }, _ => self.data.add(msg), } } } } #[cfg(test)] mod message_logs { use super::*; use crate::support_tests::TIMEOUT; #[test] fn does_log_store_messages() { let doc_name = Name::english("unimportant"); let mut queue = Queue::new(); MessageLog::start(queue.clone()); let (tx, rx) = channel(); let id = queue.add_sender(tx); let reg_msg = Register::new(id, RegMsg::AddDocName(vec![doc_name.clone()])); let rmsg = Message::new(NameType::None, reg_msg); queue.send(rmsg.clone()).unwrap(); let name_result = rx.recv().unwrap(); match name_result.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data, RegMsg::Error(err) => unreachable!("got {:?}, should have gotten data", err), _ => unreachable!("should only return a name id or an error"), }, _ => unreachable!("should only return a name id or an error"), }; let request = Register::new( id.clone(), RegMsg::AddRoute(Path::new( Include::All, Include::All, Include::Just(Action::Log), )), ); queue.send(Message::new(NameType::None, request)).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); let msg = Message::new(doc_name.clone(), Query::new()); queue.send(msg.clone()).unwrap(); let log_msg = Message::new(NameType::None, msg.get_message_id()); queue.send(log_msg.clone()).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), log_msg.get_message_id()); match result.get_action() { MsgAction::Log(output) => assert_eq!(output.len(), 1), _ => unreachable!("got {:?}, should have been log", result.get_action()), } } } pub struct Session { doc_name: Name, } impl Session { pub fn new() -> Self { Self { doc_name: Name::english("session"), } } #[allow(dead_code)] pub fn get_document_name(&self) -> &Name { &self.doc_name } pub fn create(&self, mut queue: Queue) { let mut docdef = DocDef::new(self.doc_name.clone()); let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime).unwrap(); calc.add_value(Duration::from_hours(1)).unwrap(); let name_id = Name::english("id"); docdef.add_field(name_id.clone(), FieldType::Uuid); docdef.set_default(&name_id, FieldType::Uuid).unwrap(); docdef.add_index(&name_id, IndexType::Unique).unwrap(); let name_expire = Name::english("expire"); docdef.add_field(name_expire.clone(), FieldType::DateTime); docdef.set_default(&name_expire, calc.clone()).unwrap(); let mut update = Update::new(Query::new()); update .get_values_mut() .add_field(name_expire.clone(), calc.clone()); let path = Path::new( Include::All, Include::Just(self.doc_name.clone().into()), Include::Just(Action::OnQuery), ); let query_action = DocFuncType::ExistingQuery(update.into()); docdef.add_route(path, query_action); let mut delete_qry = Query::new(); let mut delete_calc = Calculation::new(Operand::LessThan); delete_calc.add_value(FieldType::DateTime).unwrap(); delete_calc .add_value(CalcValue::Existing(FieldType::DateTime)) .unwrap(); delete_qry.add(name_expire.clone(), delete_calc); let delete = Delete::new(delete_qry); let clock_path = Path::new( Include::All, Include::Just(Name::english("clock").into()), Include::Just(Action::OnUpdate), ); let delete_func = DocFuncType::Trigger(delete.into()); docdef.add_route(clock_path, delete_func); let (tx, rx) = channel(); let sender_id = queue.add_sender(tx); let msg = Message::new(NameType::None, docdef.clone()); let path = Path::new( Include::Just(msg.get_message_id().clone()), Include::All, Include::All, ); let reg_msg = Register::new(sender_id.clone(), RegMsg::AddRoute(path)); queue.send(msg.forward(NameType::None, reg_msg)).unwrap(); rx.recv().unwrap(); // Wait for completion. queue.send(msg).unwrap(); rx.recv().unwrap(); // Wait for completion. rx.recv().unwrap(); // Wait for completion. queue.remove_sender(&sender_id); } } #[cfg(test)] mod sessions { use super::*; use crate::{document::create::CreateDoc, support_tests::TIMEOUT}; use std::{sync::mpsc::RecvTimeoutError, thread::sleep}; struct Setup { queue: Queue, rx: Receiver, sender_id: Uuid, } impl Setup { fn new() -> Self { let (tx, rx) = channel(); let mut queue = Queue::new(); let id = queue.add_sender(tx); CreateDoc::start(queue.clone()); crate::document::clock::Clock::start(queue.clone()); Self { queue: queue, rx: rx, sender_id: id, } } fn get_sender_id(&self) -> Uuid { self.sender_id.clone() } fn get_queue(&self) -> Queue { self.queue.clone() } fn recv(&self) -> Result { self.rx.recv_timeout(TIMEOUT) } fn register(&self) { let session = Session::new(); let paths = [ Path::new( Include::All, Include::Just(session.get_document_name().into()), Include::Just(Action::Error), ), Path::new( Include::All, Include::Just(session.get_document_name().into()), Include::Just(Action::Records), ), ]; for path in paths.iter() { let reg_msg = Register::new(self.sender_id.clone(), RegMsg::AddRoute(path.clone())); self.queue .send(Message::new(NameType::None, reg_msg)) .unwrap(); self.rx.recv().unwrap(); // Wait for completion. } } } #[test] fn creates_the_session_table() { let setup = Setup::new(); let queue = setup.get_queue(); let session = Session::new(); assert_eq!(session.get_document_name(), &Name::english("session")); session.create(queue.clone()); let path = Path::new( Include::All, Include::Just(session.get_document_name().into()), Include::All, ); let reg_msg = Register::new(setup.get_sender_id(), RegMsg::AddRoute(path)); queue.send(Message::new(NameType::None, reg_msg)).unwrap(); setup.recv().unwrap(); } #[test] fn session_ids_are_unique() { let setup = Setup::new(); let queue = setup.get_queue(); let session = Session::new(); session.create(queue.clone()); setup.register(); let count = 10; let msg = Message::new(session.get_document_name(), Addition::new()); let mut ids: Vec = Vec::new(); for _ in 0..count { queue.send(msg.clone()).unwrap(); 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("id")).unwrap(); let id = match holder { Field::Uuid(data) => data, _ => unreachable!("got {:?} should have been uuid", holder), }; assert!(!ids.contains(&id), "{} duplicated in {:?}", id, ids); ids.push(id); } _ => unreachable!("got {:?}, should have gotten records", action), } } } #[test] fn expire_default_is_an_hour_from_now() { let setup = Setup::new(); let queue = setup.get_queue(); let session = Session::new(); session.create(queue.clone()); setup.register(); let msg = Message::new(session.get_document_name(), Addition::new()); let start_time = Utc::now() + Duration::from_hours(1); queue.send(msg).unwrap(); 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] fn session_ids_error_when_not_unique() { let setup = Setup::new(); let queue = setup.get_queue(); let session = Session::new(); session.create(queue.clone()); setup.register(); let id = Uuid::new_v4(); let mut addition = Addition::new(); addition.add_field(Name::english("id"), id); queue .send(Message::new(session.get_document_name(), addition.clone())) .unwrap(); setup.recv().unwrap(); queue .send(Message::new(session.get_document_name(), addition)) .unwrap(); let result = setup.recv().unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate => {} _ => 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 queue = setup.get_queue(); let session = Session::new(); session.create(queue.clone()); setup.register(); let id = Uuid::new_v4(); let timestamp = Utc::now(); let mut addition = Addition::new(); addition.add_field(Name::english("id"), id.clone()); addition.add_field(Name::english("expire"), timestamp); queue .send(Message::new(session.get_document_name(), addition.clone())) .unwrap(); setup.recv().unwrap(); let mut query = Query::new(); 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()); queue .send(Message::new(session.get_document_name(), query.clone())) .unwrap(); setup.recv().unwrap(); let start_time = Utc::now() + Duration::from_secs(3600); queue .send(Message::new(session.get_document_name(), query.clone())) .unwrap(); let result = setup.recv().unwrap(); let end_time = Utc::now() + Duration::from_hours(1); sleep(TIMEOUT); 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] fn clock_removes_expired_sessions() { let setup = Setup::new(); let queue = setup.get_queue(); let session = Session::new(); session.create(queue.clone()); setup.register(); let id1 = Uuid::new_v4(); let id2 = Uuid::new_v4(); let duration = Duration::from_secs(240); let expire1 = Utc::now() + duration; println!("{:?}", expire1); println!("{:?}", Utc::now()); let expire2 = Utc::now() - duration; println!("{:?}", expire2); let mut addition1 = Addition::new(); addition1.add_field(Name::english("id"), id1.clone()); addition1.add_field(Name::english("expire"), expire1); let mut addition2 = Addition::new(); addition2.add_field(Name::english("id"), id2); addition2.add_field(Name::english("expire"), expire2); queue .send(Message::new(session.get_document_name(), addition1)) .unwrap(); queue .send(Message::new(session.get_document_name(), addition2)) .unwrap(); setup.recv().unwrap(); setup.recv().unwrap(); queue .send(Message::new( Name::english("clock"), MsgAction::OnUpdate(Records::new(Names::new())), )) .unwrap(); sleep(TIMEOUT); queue .send(Message::new(session.get_document_name(), Query::new())) .unwrap(); 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), } } }