From 02caf62f293802a3ff680c450c1730b893c29df7 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Mon, 23 Mar 2026 14:08:45 -0400 Subject: [PATCH] Completed the creation of the MTT client. --- src/lib.rs | 113 +++++++++++++++++++++++++++++++++++-------- tests/client_test.rs | 112 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8e4e444..49eca2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,11 +7,11 @@ mod queue; use document::{Clock, CreateDoc, Session}; use isolang::Language; -use message::{wrapper::Message, MessageID, MessageAction}; +use message::{wrapper::Message, MessageAction, MessageID}; use queue::{ - SenderID, data_director::{RegMsg, Register}, router::Queue, + SenderID, }; use std::{ sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender}, @@ -39,14 +39,14 @@ mod support_tests { static TIMEOUT: Duration = Duration::from_secs(10); -pub struct MTTClient { +struct PreBuildClient { queue: Queue, + msg_id: MessageID, rx: Receiver, sender_id: SenderID, - session_id: Uuid, } -impl MTTClient { +impl PreBuildClient { fn new(mut queue: Queue) -> Self { let sess_name = Session::doc_names()[0].clone(); let (tx, rx) = channel(); @@ -69,27 +69,94 @@ impl MTTClient { queue.send(Message::with_id(msg_id.clone(), reg_msg)); let result = rx.recv().unwrap(); } - let add = Addition::new(sess_name); - queue.send(Message::with_id(msg_id, add)); - let sess_info = rx.recv().unwrap(); - let session_id = match sess_info.get_action() { - MsgAction::Records(data) => { - let rec = data.iter().last().unwrap(); - match rec.get(Session::id_field_names()[0].clone()).unwrap().clone() { - Field::Uuid(id) => id.clone(), - _ => unreachable!("should only return uuid"), - } - } - _ => unreachable!("new session request should always succeed"), - }; Self { queue: queue, + msg_id: msg_id, rx: rx, sender_id: sender_id, + } + } + + fn send(&self, action: MA) -> Records + where + MA: Into, + { + self.queue + .send(Message::with_id(self.msg_id.clone(), action)); + let result = self.rx.recv().unwrap(); + match result.get_action() { + MsgAction::Records(data) => data.clone(), + _ => unreachable!("session requests should always return data"), + } + } +} + +pub struct MTTClient { + queue: Queue, + rx: Receiver, + sender_id: SenderID, + session_id: Uuid, +} + +impl MTTClient { + fn internal_new(prebuild: PreBuildClient, lang: Option) -> Self { + let mut add = Addition::new(Session::doc_names()[0].clone()); + match lang { + Some(language) => { + let field: Field = language.into(); + add.add_field(Session::language_field_names()[0].clone(), field); + } + None => {} + } + let result = prebuild.send(add); + let rec = result.iter().last().unwrap(); + let session_id = match rec.get(Session::id_field_names()[0].clone()).unwrap() { + Field::Uuid(data) => data.clone(), + _ => unreachable!("should always be uuid"), + }; + Self { + queue: prebuild.queue, + rx: prebuild.rx, + sender_id: prebuild.sender_id, session_id: session_id, } } + fn new(mut queue: Queue, lang: Option) -> Self { + let prebuild = PreBuildClient::new(queue); + Self::internal_new(prebuild, lang) + } + + fn with_session(queue: Queue, id: String, lang: Option) -> Self { + let prebuild = PreBuildClient::new(queue.clone()); + let sess_id = match Uuid::try_from(id.as_str()) { + Ok(data) => data, + Err(_) => return Self::internal_new(prebuild, lang), + }; + let mut qry = Query::new(Session::doc_names()[0].clone()); + let mut calc = Calculation::new(Operand::Equal); + calc.add_value(CalcValue::Existing(FieldType::Uuid)) + .unwrap(); + calc.add_value(sess_id).unwrap(); + qry.add(Session::id_field_names()[0].clone(), calc); + let result = prebuild.send(qry); + if result.len() == 0 { + Self::internal_new(prebuild, lang) + } else { + let rec = result.iter().last().unwrap(); + let session_id = match rec.get(Session::id_field_names()[0].clone()).unwrap() { + Field::Uuid(data) => data.clone(), + _ => unreachable!("should always be uuid"), + }; + Self { + queue: queue, + rx: prebuild.rx, + sender_id: prebuild.sender_id.clone(), + session_id: session_id, + } + } + } + pub fn session_id(&self) -> String { self.session_id.to_string() } @@ -121,7 +188,15 @@ impl MoreThanText { } pub fn client(&self) -> MTTClient { - MTTClient::new(self.queue.clone()) + MTTClient::new(self.queue.clone(), None) + } + + pub fn client_with_language(&self, lang: Language) -> MTTClient { + MTTClient::new(self.queue.clone(), Some(lang)) + } + + pub fn client_with_session(&self, id: String, lang: Option) -> MTTClient { + MTTClient::with_session(self.queue.clone(), id, lang) } fn new_session(lang: Option) -> ClientAction { diff --git a/tests/client_test.rs b/tests/client_test.rs index b98cbfc..d3eac79 100644 --- a/tests/client_test.rs +++ b/tests/client_test.rs @@ -1,5 +1,15 @@ -use morethantext::MoreThanText; -use std::collections::HashSet; +use isolang::Language; +use morethantext::{Action, Field, Include, MoreThanText, Name, Path, TestMoreThanText}; +use std::{collections::HashSet, sync::mpsc::RecvTimeoutError}; +use uuid::Uuid; + +fn doc_name() -> Name { + Name::english("session") +} + +fn lang_name() -> Name { + Name::english("language") +} #[test] fn are_session_ids_unique() { @@ -12,3 +22,101 @@ fn are_session_ids_unique() { } assert_eq!(ids.len(), count, "ids = {:?}", ids); } + +#[test] +fn can_existing_sessions_be_used() { + let mtt = MoreThanText::new(); + let client1 = mtt.client(); + let id = client1.session_id(); + drop(client1); + let client2 = mtt.client_with_session(id.clone(), None); + assert_eq!(client2.session_id(), id); +} + +#[test] +fn does_expired_session_ids_return_new() { + let id = Uuid::new_v4().to_string(); + let mtt = MoreThanText::new(); + let client = mtt.client_with_session(id.clone(), None); + assert_ne!(client.session_id(), id); +} + +#[test] +fn does_bad_id_string_get_new() { + let id = "Not uuid".to_string(); + let mtt = MoreThanText::new(); + let client = mtt.client_with_session(id.clone(), None); + assert_ne!(client.session_id(), id); +} + +#[test] +fn can_new_clients_set_langauge() { + let lang = Language::from_639_1("fr").unwrap(); + let mut test_env = TestMoreThanText::new(); + let mtt = test_env.get_morethantext(); + let path = Path::new( + Include::All, + Include::Just(doc_name().into()), + Include::Just(Action::OnAddition), + ); + test_env.register_channel(vec![path]); + mtt.client_with_language(lang.clone()); + let result = test_env.get_trigger_records(Action::OnAddition); + let rec = result.iter().last().unwrap(); + assert_eq!(rec.get(&lang_name()).unwrap(), lang.into()); +} + +#[test] +fn is_lanaguage_set_for_expired_session() { + let lang = Language::from_639_1("fr").unwrap(); + let mut test_env = TestMoreThanText::new(); + let mtt = test_env.get_morethantext(); + let path = Path::new( + Include::All, + Include::Just(doc_name().into()), + Include::Just(Action::OnAddition), + ); + test_env.register_channel(vec![path]); + mtt.client_with_session(Uuid::new_v4().to_string(), Some(lang)); + let result = test_env.get_trigger_records(Action::OnAddition); + let rec = result.iter().last().unwrap(); + assert_eq!(rec.get(&lang_name()).unwrap(), lang.into()); +} + +#[test] +fn is_lanaguage_set_for_bad_session() { + let lang = Language::from_639_1("de").unwrap(); + let mut test_env = TestMoreThanText::new(); + let mtt = test_env.get_morethantext(); + let path = Path::new( + Include::All, + Include::Just(doc_name().into()), + Include::Just(Action::OnAddition), + ); + test_env.register_channel(vec![path]); + mtt.client_with_session("bad".to_string(), Some(lang)); + let result = test_env.get_trigger_records(Action::OnAddition); + let rec = result.iter().last().unwrap(); + assert_eq!(rec.get(&lang_name()).unwrap(), lang.into()); +} + +#[test] +fn do_existing_sessions_keep_language_unchanged() { + let lang1 = Language::from_639_1("de").unwrap(); + let lang2 = Language::from_639_1("fr").unwrap(); + let mut test_env = TestMoreThanText::new(); + let mtt = test_env.get_morethantext(); + let client = mtt.client_with_language(lang1); + let id = client.session_id(); + drop(client); + let path = Path::new( + Include::All, + Include::Just(doc_name().into()), + Include::Just(Action::OnUpdate), + ); + test_env.register_channel(vec![path]); + mtt.client_with_session(id.clone(), Some(lang2)); + let result = test_env.get_trigger_records(Action::OnUpdate); + let rec = result.iter().last().unwrap(); + assert_eq!(rec.get(&lang_name()).unwrap(), lang1.into()); +}