From 55ffa538e86ec930444a7a6026748afd2a56fde3 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Fri, 25 Apr 2025 14:02:40 -0400 Subject: [PATCH] Got backend using errors. --- src/client.rs | 2 +- src/document.rs | 31 ++++++++++-- src/lib.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 16 +++--- 4 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/client.rs b/src/client.rs index 9c63a83..396b2aa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -9,7 +9,7 @@ use std::{ }; use uuid::Uuid; -const RESPONS_TO: [MsgType; 2] = [MsgType::Document, MsgType::SessionValidated]; +const RESPONS_TO: [MsgType; 3] = [MsgType::Document, MsgType::Error, MsgType::SessionValidated]; #[derive(Clone)] pub struct ClientChannel { diff --git a/src/document.rs b/src/document.rs index b4521a8..04f723f 100644 --- a/src/document.rs +++ b/src/document.rs @@ -31,11 +31,18 @@ impl Document { fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); - let mut reply = msg.reply(MsgType::Document); - if msg.get_data("name").is_some() { - reply = msg.reply(MsgType::Error); - reply.add_data("error_type", ErrorType::DocumentNotFound); - } + let mut reply = match msg.get_data("name") { + Some(name) => { + if name.to_string() == "root" { + msg.reply(MsgType::Document) + } else { + let mut output = msg.reply(MsgType::Error); + output.add_data("error_type", ErrorType::DocumentNotFound); + output + } + }, + None => msg.reply(MsgType::Document), + }; reply.add_data("doc", "Something goes hwew"); self.queue.send(reply).unwrap(); } @@ -95,4 +102,18 @@ pub mod documents { None => unreachable!("should contain error type"), } } + + #[test] + fn root_always_exists() { + let (queue, rx) = setup_document(); + let name = format!("name-{}", Uuid::new_v4()); + let mut msg = Message::new(MsgType::DocumentRequest); + msg.add_data("name", "root"); + queue.send(msg); + let reply = rx.recv().unwrap(); + match reply.get_msg_type() { + MsgType::Document => {}, + _ => unreachable!("Got '{:?}': should have been a document", reply.get_msg_type()), + } + } } diff --git a/src/lib.rs b/src/lib.rs index d5246f9..ffdfb2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,11 +13,94 @@ use queue::{Message, MsgType, Queue}; use session::Session; use uuid::Uuid; +pub enum ActionType { + Get, + Add, + Update, +} + #[derive(Clone, Debug)] pub enum ErrorType { DocumentNotFound, } +pub struct MTTReply { + document: String, + error_type: Option, +} + +impl MTTReply { + fn new(msg: Message) -> Self { + Self { + document: match msg.get_data("doc") { + Some(doc) => doc.to_string(), + None => "".to_string(), + }, + error_type: match msg.get_data("error_type") { + Some(err) => Some(err.to_error_type().unwrap()), + None => None, + }, + } + } + + pub fn get_document(&self) -> String { + self.document.clone() + } + + pub fn get_error(&self) -> Option { + self.error_type.clone() + } +} + +#[cfg(test)] +mod mtt_replies { + use super::*; + + #[test] + fn create_reply_with_no_error() { + let mut msg = Message::new(MsgType::Document); + let content = format!("content-{}", Uuid::new_v4()); + msg.add_data("doc", content.to_string()); + let reply = MTTReply::new(msg); + assert!(reply.get_error().is_none()); + assert_eq!(reply.get_document(), content); + } + + #[test] + fn create_reply_with_error() { + let mut msg = Message::new(MsgType::Error); + msg.add_data("error_type", ErrorType::DocumentNotFound); + let reply = MTTReply::new(msg); + match reply.get_error() { + Some(err) => match err { + ErrorType::DocumentNotFound => {}, + }, + None => unreachable!("should return an error type"), + } + assert_eq!(reply.get_document(), ""); + } + + #[test] + fn no_error() { + let msg = Message::new(MsgType::Document); + let reply = MTTReply::new(msg); + assert!(reply.get_error().is_none()); + } + + #[test] + fn some_error() { + let mut msg = Message::new(MsgType::Error); + msg.add_data("error_type", ErrorType::DocumentNotFound); + let reply = MTTReply::new(msg); + match reply.get_error() { + Some(err) => match err { + ErrorType::DocumentNotFound => {}, + }, + None => unreachable!("should return an error type"), + } + } +} + #[derive(Clone)] pub struct MoreThanText { client_channel: ClientChannel, @@ -48,11 +131,55 @@ impl MoreThanText { reply.get_data("sess_id").unwrap().to_uuid().unwrap() } - pub fn get_document(&self, sess_id: Uuid) -> String { + pub fn get_document(&self, sess_id: Uuid, action: ActionType, doc_name: S) -> MTTReply where S: Into { let mut msg = Message::new(MsgType::DocumentRequest); msg.add_data("sess_id", sess_id); + msg.add_data("name", doc_name.into()); let rx = self.client_channel.send(msg); let reply = rx.recv().unwrap(); - reply.get_data("doc").unwrap().to_string() + MTTReply::new(reply) + } +} + +#[cfg(test)] +mod mtt { + use super::*; + + #[test] + fn session_id_is_unique() { + let mut mtt = MoreThanText::new(); + let input: Option = None; + let mut ids: Vec = Vec::new(); + for _ in 0..10 { + let id = mtt.validate_session(input.clone()); + assert!(!ids.contains(&id)); + ids.push(id); + } + } + + #[test] + fn reuse_existing_session() { + let mut mtt = MoreThanText::new(); + let initial: Option = None; + let id = mtt.validate_session(initial); + let output = mtt.validate_session(Some(id.clone())); + assert_eq!(output, id); + } + + #[test] + fn get_root_document_with_str() { + let mut mtt = MoreThanText::new(); + let id = mtt.validate_session(Some(Uuid::new_v4())); + let output = mtt.get_document(id, ActionType::Get, "root"); + assert!(output.get_error().is_none()); + } + + #[test] + fn get_root_document_with_string() { + let mut mtt = MoreThanText::new(); + let id = mtt.validate_session(Some(Uuid::new_v4())); + let output = mtt.get_document(id, ActionType::Get, "root".to_string()); + assert!(output.get_error().is_none()); + } } diff --git a/src/main.rs b/src/main.rs index 188f9c9..de8606f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use axum::{ extract::{Extension, FromRequestParts, State}, - http::request::Parts, + http::{request::Parts, StatusCode}, response::IntoResponse, routing::{get, post}, RequestPartsExt, Router, }; use clap::Parser; -use morethantext::MoreThanText; +use morethantext::{ActionType, MoreThanText}; use std::convert::Infallible; use tokio::{spawn, sync::mpsc::channel}; use tower_cookies::{Cookie, CookieManagerLayer, Cookies}; @@ -83,10 +83,14 @@ where async fn mtt_conn(sess_id: SessionID, state: State) -> impl IntoResponse { let (tx, mut rx) = channel(1); spawn(async move { - tx.send(state.get_document(sess_id.0)).await.unwrap(); + tx.send(state.get_document(sess_id.0, ActionType::Get, "root")).await.unwrap(); }); - let content = rx.recv().await.unwrap(); - content + let reply = rx.recv().await.unwrap(); + let status = match reply.get_error() { + Some(_) => StatusCode::NOT_FOUND, + None => StatusCode::OK, + }; + (status, reply.get_document()) } #[cfg(test)] @@ -97,7 +101,7 @@ mod servers { http::{ header::{COOKIE, SET_COOKIE}, Method, - Request, StatusCode, + Request, }, }; use tower::ServiceExt;