diff --git a/src/message.rs b/src/message.rs index fa7079c..e2db202 100644 --- a/src/message.rs +++ b/src/message.rs @@ -26,6 +26,7 @@ enum MTTError { DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), FieldDuplicate(String, Field), + QueryCannotChangeData, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -1171,12 +1172,10 @@ impl FieldSetting { } Ok(data.clone()) } - None => { - match &self.default_value { - Some(calc) => Ok(calc.calculate()), - None => Err(MTTError::DocumentFieldMissing("".to_string())), - } - } + None => match &self.default_value { + Some(calc) => Ok(calc.calculate()), + None => Err(MTTError::DocumentFieldMissing("".to_string())), + }, } } } @@ -1394,6 +1393,10 @@ impl DocDef { } } + fn field_ids(&self) -> HashSet<&String> { + self.fields.keys().collect::>() + } + fn validate(&self, field_name: &str, value: Option) -> Result { let setting = match self.get_field(field_name) { Ok(data) => data, @@ -1542,6 +1545,24 @@ mod docdefs { }, } } + + #[test] + fn returns_field_ids() { + let count = 5; + let mut ids: HashSet = HashSet::new(); + while ids.len() < count { + ids.insert(Uuid::new_v4().to_string()); + } + let mut docdef = DocDef::new(); + for id in ids.iter() { + docdef.add_field(id.clone(), FieldType::Uuid); + } + let result = docdef.field_ids(); + assert_eq!(result.len(), ids.len()); + for id in result.iter() { + assert!(ids.contains(id.clone())); + } + } } #[derive(Clone, Debug)] @@ -1753,6 +1774,10 @@ impl Calculation { } } + fn operation(&self) -> &Operand { + &self.operation + } + fn get_fields(&self) -> Vec { let mut output = Vec::new(); for item in self.values.iter() { @@ -1761,6 +1786,20 @@ impl Calculation { output } + fn push_value( + &mut self, + base: FieldType, + ftype: FieldType, + data: CalcValue, + ) -> Result<(), MTTError> { + if base == ftype { + self.values.push(data); + } else { + return Err(MTTError::DocumentFieldWrongDataType(base, ftype)); + } + Ok(()) + } + fn add_value(&mut self, data: CV) -> Result<(), MTTError> where CV: Into, @@ -1768,24 +1807,26 @@ impl Calculation { let holder: CalcValue = data.into(); if self.values.len() == 0 { self.values.push(holder); + Ok(()) } else { + let mut base = self.values[0].get().get_type(); + let ftype = holder.get().get_type(); match self.operation { Operand::Add => { - let mut base = self.values[0].get().get_type(); if base == FieldType::DateTime { base = FieldType::Duration; } - let ftype = holder.get().get_type(); - if base == ftype { - self.values.push(holder); - } else { - return Err(MTTError::DocumentFieldWrongDataType(base, ftype)); + match self.push_value(base, ftype, holder) { + Ok(_) => Ok(()), + Err(err) => Err(err), } } - _ => self.values.push(holder), + _ => match self.push_value(base, ftype, holder) { + Ok(_) => Ok(()), + Err(err) => Err(err), + }, } } - Ok(()) } fn calculate(&self) -> Field { @@ -1828,6 +1869,36 @@ 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::Assign); + match calc.operation() { + Operand::Assign => {} + _ => 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_assign_value() { let mut calc = Calculation::new(Operand::Assign); @@ -1951,26 +2022,112 @@ impl Operation { #[derive(Clone, Debug)] struct Query { + data: HashMap, + specifiers: Vec, } impl Query { fn new() -> Self { Self { + data: HashMap::new(), + specifiers: Vec::new(), } } - fn add_specifier(&mut self, name: String, op: Operand, value: F) - where - F: Into, - { - let spec = Operation::new(name, op, value); - self.specifiers.push(spec); + fn add(&mut self, name: String, operation: Calculation) -> Result<(), MTTError> { + match operation.operation() { + Operand::Equal => { + self.data.insert(name, operation); + Ok(()) + } + _ => Err(MTTError::QueryCannotChangeData), + } } - fn iter(&self) -> impl Iterator { - self.specifiers.iter() + fn get(&self, name: &str) -> Option { + match self.data.get(name) { + Some(calc) => Some(calc.clone()), + None => None, + } + } + + fn field_ids(&self) -> HashSet<&String> { + self.data.keys().collect::>() + } +} + +#[cfg(test)] +mod queries { + use super::*; + + #[test] + fn holds_calculation_to_run_query() { + let name = Uuid::new_v4().to_string(); + 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()); + 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); + assert_eq!(holder.calculate(), 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); + assert_eq!(holder.calculate(), expected); + } + None => unreachable!("should have returned a calculation"), + } + } + + #[test] + fn does_not_allow_data_changes() { + let mut calc = Calculation::new(Operand::Assign); + calc.add_value(Uuid::nil()); + let mut query = Query::new(); + match query.add("name".to_string(), calc) { + Ok(_) => unreachable!("Should have received an error"), + Err(err) => match err { + MTTError::QueryCannotChangeData => {} + _ => unreachable!("got {:?}, should have gotten cannot change data", err), + }, + } + } + + #[test] + fn returns_set_of_fields() { + let count = 5; + let mut field_ids: HashSet = HashSet::new(); + while field_ids.len() < count { + field_ids.insert(Uuid::new_v4().to_string()); + } + let mut query = Query::new(); + for field_id in field_ids.iter() { + query.add(field_id.clone(), Calculation::new(Operand::Equal)); + } + let result = query.field_ids(); + assert_eq!(result.len(), field_ids.len()); + for field_id in result.iter() { + assert!( + field_ids.contains(field_id.clone()), + "field id {:?} not found", + field_id + ); + } } } @@ -2545,31 +2702,34 @@ impl DocumentFile { reply.into() } - fn run_query(&self, query: &Query) -> Result, MTTError> { - let mut reply = Reply::new(); - for specifier in query.iter() { - match self.validate(&specifier.field_name, Some(specifier.value.clone())) { - Ok(_) => {} - Err(err) => match err { - MTTError::FieldDuplicate(_, _) => {} - _ => return Err(err), - }, - } + fn run_query(&self, query: &Query) -> Result, MTTError> { + let query_ids = query.field_ids(); + let doc_ids = self.docdef.field_ids(); + if !doc_ids.is_superset(&query_ids) { + let missed = query_ids.difference(&doc_ids).last().unwrap(); + return Err(MTTError::DocumentFieldNotFound(missed.to_string())); } - let mut result = Vec::new(); - for (oid, doc) in self.get_documents() { - let mut output = true; - for specifier in query.iter() { - let value = doc.get_field(&specifier.field_name).unwrap(); - if value != &specifier.value { - output = false; + let mut oids: HashSet = HashSet::new(); + 'docs: for (oid, doc) in self.docs.iter() { + for query_id in query_ids.iter() { + let doc_data = doc.get_field(query_id).unwrap(); + let mut operation = query.get(query_id).unwrap(); + match operation.add_value(doc_data.clone()) { + Ok(_) => {} + Err(err) => match err { + MTTError::DocumentFieldWrongDataType(got, expected) => { + return Err(MTTError::DocumentFieldWrongDataType(expected, got)) + } + _ => return Err(err), + }, + } + if operation.calculate() == false.into() { + continue 'docs; } } - if output { - result.push(oid.clone()); - } + oids.insert(oid.clone()); } - Ok(result) + Ok(oids) } fn query(&self, query: &Query) -> MsgAction { @@ -2630,6 +2790,86 @@ mod document_files { use super::{support_test::TIMEOUT, *}; use std::sync::mpsc::RecvTimeoutError; + struct TestDocument { + docdef: DocDef, + doc_name: String, + queue: Queue, + routes: Vec, + tx: Sender, + rx: Receiver, + } + + impl TestDocument { + fn new(field_types: Vec) -> Self { + let mut docdef = DocDef::new(); + let mut count = 0; + for field_type in field_types.iter() { + docdef.add_field(format!("field{}", count), field_type.clone()); + count += 1; + } + let (tx, rx) = channel(); + Self { + docdef: docdef, + doc_name: Uuid::new_v4().to_string(), + queue: Queue::new(), + routes: [ + RouteRequest::new(Include::All, Include::All, Include::Some(Action::Reply)), + RouteRequest::new(Include::All, Include::All, Include::Some(Action::Error)), + ] + .to_vec(), + tx: tx, + rx: rx, + } + } + + fn get_docdef_mut(&mut self) -> &mut DocDef { + &mut self.docdef + } + + fn get_routes_mut(&mut self) -> &mut Vec { + &mut self.routes + } + + fn get_queue(&mut self) -> Queue { + self.queue.clone() + } + + fn get_receiver(&self) -> &Receiver { + &self.rx + } + + fn send(&self, action: A) -> Result<(), MTTError> + where + A: Into, + { + let msg = Message::new(self.doc_name.clone(), action); + self.queue.send(msg) + } + + fn start(&mut self) { + let msg = Message::new(self.doc_name.clone(), self.docdef.clone()); + DocumentFile::start(self.queue.clone(), msg); + self.queue + .register( + self.tx.clone(), + Uuid::new_v4().to_string(), + self.routes.clone(), + ) + .unwrap(); + } + + fn populate(&self, data: Vec) { + let mut add = Addition::new(); + let mut count = 0; + for item in data.iter() { + add.add_field(format!("field{}", count), item.clone()); + count += 1; + } + self.send(add).unwrap(); + self.rx.recv().unwrap(); // eat addition response. + } + } + fn standard_routes() -> Vec { [ RouteRequest::new(Include::All, Include::All, Include::Some(Action::Reply)), @@ -2908,46 +3148,35 @@ mod document_files { #[test] fn does_query_return_related_entries() { - let (docdef, doc_name) = create_docdef([FieldType::Uuid, FieldType::Uuid].to_vec()); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - let field0 = Uuid::new_v4(); - let field1 = Uuid::new_v4(); - for _ in 0..3 { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), Uuid::new_v4()); - addition.add_field("field1".to_string(), Uuid::new_v4()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); + doc.start(); + let count = 3; + let mut values: HashSet = HashSet::new(); + while values.len() < count { + values.insert(Uuid::new_v4().into()); } - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), field0.clone()); - addition.add_field("field1".to_string(), field1.clone()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); - for _ in 0..3 { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), Uuid::new_v4()); - addition.add_field("field1".to_string(), Uuid::new_v4()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + for value in values.iter() { + doc.populate([value.clone()].to_vec()); } + let expected = values.iter().last().unwrap().clone(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(expected.clone()); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, field0.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + query.add("field0".to_string(), calc); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); - let field0: Field = field0.into(); - let field1: Field = field1.into(); match action { MsgAction::Reply(data) => { - assert_eq!(data.len(), 1, "should return one entry"); + assert_eq!( + data.len(), + 1, + "should return one entry containing {:?} got:\n{:?}", + expected, + action + ); for doc in data.iter() { - assert_eq!(doc.get_field("field0").unwrap(), &field0); - assert_eq!(doc.get_field("field1").unwrap(), &field1); + assert_eq!(doc.get_field("field0").unwrap(), &expected); } } _ => unreachable!("got {:?}: should have been a reply", action), @@ -2956,34 +3185,32 @@ mod document_files { #[test] fn gets_all_documents_in_query() { - let (docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); - let count = 4; - let input = Uuid::new_v4(); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - for _ in 0..3 { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), Uuid::new_v4()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); - } - for _ in 0..count { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), input.clone()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + let mut doc = TestDocument::new([FieldType::Integer].to_vec()); + doc.start(); + let values = [ + [1.into()].to_vec(), + [2.into()].to_vec(), + [1.into()].to_vec(), + [3.into()].to_vec(), + [1.into()].to_vec(), + [4.into()].to_vec(), + [1.into()].to_vec(), + [5.into()].to_vec(), + ]; + for value in values.iter() { + doc.populate(value.clone()); } + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(1); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, input.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + query.add("field0".to_string(), calc); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); - let input: Field = input.into(); + let input: Field = 1.into(); match action { MsgAction::Reply(data) => { - assert_eq!(data.len(), count, "should return {} entries", count); + assert_eq!(data.len(), 4, "should return 4 entries"); for doc in data.iter() { assert_eq!(doc.get_field("field0").unwrap(), &input); } @@ -2994,40 +3221,36 @@ mod document_files { #[test] fn query_should_work_with_multiple_fields() { - let (docdef, doc_name) = create_docdef([FieldType::Uuid, FieldType::Uuid].to_vec()); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - let field0 = Uuid::new_v4(); - let field1 = Uuid::new_v4(); - let input = [ - [Uuid::new_v4(), Uuid::new_v4()], - [field0.clone(), field1.clone()], - [field1.clone(), field0.clone()], - [field0.clone(), Uuid::new_v4()], - [Uuid::new_v4(), field1.clone()], + let mut doc = + TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); + doc.start(); + 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 combo in input.iter() { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), combo[0].clone()); - addition.add_field("field1".to_string(), combo[1].clone()); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + for value in values.iter() { + doc.populate(value.clone()); } let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, field0.clone()); - query.add_specifier("field1".to_string(), Operand::Equal, field1.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value("a"); + query.add("field0".to_string(), calc); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value("b"); + query.add("field1".to_string(), calc); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); - let field0: Field = field0.into(); - let field1: Field = field1.into(); match action { MsgAction::Reply(data) => { - assert_eq!(data.len(), 1, "should return one entry"); + 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_field("field0").unwrap(), &field0); - assert_eq!(doc.get_field("field1").unwrap(), &field1); + assert_eq!(doc.get_field("field0").unwrap(), &afield); + assert_eq!(doc.get_field("field1").unwrap(), &bfield); } } _ => unreachable!("got {:?}: should have been a reply", action), @@ -3040,7 +3263,9 @@ mod document_files { let field_name = "wrong"; let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let mut query = Query::new(); - query.add_specifier(field_name.to_string(), Operand::Equal, Uuid::new_v4()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value("something"); + query.add(field_name.to_string(), calc); let msg = Message::new(doc_name, query); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); @@ -3056,13 +3281,15 @@ mod document_files { #[test] fn errors_on_bad_field_type() { - let (docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); + doc.start(); + doc.populate([Uuid::nil().into()].to_vec()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value("notUUID"); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, "wrong"); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + query.add("field0".to_string(), calc); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(data) => match data { @@ -3152,18 +3379,27 @@ mod document_files { #[test] fn empty_update_query_results_in_zero_changes() { - let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + 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(); + for id in ids.iter() { + doc.populate([id.clone().into()].to_vec()); + } let mut update = Update::new(); - update - .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, Uuid::new_v4()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(id); + update.get_query_mut().add("field0".to_string(), calc); update .get_values_mut() .add_field("field0".to_string(), Uuid::nil()); - let msg = Message::new(doc_name, update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + doc.send(update).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(docs) => assert_eq!(docs.len(), 0), @@ -3173,23 +3409,18 @@ mod document_files { #[test] fn only_responses_to_its_update_request() { - let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Integer].to_vec()); + doc.start(); let alt_doc_name = "alternate"; let (tx, _) = channel(); + let mut queue = doc.get_queue(); queue .register(tx, alt_doc_name.to_string(), Vec::new()) .unwrap(); - let mut update = Update::new(); - update - .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, Uuid::new_v4()); - update - .get_values_mut() - .add_field("field0".to_string(), Uuid::nil()); + let update = Update::new(); let msg = Message::new(alt_doc_name, update); queue.send(msg).unwrap(); - match rx.recv_timeout(TIMEOUT) { + match doc.get_receiver().recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} @@ -3200,95 +3431,89 @@ mod document_files { #[test] fn changes_information_requested() { - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.start(); let id = Uuid::new_v4(); let old = "old"; let new = "new"; - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), id.clone()); - addition.add_field("field1".to_string(), old); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(id.clone()); update .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, id.clone()); + .add("field0".to_string(), calc.clone()); update.get_values_mut().add_field("field1".to_string(), new); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Reply(docs) => { - assert_eq!(docs.len(), 1); - let expected_id: Field = id.into(); - let output: Field = new.into(); - for doc in docs.iter() { - assert_eq!(doc.get_field("field0").unwrap(), &expected_id); - assert_eq!(doc.get_field("field1").unwrap(), &output); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } + doc.send(update).unwrap(); + let mut results: HashMap = HashMap::new(); + results.insert( + "update".to_string(), + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), + ); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, id.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Reply(docs) => { - assert_eq!(docs.len(), 1); - let expected_id: Field = id.into(); - let output: Field = new.into(); - for doc in docs.iter() { - assert_eq!(doc.get_field("field0").unwrap(), &expected_id); - assert_eq!(doc.get_field("field1").unwrap(), &output); + query.add("field0".to_string(), calc.clone()); + doc.send(query).unwrap(); + results.insert( + "query".to_string(), + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), + ); + let expected_id: Field = id.into(); + let output: Field = new.into(); + for (key, result) in results.iter() { + let action = result.get_action(); + match action { + MsgAction::Reply(docs) => { + assert_eq!(docs.len(), 1, "{}", key); + for doc in docs.iter() { + assert_eq!(doc.get_field("field0").unwrap(), &expected_id, "{}", key); + assert_eq!(doc.get_field("field1").unwrap(), &output, "{}", key); + } } + _ => unreachable!("got {:?}: should have gotten a reply", action), } - _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn changes_only_the_queried() { - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - let id1 = Uuid::new_v4(); - let id2 = Uuid::new_v4(); + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.start(); + let mut ids: HashSet = HashSet::new(); + while ids.len() < 2 { + ids.insert(Uuid::new_v4()); + } + let expected = ids.iter().last().unwrap(); let old = "old"; let new = "new"; - for id in [id1.clone(), id2.clone()].into_iter() { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), id); - addition.add_field("field1".to_string(), old); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + let mut values: Vec> = Vec::new(); + for id in ids.iter() { + let mut holder: Vec = Vec::new(); + holder.push(id.clone().into()); + holder.push(old.into()); + values.push(holder); + } + for value in values { + doc.populate(value); } let mut update = Update::new(); - update - .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, id1.clone()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(expected.clone()); + update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("field1".to_string(), new); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.send(update).unwrap(); + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(new); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, id2.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + query.add("field1".to_string(), calc); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(docs) => { assert_eq!(docs.len(), 1); - let expected_id: Field = id2.into(); - let output: Field = old.into(); + let expected_id: Field = expected.clone().into(); + let output: Field = new.into(); for doc in docs.iter() { assert_eq!(doc.get_field("field0").unwrap(), &expected_id); assert_eq!(doc.get_field("field1").unwrap(), &output); @@ -3300,84 +3525,67 @@ mod document_files { #[test] fn can_handle_multiple_updates() { - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.start(); let count = 3; let id = Uuid::new_v4(); let old = "old"; let new = "new"; - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), id.clone()); - addition.add_field("field1".to_string(), old); - let msg = Message::new(doc_name.clone(), addition); for _ in 0..count { - queue.send(msg.clone()).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.populate([id.into(), old.into()].to_vec()); } + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(id.clone()); let mut update = Update::new(); update .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, id.clone()); + .add("field0".to_string(), calc.clone()); update.get_values_mut().add_field("field1".to_string(), new); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Reply(docs) => { - assert_eq!(docs.len(), count); - let expected_id: Field = id.into(); - let output: Field = new.into(); - for doc in docs.iter() { - assert_eq!(doc.get_field("field0").unwrap(), &expected_id); - assert_eq!(doc.get_field("field1").unwrap(), &output); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } + doc.send(update).unwrap(); + let mut results: HashMap = HashMap::new(); + results.insert( + "update".to_string(), + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), + ); let mut query = Query::new(); - query.add_specifier("field0".to_string(), Operand::Equal, id.clone()); - let msg = Message::new(doc_name, query); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Reply(docs) => { - assert_eq!(docs.len(), count); - let expected_id: Field = id.into(); - let output: Field = new.into(); - for doc in docs.iter() { - assert_eq!(doc.get_field("field0").unwrap(), &expected_id); - assert_eq!(doc.get_field("field1").unwrap(), &output); + query.add("field0".to_string(), calc.clone()); + doc.send(query).unwrap(); + results.insert( + "query".to_string(), + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), + ); + let expected_id: Field = id.into(); + let output: Field = new.into(); + for (key, result) in results.iter() { + let action = result.get_action(); + match action { + MsgAction::Reply(docs) => { + assert_eq!(docs.len(), count, "{}", key); + for doc in docs.iter() { + assert_eq!(doc.get_field("field0").unwrap(), &expected_id, "{}", key); + assert_eq!(doc.get_field("field1").unwrap(), &output, "{}", key); + } } + _ => unreachable!("got {:?}: should have gotten a reply", action), } - _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn update_errors_on_bad_field_name() { - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.start(); let id = Uuid::new_v4(); let old = "old"; let new = "new"; - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), id.clone()); - addition.add_field("field1".to_string(), old); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); - update - .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, id.clone()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(id.clone()); + update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("wrong".to_string(), new); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + doc.send(update).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { @@ -3390,25 +3598,19 @@ mod document_files { #[test] fn update_errors_on_bad_field_type() { - let (docdef, doc_name) = create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.start(); let id = Uuid::new_v4(); let old = "old"; let new = Uuid::nil(); - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), id.clone()); - addition.add_field("field1".to_string(), old); - let msg = Message::new(doc_name.clone(), addition); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); - update - .get_query_mut() - .add_specifier("field0".to_string(), Operand::Equal, id.clone()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(id.clone()); + update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("field1".to_string(), new); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + doc.send(update).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { @@ -3416,7 +3618,7 @@ mod document_files { assert_eq!(expected, &FieldType::StaticString); assert_eq!(got, &FieldType::Uuid); } - _ => unreachable!("got {:?}: should have gotten an missing field", err), + _ => unreachable!("got {:?}: should have gotten incorrect file type", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } @@ -3477,66 +3679,40 @@ mod document_files { #[test] fn updating_unique_updates_index_entries() { - let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); - docdef.add_index("field0".to_string(), IndexType::Unique); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - let old = Uuid::new_v4(); - let mut new = Uuid::new_v4(); - while old == new { - new = Uuid::new_v4(); - } + let mut doc = TestDocument::new([FieldType::StaticString].to_vec()); + doc.get_docdef_mut() + .add_index("field0".to_string(), IndexType::Unique); + doc.start(); + let old = "old"; + let new = "new"; let fold: Field = old.into(); let fnew: Field = new.into(); - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), old.clone()); - let msg = Message::new(doc_name.clone(), addition.clone()); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + doc.populate([old.into()].to_vec()); let mut update = Update::new(); - let query = update.get_query_mut(); - query.add_specifier("field0".to_string(), Operand::Equal, old.clone()); - let values = update.get_values_mut(); - values.add_field("field0".to_string(), new.clone()); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(old); + update.get_query_mut().add("field0".to_string(), calc); + update.get_values_mut().add_field("field0".to_string(), new); + doc.send(update).unwrap(); + doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); + let mut old_addition = Addition::new(); + old_addition.add_field("field0".to_string(), old); + doc.send(old_addition).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { - assert_eq!( - doc.get_field("field0").unwrap(), - &fnew, - "got {:?} as a reply", - data - ); + assert_eq!(doc.get_field("field0").unwrap(), &fold); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } - let msg = Message::new(doc_name.clone(), addition.clone()); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); - let action = result.get_action(); - match action { - MsgAction::Reply(data) => { - assert_eq!(data.len(), 1); - for doc in data.iter() { - assert_eq!( - doc.get_field("field0").unwrap(), - &fold, - "got {:?} as a reply", - data - ); - } - } - _ => unreachable!("got {:?}: should have gotten a reply", action), - } - addition.add_field("field0".to_string(), new.clone()); - let msg = Message::new(doc_name.clone(), addition.clone()); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + let mut new_addition = Addition::new(); + new_addition.add_field("field0".to_string(), new); + doc.send(new_addition).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { @@ -3553,46 +3729,58 @@ mod document_files { #[test] fn unique_available_after_bad_change() { - let mut ids: Vec = Vec::new(); - while ids.len() < 3 { - let id = Uuid::new_v4(); - if !ids.contains(&id) { - ids.push(id); - } + let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); + doc.get_docdef_mut() + .add_index("field0".to_string(), IndexType::Unique); + doc.start(); + let count = 5; + let data = "data"; + let mut ids: HashSet = HashSet::new(); + while ids.len() < count { + ids.insert(Uuid::new_v4()); } - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - docdef.add_index("field0".to_string(), IndexType::Unique); - let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); - let field1 = "fred"; - for index in 0..2 { - let mut addition = Addition::new(); - addition.add_field("field0".to_string(), ids[index].clone()); - addition.add_field("field1".to_string(), field1); - let msg = Message::new(doc_name.clone(), addition.clone()); - queue.send(msg).unwrap(); - rx.recv_timeout(TIMEOUT).unwrap(); + let holder = ids.iter().last().unwrap().clone(); + let fholder: Field = holder.into(); + ids.remove(&holder); + for id in ids.iter() { + doc.populate([id.clone().into(), data.into()].to_vec()); } let mut update = Update::new(); - let query = update.get_query_mut(); - query.add_specifier("field1".to_string(), Operand::Equal, field1); - let values = update.get_values_mut(); - values.add_field("field0".to_string(), ids[2].clone()); - let msg = Message::new(doc_name.clone(), update); - queue.send(msg).unwrap(); - let result = rx.recv_timeout(TIMEOUT).unwrap(); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(data); + update.get_query_mut().add("field1".to_string(), calc); + update + .get_values_mut() + .add_field("field0".to_string(), holder.clone()); + doc.send(update).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate(key, field) => { - let expected: Field = ids[2].into(); assert_eq!(key, "field0"); - assert_eq!(field, &expected); + assert_eq!(field, &fholder); } _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } + let query = Query::new(); + doc.send(query).unwrap(); + let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); + let action = result.get_action(); + match action { + MsgAction::Reply(data) => { + assert_eq!(data.len(), ids.len()); + for doc in data.iter() { + match doc.get_field("field0").unwrap() { + Field::Uuid(id) => assert!(ids.contains(id)), + _ => unreachable!("did not get uuid"), + } + } + } + _ => unreachable!("got {:?}: should have gotten reply", action), + } } }