use chrono::{DateTime, Utc}; use morethantext::{ action::{Addition, CalcValue, Calculation, Field, FieldType, Operand, Query, Record}, Action, ErrorID, Include, MTTError, MoreThanText, Name, Path, TestMoreThanText, Update, }; use std::time::Duration; use uuid::Uuid; fn doc_name() -> Name { Name::english("session") } fn get_session(mtt: &mut MoreThanText, id: &Uuid) -> Result { let mut qry = Query::new(doc_name()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Uuid)) .unwrap(); calc.add_value(id.clone()).unwrap(); qry.add(Name::english("id"), calc.clone()); match mtt.records(qry) { Ok(data) => Ok(data.iter().last().unwrap()), Err(err) => Err(err), } } #[test] fn are_session_ids_unique() { let mut mtt = MoreThanText::new(); let count = 10; let mut result: Vec = Vec::new(); for _ in 0..count { let id = mtt.validate_session(None); assert!(!result.contains(&id), "found {} in {:?}", id, result); result.push(id); } } #[test] fn bad_session_id_returns_new_id() { let mut mtt = MoreThanText::new(); let id1 = mtt.validate_session(Some("stuff".to_string())); let id2 = mtt.validate_session(Some("stuff".to_string())); assert_ne!(id1, id2); } #[test] fn creates_new_session_if_bad_or_expired() { let mut mtt = MoreThanText::new(); let id1 = mtt.validate_session(Some(Uuid::nil().to_string())); let id2 = mtt.validate_session(Some(Uuid::nil().to_string())); assert_ne!(id1, id2); } #[test] fn returns_same_session_id_when_valid() { let mut mtt = MoreThanText::new(); let id = mtt.validate_session(None); let result = mtt.validate_session(Some(id.to_string())); assert_eq!(result, id); } #[test] fn is_expiration_date_set_in_the_future() { let mut mtt = MoreThanText::new(); let start_time = Utc::now() + Duration::from_hours(1); let id = mtt.validate_session(None); let end_time = Utc::now() + Duration::from_hours(1); let rec = get_session(&mut mtt, &id).unwrap(); let holder = rec.get(Name::english("expire")).unwrap(); match holder { Field::DateTime(data) => { assert!(data > start_time, "expire should be after {:?}", start_time); assert!(data < end_time, "expire should be before {:?}", end_time); } _ => unreachable!("got {:?} should have been date time", holder), }; } #[test] fn are_session_ids_unique_on_update() { let mut mtt = MoreThanText::new(); let id_name = Name::english("id"); let id = mtt.validate_session(None); let mut addition = Addition::new(doc_name()); addition.add_field(id_name.clone(), id); let mut error = MTTError::new(ErrorID::IndexEntryAlreadyExists(id.into())); error.add_parent(ErrorID::Field(id_name.into())); error.add_parent(ErrorID::Document(Name::english("session").into())); match mtt.records(addition) { Ok(data) => unreachable!("got {:?} should have been error", data), Err(err) => assert_eq!(err.to_string(), error.to_string()), } } #[test] fn does_expire_update_on_query() { let mut mtt = MoreThanText::new(); let id = mtt.validate_session(None); let start_time = Utc::now() + Duration::from_secs(3600); mtt.validate_session(Some(id.to_string())); let end_time = Utc::now() + Duration::from_secs(3601); let rec = get_session(&mut mtt, &id).unwrap(); let holder = rec.get(Name::english("expire")).unwrap(); match holder { Field::DateTime(data) => { assert!(data > start_time, "expire should be after {:?}", start_time); assert!(data < end_time, "expire should be before {:?}", end_time); } _ => unreachable!("got {:?} should have been date time", holder), } } #[test] fn are_expired_sessions_removed() { let id_name = Name::english("id"); let mut test_env = TestMoreThanText::new(); let mut mtt = test_env.get_morethantext(); for _ in 0..5 { mtt.validate_session(None); } let id = mtt.validate_session(None); let mut update = Update::new(doc_name()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Uuid)) .unwrap(); calc.add_value(id.clone()).unwrap(); update.get_query_mut().add(id_name.clone(), calc.clone()); let expire = Utc::now() - Duration::from_secs(10); update .get_values_mut() .add_field(Name::english("expire"), expire); mtt.records(update).unwrap(); let path = Path::new( Include::All, Include::Just(doc_name().into()), Include::Just(Action::OnDelete), ); test_env.register_channel(vec![path]); test_env.send_time_pulse(); let result = test_env.get_trigger_records(Action::OnDelete); assert_eq!(result.len(), 1, "incorrect number of records"); let rec = result.iter().last().unwrap(); assert_eq!(rec.get(id_name).unwrap(), id.into()); }