diff --git a/src/action.rs b/src/action.rs index d8c2268..276f418 100644 --- a/src/action.rs +++ b/src/action.rs @@ -10,7 +10,7 @@ mod show; mod update; mod user; -pub use crate::document::{DocDef, Field, FieldType, IndexType, Record, Records}; +pub use crate::document::{DocDef, DocFuncType, Field, FieldType, IndexType, Record, Records}; pub use action_type::Action; pub use addition::Addition; pub use calculation::{CalcValue, Calculation, Operand}; diff --git a/src/document.rs b/src/document.rs index f99ab97..baec32e 100644 --- a/src/document.rs +++ b/src/document.rs @@ -9,7 +9,7 @@ use record::{InternalRecord, InternalRecords, Oid}; pub use clock::Clock; pub use create::{CreateDoc, IndexType}; -pub use definition::DocDef; +pub use definition::{DocDef, DocFuncType}; pub use field::{Field, FieldType}; pub use record::{Record, Records}; pub use session::Session; diff --git a/src/document/create.rs b/src/document/create.rs index 46dbca8..bcd7fcc 100644 --- a/src/document/create.rs +++ b/src/document/create.rs @@ -782,1496 +782,3 @@ impl DocumentFile { .send(msg.forward(self.name_id.clone(), action.clone())); } } - -#[cfg(test)] -mod document_files { - use super::*; - use crate::{ - action::{Addition, Delete, FieldType, Operand, Show}, - document::Clock, - name::{Name, Names}, - support_tests::TIMEOUT, - }; - use chrono::Utc; - use std::{sync::mpsc::RecvTimeoutError, thread::sleep, time::Duration}; - - fn standard_paths() -> Vec { - [ - Path::new(Include::All, Include::All, Include::Just(Action::Records)), - Path::new(Include::All, Include::All, Include::Just(Action::Reply)), - Path::new(Include::All, Include::All, Include::Just(Action::Error)), - ] - .to_vec() - } - - struct TestDocument { - docdef: DocDef, - queue: Queue, - sender_id: Uuid, - rx: Receiver, - } - - impl TestDocument { - fn new(field_types: Vec) -> Self { - let doc_name = Name::english(Uuid::new_v4().to_string().as_str()); - let mut docdef = DocDef::new(doc_name.clone()); - let mut count = 0; - for field_type in field_types.iter() { - docdef.add_field( - Name::english(format!("field{}", count).as_str()), - field_type.clone(), - ); - count += 1; - } - let (tx, rx) = channel(); - let mut queue = Queue::new(); - let id = queue.add_sender(tx); - Self { - docdef: docdef, - queue: queue, - sender_id: id, - rx: rx, - } - } - - fn doc_name(&self) -> Name { - self.docdef.get_document_names()[0].clone() - } - - fn get_docdef(&self) -> &DocDef { - &self.docdef - } - - fn get_docdef_mut(&mut self) -> &mut DocDef { - &mut self.docdef - } - - fn get_queue(&mut self) -> Queue { - self.queue.clone() - } - - fn get_receiver(&self) -> &Receiver { - &self.rx - } - - fn get_sender_id(&self) -> Uuid { - self.sender_id.clone() - } - - fn send(&self, action: A) - where - A: Into, - { - let msg = Message::new(action); - self.queue.send(msg); - } - - fn start(&mut self, routes: Vec) { - let msg = Message::new(self.docdef.clone()); - DocumentFile::start(self.queue.clone(), msg); - for route in routes.iter() { - let request = - Register::new(self.sender_id.clone(), RegMsg::AddRoute(route.clone())); - let add_route = Message::new(request); - self.queue.send(add_route); - self.rx.recv().unwrap(); - } - } - - fn populate(&self, data: Vec) { - let mut add = Addition::new(self.doc_name()); - let mut count = 0; - for item in data.iter() { - add.add_field( - Name::english(format!("field{}", count).as_str()), - item.clone(), - ); - count += 1; - } - self.send(add); - match self.rx.recv_timeout(TIMEOUT) { - Ok(_) => {} // eats the addition response. - Err(err) => match err { - RecvTimeoutError::Timeout => {} - _ => unreachable!("got {}, should have been ok or time out", err), - }, - } - } - } - - impl From for TestDocument { - fn from(value: DocDef) -> Self { - let (tx, rx) = channel(); - let mut queue = Queue::new(); - let id = queue.add_sender(tx); - Self { - docdef: value, - queue: queue, - sender_id: id, - rx: rx, - } - } - } - - /* - #[test] - fn query_sends_on_query_message() { - let count = 5; - let mut data: HashSet = HashSet::new(); - while data.len() < count { - let field: Field = Uuid::new_v4().into(); - data.insert(field); - } - let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - let queue = test_doc.get_queue(); - let routes = [Path::new( - Include::All, - Include::All, - Include::Just(Action::OnQuery), - )] - .to_vec(); - test_doc.start(routes); - for item in data.iter() { - test_doc.populate([item.clone()].to_vec()); - } - let msg = Message::new(Query::new(doc_name.clone())); - queue.send(msg.clone()); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!( - result.get_message_id(), - msg.get_message_id(), - "message ids should match" - ); - match result.get_action() { - MsgAction::OnQuery(output) => { - assert_eq!( - output.len(), - count, - "wrong number of entries: got {:?}", - output - ); - for rec in output.iter() { - assert!(data.contains(&rec.get(Name::english("field0")).unwrap())); - } - } - _ => unreachable!("should never get here"), - } - } - - #[test] - fn send_on_addition_message() { - let data: Field = Uuid::new_v4().into(); - let field_name = Name::english("field0"); - let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - let queue = test_doc.get_queue(); - let routes = vec![Path::new( - Include::All, - Include::All, - Include::Just(Action::OnAddition), - )]; - test_doc.start(routes); - let mut add = Addition::new(doc_name.clone()); - add.add_field(field_name.clone(), data.clone()); - let msg = Message::new(add); - queue.send(msg.clone()); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!( - result.get_message_id(), - msg.get_message_id(), - "message ids should match" - ); - match result.get_action() { - MsgAction::OnAddition(output) => { - assert_eq!(output.len(), 1, "wrong number of entries: got {:?}", output); - for rec in output.iter() { - assert_eq!(rec.get(Name::english("field0")).unwrap(), data); - } - } - _ => unreachable!("should never get here"), - } - } - - #[test] - fn sends_on_delete_message() { - let count = 5; - let mut data: HashSet = HashSet::new(); - while data.len() < count { - let field: Field = Uuid::new_v4().into(); - data.insert(field); - } - let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - let queue = test_doc.get_queue(); - let routes = [Path::new( - Include::All, - Include::All, - Include::Just(Action::OnDelete), - )] - .to_vec(); - test_doc.start(routes); - for item in data.iter() { - test_doc.populate([item.clone()].to_vec()); - } - let msg = Message::new(Delete::new(doc_name.clone())); - queue.send(msg.clone()); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!( - result.get_message_id(), - msg.get_message_id(), - "message ids should match" - ); - match result.get_action() { - MsgAction::OnDelete(output) => { - assert_eq!( - output.len(), - count, - "wrong number of entries: got {:?}", - output - ); - for rec in output.iter() { - assert!(data.contains(&rec.get(Name::english("field0")).unwrap())); - } - } - _ => unreachable!("should never get here"), - } - } - - #[test] - fn sends_on_update_message() { - let count = 5; - let field_name = Name::english("field0"); - let mut data: HashSet = HashSet::new(); - while data.len() < count { - let field: Field = Uuid::new_v4().into(); - data.insert(field); - } - let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - let queue = test_doc.get_queue(); - let routes = [Path::new( - Include::All, - Include::All, - Include::Just(Action::OnUpdate), - )] - .to_vec(); - test_doc.start(routes); - for item in data.iter() { - test_doc.populate([item.clone()].to_vec()); - } - let mut update = Update::new(doc_name.clone()); - update - .get_values_mut() - .add_field(field_name.clone(), Uuid::nil()); - let msg = Message::new(update); - queue.send(msg.clone()); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!( - result.get_message_id(), - msg.get_message_id(), - "message ids should match" - ); - match result.get_action() { - MsgAction::OnUpdate(output) => { - assert_eq!( - output.len(), - count, - "wrong number of entries: got {:?}", - output - ); - for rec in output.iter() { - assert_eq!(rec.get(&field_name).unwrap(), Uuid::nil().into()); - } - } - _ => unreachable!("should never get here"), - } - } - - #[test] - fn can_document_be_added() { - let doc_name = Name::english("document"); - let mut docdef = DocDef::new(doc_name.clone()); - let name = Name::english("field"); - let data = Uuid::new_v4(); - docdef.add_field(name.clone(), FieldType::Uuid); - let mut test_doc: TestDocument = docdef.clone().into(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let mut new_doc = Addition::new(doc_name.clone()); - new_doc.add_field(name.clone(), data.clone()); - let testing = |msg: Message| { - queue.send(msg.clone()); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!(result.get_message_id(), msg.get_message_id()); - match result.get_action() { - MsgAction::Records(output) => { - assert_eq!(output.len(), 1); - for rec in output.iter() { - let holder = rec.get(&name).unwrap(); - match holder { - Field::Uuid(field_data) => assert_eq!(field_data, data), - _ => unreachable!("got {:?}, should have been uuid", holder), - } - } - } - _ => unreachable!( - "\n\ngot {:?}\n\nfor {:?}\n\nshould have been records", - result, msg - ), - } - }; - testing(Message::new(new_doc)); - testing(Message::new(Query::new(doc_name.clone()))); - } - - #[test] - fn can_add_multiple_documents() { - let doc_name = Name::english("multiple"); - let mut docdef = DocDef::new(doc_name.clone()); - let name = Name::english("count"); - docdef.add_field(name.clone(), FieldType::Integer); - let mut test_doc: TestDocument = docdef.clone().into(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let count = 5; - for i in 0..count { - let mut new_doc = Addition::new(doc_name.clone()); - new_doc.add_field(name.clone(), i); - queue.send(Message::new(new_doc)); - test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - } - queue.send(Message::new(Query::new(doc_name.clone()))); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - let mut entries: HashSet = (0..count).collect(); - match action { - MsgAction::Records(output) => { - let entry_count: usize = count.try_into().unwrap(); - assert_eq!( - output.len(), - entry_count, - "should have the same number of entries" - ); - for record in output.iter() { - let holder = record.get(&name).unwrap(); - let data = match holder { - Field::Integer(item) => item.clone(), - _ => unreachable!("got {:?}, should have been integer", holder), - }; - assert!( - entries.contains(&data), - "did not find {:?} in {:?}", - data, - entries - ); - entries.remove(&data); - } - } - _ => unreachable!("\n\ngot {:?}\n\nshould have been records", action), - } - assert!(entries.is_empty(), "did not use {:?}", entries); - } - - #[test] - fn errors_on_wrong_field_name() { - let mut test_doc = TestDocument::new(Vec::new()); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let name = Name::english("bad"); - let mut addition = Addition::new(test_doc.doc_name()); - addition.add_field(name.clone(), "doesn't matter"); - queue.send(Message::new(addition)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - match result.get_action() { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::NameNotFound(_) => {} - _ => unreachable!("got {:?}: should have been document field not found.", err), - }, - _ => unreachable!("got {:?}: should have been an error", result.get_action()), - } - } - - #[test] - #[ignore = "move to lib"] - fn errors_on_wrong_field_type() { - let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let mut addition = Addition::new(test_doc.doc_name()); - addition.add_field(Name::english("field0"), "string"); - queue.send(Message::new(addition)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - match result.get_action() { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::FieldInvalidType => {} - _ => unreachable!( - "got {:?}: should have been document field data mismatch.", - err - ), - }, - _ => unreachable!("got {:?}: should have been an error", result.get_action()), - } - } - - #[test] - fn errors_on_missing_fields() { - let mut test_doc = TestDocument::new([FieldType::Integer, FieldType::Integer].to_vec()); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let mut addition = Addition::new(test_doc.doc_name()); - addition.add_field(Name::english("field0"), 1); - queue.send(Message::new(addition)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - match result.get_action() { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::FieldInvalidNone => {} - _ => unreachable!("got {:?}: should have been document field missing", err), - }, - _ => unreachable!("got {:?}: should have been an error", result.get_action()), - } - } - - #[test] - fn does_query_return_related_entries() { - let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let count = 5; - let expected = 3; - for i in 0..count { - test_doc.populate([i.into()].to_vec()); - } - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(expected.clone()).unwrap(); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - let mut query = Query::new(doc_name); - query.add(Name::english("field0"), calc); - queue.send(Message::new(query)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!( - data.len(), - 1, - "should return one entry containing {:?} got:\n{:?}", - expected, - action - ); - for doc in data.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), expected.into()); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn does_query_work_with_greater_than() { - let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - test_doc.populate([1.into()].to_vec()); - test_doc.populate([2.into()].to_vec()); - let mut calc = Calculation::new(Operand::GreaterThan); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(1).unwrap(); - let mut query = Query::new(doc_name); - query.add(Name::english("field0"), calc); - queue.send(Message::new(query)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!( - data.len(), - 1, - "should return one entry containing 2 got:\n{:?}", - action - ); - for doc in data.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), 2.into()); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn gets_all_documents_in_query() { - let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); - let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let data = 1; - let count = 5; - for i in 0..count { - let holder: i128 = (i + count).try_into().unwrap(); - test_doc.populate([holder.into()].to_vec()); - test_doc.populate([data.into()].to_vec()); - } - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(data.clone()).unwrap(); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - let mut query = Query::new(doc_name); - query.add(Name::english("field0"), calc); - queue.send(Message::new(query)); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!( - docs.len(), - count, - "should return one entry containing {:?} got:\n{:?}", - data, - action - ); - for doc in docs.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), data.into()); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn query_should_work_with_multiple_fields() { - let mut doc = - TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let values = [ - ["a".into(), "a".into()].to_vec(), - ["a".into(), "b".into()].to_vec(), - ["b".into(), "a".into()].to_vec(), - ["b".into(), "b".into()].to_vec(), - ]; - for value in values.iter() { - doc.populate(value.clone()); - } - let mut query = Query::new(doc.doc_name()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("a").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field0"), calc); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("b").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field1"), calc); - doc.send(query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - let afield: Field = "a".into(); - let bfield: Field = "b".into(); - assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); - for doc in data.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); - assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn query_should_work_with_multiple_inexed_fields() { - let mut doc = - TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); - let docdef = doc.get_docdef_mut(); - docdef - .add_index(&Name::english("field0"), IndexType::Index) - .unwrap(); - docdef - .add_index(&Name::english("field1"), IndexType::Index) - .unwrap(); - doc.start(standard_paths()); - let values = [ - ["a".into(), "a".into()].to_vec(), - ["a".into(), "b".into()].to_vec(), - ["b".into(), "a".into()].to_vec(), - ["b".into(), "b".into()].to_vec(), - ]; - for value in values.iter() { - doc.populate(value.clone()); - } - let mut query = Query::new(doc.doc_name()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("a").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field0"), calc); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("b").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field1"), calc); - doc.send(query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - let afield: Field = "a".into(); - let bfield: Field = "b".into(); - assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); - for doc in data.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); - assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn query_should_work_with_mixed_inexed_fields() { - let mut doc = - TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); - let docdef = doc.get_docdef_mut(); - docdef - .add_index(&Name::english("field0"), IndexType::Index) - .unwrap(); - doc.start(standard_paths()); - let values = [ - ["a".into(), "a".into()].to_vec(), - ["a".into(), "b".into()].to_vec(), - ["b".into(), "a".into()].to_vec(), - ["b".into(), "b".into()].to_vec(), - ]; - for value in values.iter() { - doc.populate(value.clone()); - } - let mut query = Query::new(doc.doc_name()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("a").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field0"), calc); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("b").unwrap(); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - query.add(Name::english("field1"), calc); - doc.send(query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - let afield: Field = "a".into(); - let bfield: Field = "b".into(); - assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); - for doc in data.iter() { - assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); - assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); - } - } - _ => unreachable!("got {:?}: should have been a reply", action), - } - } - - #[test] - fn errors_on_bad_field_name() { - let mut doc = TestDocument::new(Vec::new()); - doc.start(standard_paths()); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - let queue = doc.get_queue(); - let rx = doc.get_receiver(); - let field_name = Name::english("wrong"); - let mut query = Query::new(doc.doc_name()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("something").unwrap(); - query.add(field_name.clone(), calc); - let msg = Message::new(query); - queue.send(msg); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(data) => match data.get_error_ids().back().unwrap() { - ErrorID::NameNotFound(_) => {} - _ => unreachable!("got {:?}: should been field not found", data), - }, - _ => unreachable!("got {:?}: should have been a error", action), - } - } - - #[test] - #[ignore = "moving"] - fn errors_on_bad_field_type_with_index() { - let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); - doc.get_docdef_mut() - .add_index(&Name::english("field0"), IndexType::Index) - .unwrap(); - doc.start(standard_paths()); - doc.populate([Uuid::nil().into()].to_vec()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value("notUUID").unwrap(); - let mut query = Query::new(doc.doc_name()); - query.add(Name::english("field0"), calc); - doc.send(query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(data) => match data.get_error_ids().back().unwrap() { - ErrorID::FieldInvalidType => {} - _ => unreachable!("got {:?}: should been invalid field type", data), - }, - _ => unreachable!("got {:?}: should have been a error", action), - } - } - - #[test] - fn can_use_default_values() { - let doc_name = Name::english("default"); - let mut docdef = DocDef::new(doc_name.clone()); - let field_name = Name::english("holder"); - docdef.add_field(field_name.clone(), FieldType::StaticString); - docdef - .set_default(&field_name, FieldType::StaticString) - .unwrap(); - let mut test_doc: TestDocument = docdef.into(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let rx = test_doc.get_receiver(); - let new_doc = Addition::new(doc_name.clone()); - let msg = Message::new(new_doc); - queue.send(msg); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1); - for doc in docs.iter() { - let expected: Field = "".into(); - assert_eq!(doc.get(&field_name).unwrap(), expected); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - } - - #[test] - fn can_a_default_value_be_set() { - let doc_name = Name::english("assigned"); - let mut docdef = DocDef::new(doc_name.clone()); - let field_name = Name::english("id"); - docdef.add_field(field_name.clone(), FieldType::Uuid); - docdef.set_default(&field_name, Uuid::nil()).unwrap(); - let mut test_doc: TestDocument = docdef.into(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let rx = test_doc.get_receiver(); - let new_doc = Addition::new(doc_name.clone()); - let msg = Message::new(new_doc); - queue.send(msg); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1); - for doc in docs.iter() { - let expected: Field = Uuid::nil().into(); - assert_eq!(doc.get(&field_name).unwrap(), expected); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - } - - #[test] - fn can_default_values_be_overridden() { - let doc_name = Name::english("assigned"); - let mut docdef = DocDef::new(doc_name.clone()); - let field_name = Name::english("id"); - docdef.add_field(field_name.clone(), FieldType::Uuid); - docdef.set_default(&field_name, FieldType::Uuid).unwrap(); - let mut test_doc: TestDocument = docdef.into(); - test_doc.start(standard_paths()); - let queue = test_doc.get_queue(); - let rx = test_doc.get_receiver(); - let mut new_doc = Addition::new(doc_name.clone()); - new_doc.add_field(&field_name, Uuid::nil()); - let msg = Message::new(new_doc); - queue.send(msg); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1); - for doc in docs.iter() { - let expected: Field = Uuid::nil().into(); - assert_eq!(doc.get(&field_name).unwrap(), expected); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - } - - #[test] - fn empty_update_query_results_in_zero_changes() { - let count = 5; - let mut ids: HashSet = HashSet::new(); - while ids.len() < count { - ids.insert(Uuid::new_v4()); - } - let id = ids.iter().last().unwrap().clone(); - ids.remove(&id); - let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); - doc.start(standard_paths()); - for id in ids.iter() { - doc.populate([id.clone().into()].to_vec()); - } - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Uuid)) - .unwrap(); - calc.add_value(Uuid::nil()).unwrap(); - let mut update = Update::new(doc.doc_name()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update - .get_values_mut() - .add_field(Name::english("field0"), Uuid::nil()); - doc.send(update); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => assert_eq!(docs.len(), 0), - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - } - - #[test] - fn changes_information_requested() { - let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - let old = "old"; - let new = "new"; - let id = Uuid::new_v4(); - doc.populate([id.into(), old.into()].to_vec()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Uuid)) - .unwrap(); - calc.add_value(id.clone()).unwrap(); - let mut update = Update::new(doc_name.clone()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update - .get_values_mut() - .add_field(Name::english("field1"), new); - let mut testing = |msg: Message| { - doc.get_queue().send(msg.clone()); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - assert_eq!(result.get_message_id(), msg.get_message_id()); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1, "for {:?}, should have one entry", msg); - for doc in docs.iter() { - assert_eq!(doc.get(Name::english("field0")).unwrap(), id.into()); - assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - }; - testing(Message::new(update)); - testing(Message::new(Query::new(doc_name.clone()))); - } - - #[test] - fn changes_only_the_queried() { - let mut doc = TestDocument::new([FieldType::Integer, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - let old = "old"; - let new = "new"; - let count = 5; - let field_count = count.clone().try_into().unwrap(); - let picked = 3; - for i in 0..field_count { - doc.populate([i.into(), old.into()].to_vec()); - } - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(picked.clone()).unwrap(); - let mut update = Update::new(doc_name.clone()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update - .get_values_mut() - .add_field(Name::english("field1"), new); - doc.get_queue().send(Message::new(update)); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1, "should have one entry"); - for doc in docs.iter() { - assert_eq!(doc.get(Name::english("field0")).unwrap(), picked.into()); - assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - doc.get_queue() - .send(Message::new(Query::new(doc_name.clone()))); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), count, "should have one entry"); - for doc in docs.iter() { - if doc.get(Name::english("field0")).unwrap() == picked.into() { - assert_eq!( - doc.get(Name::english("field1")).unwrap(), - new.into(), - "{:?}", - docs - ); - } else { - assert_eq!( - doc.get(Name::english("field1")).unwrap(), - old.into(), - "{:?}", - docs - ); - } - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - } - - #[test] - fn can_handle_multiple_updates() { - let mut doc = TestDocument::new([FieldType::Integer, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - let old = "old"; - let new = "new"; - let count = 5; - let picked = 3; - for _ in 0..count { - doc.populate([picked.into(), old.into()].to_vec()); - } - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(picked.clone()).unwrap(); - let mut update = Update::new(doc_name.clone()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update - .get_values_mut() - .add_field(Name::english("field1"), new); - let mut testing = |msg: Message| { - doc.get_queue().send(msg); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), count, "should have one entry"); - for doc in docs.iter() { - assert_eq!(doc.get(Name::english("field0")).unwrap(), picked.into()); - assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - }; - testing(Message::new(update)); - testing(Message::new(Query::new(doc_name.clone()))); - } - - #[test] - fn update_errors_on_bad_field_name() { - let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let id = Uuid::new_v4(); - let old = "old"; - let new = "new"; - let bad_name = Name::english("wrong"); - doc.populate([id.into(), old.into()].to_vec()); - let mut update = Update::new(doc.doc_name()); - update.get_values_mut().add_field(bad_name.clone(), new); - doc.send(update); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::NameNotFound(_) => {} - _ => unreachable!("got {:?}: should have gotten an missing field", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - } - - #[test] - #[ignore = "move to lib"] - fn update_errors_on_bad_field_type() { - let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); - doc.start(standard_paths()); - let id = Uuid::new_v4(); - let old = "old"; - let new = Uuid::nil(); - doc.populate([id.into(), old.into()].to_vec()); - - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Uuid)) - .unwrap(); - calc.add_value(id.clone()).unwrap(); - let mut update = Update::new(doc.doc_name()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update - .get_values_mut() - .add_field(Name::english("field1"), new); - doc.send(update); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::FieldInvalidType => {} - _ => unreachable!("got {:?}: should have gotten incorrect file type", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - } - - #[test] - fn does_update_maintain_unique_fields() { - let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); - test_doc - .get_docdef_mut() - .add_index(&Name::english("field0"), IndexType::Unique) - .unwrap(); - test_doc.start(standard_paths()); - let fname = Name::english("field0"); - let old = 3; - let new = 5; - test_doc.populate([old.into()].to_vec()); - - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(old.clone()).unwrap(); - let mut update = Update::new(test_doc.doc_name()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update.get_values_mut().add_field(&fname, new); - test_doc.send(update); - test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let mut should_clear = Addition::new(test_doc.doc_name()); - should_clear.add_field(fname.clone(), old); - let mut should_error = Addition::new(test_doc.doc_name()); - should_error.add_field(fname.clone(), new); - test_doc.send(should_clear); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1, "should have one entry"); - for doc in docs.iter() { - assert_eq!(doc.get(&fname).unwrap(), old.into()); - } - } - _ => unreachable!("got {:?}: should have gotten records", action), - } - test_doc.send(should_error); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::IndexEntryAlreadyExists(_) => {} - _ => unreachable!("got {:?}: should have gotten incorrect file type", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - } - - #[test] - fn unique_value_remains_available_if_failure_occurs() { - let f0name = Name::english("field0"); - let f1name = Name::english("field1"); - let mut test_doc = TestDocument::new([FieldType::Uuid, FieldType::Uuid].to_vec()); - test_doc - .get_docdef_mut() - .add_index(&f0name, IndexType::Unique) - .unwrap(); - test_doc.start(standard_paths()); - let f0data = Uuid::new_v4(); - let f1bad_data = "NotUuid"; - let f1good_data = Uuid::nil(); - let mut bad_addition = Addition::new(test_doc.doc_name()); - bad_addition.add_field(&f0name, f0data.clone()); - bad_addition.add_field(&f1name, f1bad_data); - let mut good_addition = Addition::new(test_doc.doc_name()); - good_addition.add_field(&f0name, f0data.clone()); - good_addition.add_field(&f1name, f1good_data.clone()); - test_doc.send(bad_addition); - test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - test_doc.send(good_addition); - let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(docs) => { - assert_eq!(docs.len(), 1, "should have one entry"); - for doc in docs.iter() { - assert_eq!(doc.get(&f0name).unwrap(), f0data.into()); - assert_eq!(doc.get(&f1name).unwrap(), f1good_data.into()); - } - } - _ => unreachable!("got {:?}: should have gotten records", action), - } - } - - #[test] - fn updating_unique_updates_index_entries() { - let fname = Name::english("field0"); - let mut doc = TestDocument::new([FieldType::StaticString].to_vec()); - doc.get_docdef_mut() - .add_index(&fname, IndexType::Unique) - .unwrap(); - doc.start(standard_paths()); - let old = "old"; - let new = "new"; - let fold: Field = old.into(); - doc.populate([old.into()].to_vec()); - - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - calc.add_value(old).unwrap(); - let mut update = Update::new(doc.doc_name()); - let query = update.get_query_mut(); - query.add(Name::english("field0"), calc); - update.get_values_mut().add_field(fname.clone(), new); - doc.send(update); - doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let mut old_addition = Addition::new(doc.doc_name()); - old_addition.add_field(&fname, old); - doc.send(old_addition); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), 1); - for doc in data.iter() { - assert_eq!(doc.get(&fname).unwrap(), fold); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - let mut new_addition = Addition::new(doc.doc_name()); - new_addition.add_field(fname.clone(), new); - doc.send(new_addition); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::IndexEntryAlreadyExists(_) => {} - _ => unreachable!("got {:?}: should have gotten an missing field", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - } - - #[test] - fn update_does_not_override_unique_index() { - let f0name = Name::english("field0"); - let f1name = Name::english("field1"); - let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); - doc.get_docdef_mut() - .add_index(&f0name, IndexType::Unique) - .unwrap(); - doc.start(standard_paths()); - let count = 5; - let data = "data"; - let mut ids: HashSet = HashSet::new(); - while ids.len() < count { - ids.insert(Uuid::new_v4()); - } - let holder = ids.iter().last().unwrap().clone(); - ids.remove(&holder); - for id in ids.iter() { - doc.populate([id.clone().into(), data.into()].to_vec()); - } - - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::StaticString)) - .unwrap(); - calc.add_value(data).unwrap(); - let mut update = Update::new(doc.doc_name()); - let query = update.get_query_mut(); - query.add(&f1name, calc); - update.get_values_mut().add_field(&f0name, holder.clone()); - doc.send(update); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::IndexEntryAlreadyExists(_) => {} - _ => unreachable!("got {:?}: should have gotten field duplicate", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - let query = Query::new(doc.doc_name()); - doc.send(query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), ids.len()); - for doc in data.iter() { - match doc.get(&f0name).unwrap() { - Field::Uuid(id) => { - assert!(ids.contains(&id)); - ids.remove(&id); - } - _ => unreachable!("did not get uuid"), - } - } - } - _ => unreachable!("got {:?}: should have gotten reply", action), - } - assert!(ids.is_empty(), "did not find {:?}", ids); - } - - #[test] - fn can_calculate_field_values() { - let fname = Name::english("field0"); - let mut doc = TestDocument::new([FieldType::DateTime].to_vec()); - doc.start(standard_paths()); - let duration = Duration::from_secs(300); - let mut calc = Calculation::new(Operand::Add); - calc.add_value(FieldType::DateTime).unwrap(); - calc.add_value(duration.clone()).unwrap(); - let mut addition = Addition::new(doc.doc_name()); - addition.add_field(&fname, calc); - let start = Utc::now() + duration; - doc.send(addition); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let stop = Utc::now() + duration; - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), 1); - for doc in data.iter() { - match doc.get(&fname).unwrap() { - Field::DateTime(datetime) => assert!(datetime > start && datetime < stop), - _ => unreachable!("did not get uuid"), - } - } - } - _ => unreachable!("got {:?}: should have gotten reply", action), - } - } - - #[test] - fn can_delete() { - let fname = Name::english("field0"); - let mut doc = TestDocument::new([FieldType::Integer].to_vec()); - doc.start(standard_paths()); - doc.populate([1.into()].to_vec()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(1).unwrap(); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - let mut delete = Delete::new(doc.doc_name()); - let query = delete.get_query_mut(); - query.add(&fname, calc.clone()); - doc.send(delete); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), 1); - for doc in data.iter() { - match doc.get(&fname).unwrap() { - Field::Integer(num) => assert_eq!(num, 1), - _ => unreachable!("did not get uuid"), - } - } - } - _ => unreachable!("got {:?}: should have gotten reply", action), - } - let mut result_query = Query::new(doc.doc_name()); - result_query.add(&fname, calc.clone()); - doc.send(result_query); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => assert_eq!(data.len(), 0), - _ => unreachable!("got {:?}, should have been empty", action), - } - } - - #[test] - fn does_delete_return_query_errors() { - let field_name = Name::english("wrong"); - let mut doc = TestDocument::new([FieldType::Integer].to_vec()); - doc.start(standard_paths()); - let mut calc = Calculation::new(Operand::Equal); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(1).unwrap(); - let mut delete = Delete::new(doc.doc_name()); - let query = delete.get_query_mut(); - query.add(field_name.clone(), calc); - doc.send(delete); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Error(err) => match err.get_error_ids().back().unwrap() { - ErrorID::NameNotFound(_) => {} - _ => unreachable!("got {:?}: should have gotten an missing field", err), - }, - _ => unreachable!("got {:?}: should have gotten an error", action), - } - } - - #[test] - fn does_delete_update_indexes() { - let fname = Name::english("field0"); - let value = 1; - let mut doc = TestDocument::new([FieldType::Integer].to_vec()); - doc.get_docdef_mut() - .add_index(&fname, IndexType::Unique) - .unwrap(); - doc.start(standard_paths()); - doc.populate([value.into()].to_vec()); - doc.send(Delete::new(doc.doc_name())); - doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let mut addition = Addition::new(doc.doc_name()); - addition.add_field(&fname, value.clone()); - doc.send(addition); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => assert_eq!(data.len(), 1), - _ => unreachable!("got {:?}, should have added entry", action), - } - } - */ - - #[test] - fn can_query_trigger_reaction() { - let mut doc = TestDocument::new([FieldType::Integer].to_vec()); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - let path = Path::new( - Include::All, - Include::Just(doc_name.clone().into()), - Include::Just(Action::OnQuery), - ); - let mut update = Update::new(doc_name.clone()); - let mut calc = Calculation::new(Operand::Add); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(1).unwrap(); - update - .get_values_mut() - .add_field(Name::english("field0"), calc); - let function = DocFuncType::ExistingQuery(update.into()); - doc.get_docdef_mut().add_route(path, function); - let mut paths = standard_paths(); - paths.push(Path::new( - Include::All, - Include::Just(doc_name.clone().into()), - Include::Just(Action::OnUpdate), - )); - doc.start(paths); - doc.populate([0.into()].to_vec()); - for i in 0..5 { - let expected: Field = i.try_into().unwrap(); - doc.send(Query::new(doc_name.clone())); - let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), 1); - for rec in data.iter() { - assert_eq!(rec.get(&Name::english("field0")).unwrap(), expected); - } - } - _ => unreachable!("got {:?}, should have added entry", action), - } - let on_update = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - match on_update.get_action() { - MsgAction::OnUpdate(recs) => { - let expected: Field = (i + 1).into(); - assert_eq!(recs.len(), 1); - for rec in recs.iter() { - assert_eq!(rec.get(Name::english("field0")).unwrap(), expected); - } - } - _ => unreachable!("should only be on update"), - } - } - } - - #[test] - fn can_an_action_trigger_an_action() { - let mut doc = TestDocument::new([FieldType::Integer].to_vec()); - let queue = doc.get_queue(); - let doc_name = doc.get_docdef().get_document_names()[0].clone(); - crate::document::clock::Clock::start(queue.clone()); - let mut calc = Calculation::new(Operand::GreaterThan); - calc.add_value(CalcValue::Existing(FieldType::Integer)) - .unwrap(); - calc.add_value(1).unwrap(); - let mut delete = Delete::new(doc_name.clone()); - let query = delete.get_query_mut(); - query.add(Name::english("field0"), calc); - let path = Path::new( - Include::All, - Include::Just(Name::english("clock").into()), - Include::Just(Action::OnUpdate), - ); - let function = DocFuncType::Trigger(delete.into()); - doc.get_docdef_mut().add_route(path, function); - let mut paths = standard_paths(); - paths.push(Path::new( - Include::All, - Include::Just(doc_name.clone().into()), - Include::Just(Action::Delete), - )); - doc.start(paths); - let queue = doc.get_queue(); - for item in 1..3 { - doc.populate([item.into()].to_vec()); - } - let trigger = Message::new(MsgAction::OnUpdate(Records::new( - Clock::doc_names(), - Names::new(), - ))); - queue.send(trigger.clone()); - sleep(TIMEOUT); - let msg = Message::new(Query::new(doc_name.clone())); - queue.send(msg.clone()); - let mut result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - while result.get_message_id() != msg.get_message_id() { - result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); - } - let action = result.get_action(); - let expected: Field = 1.into(); - match action { - MsgAction::Records(data) => { - assert_eq!(data.len(), 1, "should have been one record:\n{:?}", data); - for rec in data.iter() { - assert_eq!(rec.get(Name::english("field0")).unwrap(), expected); - } - } - _ => unreachable!("should return records"), - } - } -} diff --git a/tests/trigger_test.rs b/tests/trigger_test.rs new file mode 100644 index 0000000..51241e0 --- /dev/null +++ b/tests/trigger_test.rs @@ -0,0 +1,160 @@ +mod support; + +use morethantext::{ + Action, CalcValue, Calculation, Delete, DocFuncType, FieldType, Include, MoreThanText, Name, + Operand, Path, Query, TestMoreThanText, Update, +}; +use support::TestDocument; + +#[test] +fn can_a_trigger_cause_an_update() { + let data0 = 0; + let data1 = 0; + let data1_expected = 1; + let mut mtt = MoreThanText::new(); + let test_doc = TestDocument::new(vec![FieldType::Integer, FieldType::Integer]); + let mut calc = Calculation::new(Operand::Add); + calc.add_value(CalcValue::Existing(FieldType::Integer)) + .unwrap(); + calc.add_value(1).unwrap(); + let mut update = Update::new(test_doc.get_doc_name()); + update.add_field(test_doc.get_field_name(1), calc); + let path = Path::new( + Include::All, + Include::Just(test_doc.get_doc_name().into()), + Include::Just(Action::OnQuery), + ); + let function = DocFuncType::ExistingQuery(update.into()); + let mut docdef = test_doc.get_docdef(); + docdef.add_route(path, function); + mtt.create_document(docdef); + test_doc.populate(&mut mtt, vec![data0.clone(), data1.clone()]); + let first_qry = mtt.records(Query::new(test_doc.get_doc_name())).unwrap(); + assert_eq!(first_qry.len(), 1); + let first_rec = first_qry.iter().last().unwrap(); + assert_eq!( + first_rec.get(test_doc.get_field_name(0)).unwrap(), + data0.clone().into() + ); + assert_eq!( + first_rec.get(test_doc.get_field_name(1)).unwrap(), + data1.clone().into() + ); + let second_qry = mtt.records(Query::new(test_doc.get_doc_name())).unwrap(); + assert_eq!(second_qry.len(), 1); + let second_rec = second_qry.iter().last().unwrap(); + assert_eq!( + second_rec.get(test_doc.get_field_name(0)).unwrap(), + data0.clone().into() + ); + assert_eq!( + second_rec.get(test_doc.get_field_name(1)).unwrap(), + data1_expected.clone().into(), + "not updated" + ); +} + +#[test] +fn can_trigger_update_specific_record() { + let count = 3; + let selected = 1; // must be greater than or equal to 0 and less than count + let initial_data = 0; + let expected = 1; + let mut input: Vec> = Vec::new(); + for i in 0..count { + input.push(vec![i.clone(), initial_data.clone()]); + } + let mut mtt = MoreThanText::new(); + let test_doc = TestDocument::new(vec![FieldType::Integer, FieldType::Integer]); + let mut calc = Calculation::new(Operand::Add); + calc.add_value(CalcValue::Existing(FieldType::Integer)) + .unwrap(); + calc.add_value(1).unwrap(); + let mut update = Update::new(test_doc.get_doc_name()); + update.add_field(test_doc.get_field_name(1), calc); + let path = Path::new( + Include::All, + Include::Just(test_doc.get_doc_name().into()), + Include::Just(Action::OnQuery), + ); + let function = DocFuncType::ExistingQuery(update.into()); + let mut docdef = test_doc.get_docdef(); + docdef.add_route(path, function); + mtt.create_document(docdef); + test_doc.populate_multiple(&mut mtt, input); + let mut qry_calc = Calculation::new(Operand::Equal); + qry_calc + .add_value(CalcValue::Existing(FieldType::Integer)) + .unwrap(); + qry_calc.add_value(selected.clone()).unwrap(); + let mut qry = Query::new(test_doc.get_doc_name()); + qry.add(test_doc.get_field_name(0), qry_calc); + let first_result = mtt.records(qry).unwrap(); + assert_eq!(first_result.len(), 1); + let first_rec = first_result.iter().last().unwrap(); + assert_eq!( + first_rec.get(test_doc.get_field_name(0)).unwrap(), + selected.clone().into() + ); + assert_eq!( + first_rec.get(test_doc.get_field_name(1)).unwrap(), + initial_data.clone().into() + ); + let second_result = mtt.records(Query::new(test_doc.get_doc_name())).unwrap(); + assert_eq!(second_result.len(), count.try_into().unwrap()); + for rec in second_result.iter() { + if rec.get(test_doc.get_field_name(0)).unwrap() == selected.clone().into() { + assert_eq!( + rec.get(test_doc.get_field_name(1)).unwrap(), + expected.clone().into() + ); + } else { + assert_eq!( + rec.get(test_doc.get_field_name(1)).unwrap(), + initial_data.clone().into() + ); + } + } +} + +#[test] +fn can_a_trigger_from_another_document_be_used() { + let count = 3; + let selected = 1; // must be greater than or equal to 0 and less than count + let mut input: Vec> = Vec::new(); + for i in 0..count { + input.push(vec![i]); + } + let test_env = TestMoreThanText::new(); + let mut mtt = test_env.get_morethantext(); + let test_doc = TestDocument::new(vec![FieldType::Integer]); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(CalcValue::Existing(FieldType::Integer)) + .unwrap(); + calc.add_value(1).unwrap(); + let mut delete = Delete::new(test_doc.get_doc_name()); + delete.get_query_mut().add(test_doc.get_field_name(0), calc); + let path = Path::new( + Include::All, + Include::Just(Name::english("clock").into()), + Include::Just(Action::OnUpdate), + ); + let function = DocFuncType::Trigger(delete.into()); + let mut docdef = test_doc.get_docdef(); + docdef.add_route(path, function); + mtt.create_document(docdef); + test_doc.populate_multiple(&mut mtt, input); + test_env.send_time_pulse(); + let result = mtt.records(Query::new(test_doc.get_doc_name())).unwrap(); + assert_eq!( + result.len(), + (count - 1).try_into().unwrap(), + "wrong number of records" + ); + for rec in result.iter() { + assert!( + rec.get(test_doc.get_field_name(0)).unwrap() != selected.clone().into(), + "did not remove selected" + ); + } +}