add test with warp
cargo devel CI / cargo CI (push) Failing after 1m46s Details

This commit is contained in:
Christoph J. Scherr 2024-03-08 19:49:40 +01:00
parent 77ced4b277
commit b17fcb8c41
Signed by: PlexSheep
GPG Key ID: 7CDD0B14851A08EF
2 changed files with 100 additions and 13 deletions

View File

@ -4,7 +4,9 @@ use crate::{store::Sequence, Item, Store};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
const ALPHABET: &str = "qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"; pub const ALPHABET: &str = "qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM";
pub const TOK_LEN: usize = 40;
pub const ID_LEN: usize = 20;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Client { pub struct Client {
@ -61,20 +63,24 @@ impl Id {
pub fn new() -> Self { pub fn new() -> Self {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut data = ALPHABET.to_string().into_bytes(); let mut data = ALPHABET.to_string().into_bytes();
data.repeat(ID_LEN);
data.shuffle(&mut rng); data.shuffle(&mut rng);
Self { Self {
inner: String::from_utf8(data[..20].into()).unwrap(), inner: String::from_utf8(data[..ID_LEN].into()).unwrap(),
} }
} }
pub fn len(&self) -> usize {
self.inner.len()
}
} }
impl FromStr for Id { impl FromStr for Id {
type Err = Infallible; type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self { let s = s.replace('"', "");
inner: s.to_string(), Ok(Self { inner: s })
})
} }
} }
@ -84,7 +90,7 @@ impl Display for Id {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Token { pub struct Token {
#[serde(rename = "token")] #[serde(rename = "token")]
inner: String, inner: String,
@ -94,19 +100,29 @@ impl Token {
pub fn new() -> Self { pub fn new() -> Self {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut data = ALPHABET.to_string().into_bytes(); let mut data = ALPHABET.to_string().into_bytes();
data.repeat(TOK_LEN);
data.shuffle(&mut rng); data.shuffle(&mut rng);
Self { Self {
inner: String::from_utf8(data).unwrap(), inner: String::from_utf8(data[..TOK_LEN].to_vec()).unwrap(),
} }
} }
pub fn len(&self) -> usize {
self.inner.len()
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
} }
impl FromStr for Token { impl FromStr for Token {
type Err = Infallible; type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self { let s = s.replace('"', "");
inner: s.to_string(), Ok(Self { inner: s })
})
} }
} }

View File

@ -5,7 +5,8 @@ use warp::{
filters::BoxedFilter, filters::BoxedFilter,
http::StatusCode, http::StatusCode,
reject::{MissingHeader, Rejection}, reject::{MissingHeader, Rejection},
reply, Filter, Reply, reply::{self, Json},
Filter, Reply,
}; };
use crate::{Client, Id, Store, StoreErr, Token}; use crate::{Client, Id, Store, StoreErr, Token};
@ -62,7 +63,7 @@ pub async fn item_getter(
} }
// GET /api/v1/items // GET /api/v1/items
pub fn get_items(store: Store) -> BoxedFilter<(impl Reply,)> { pub fn get_items(store: Store) -> impl Filter<Extract = (Json,), Error = warp::Rejection> + Clone {
warp::path!("api" / "v1" / "items") warp::path!("api" / "v1" / "items")
.and(warp::get()) .and(warp::get())
.and(warp::query::<HashMap<String, String>>()) .and(warp::query::<HashMap<String, String>>())
@ -74,7 +75,9 @@ pub fn get_items(store: Store) -> BoxedFilter<(impl Reply,)> {
} }
// GET /api/v1/register // GET /api/v1/register
pub fn get_register(store: Store) -> BoxedFilter<(impl Reply,)> { pub fn get_register(
store: Store,
) -> impl Filter<Extract = (Json,), Error = warp::Rejection> + Clone {
warp::path!("api" / "v1" / "register") warp::path!("api" / "v1" / "register")
.and(warp::get()) .and(warp::get())
.and(with_store(store)) .and(with_store(store))
@ -88,3 +91,71 @@ pub fn get_register(store: Store) -> BoxedFilter<(impl Reply,)> {
}) })
.boxed() .boxed()
} }
mod test {
use warp::{
http::StatusCode,
hyper::{self, body::*},
reply::{Json, Reply},
};
use std::str::FromStr;
#[tokio::test]
async fn test_register_and_get() {
let store = crate::Store::new();
let filter = super::get_register(store.clone());
let response: warp::reply::Response = warp::test::request()
.path("/api/v1/register")
.filter(&filter)
.await
.unwrap()
.into_response();
assert_eq!(response.status(), StatusCode::OK);
let body_raw: Bytes = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body_json: serde_json::Value = serde_json::from_slice(&body_raw).unwrap();
assert!(body_json.is_object());
let id: crate::Id = crate::Id::from_str(
&body_json
.get("id")
.expect("response has no field 'id'")
.to_owned()
.to_string(),
)
.unwrap();
let token: crate::Token = crate::Token::from_str(
&body_json
.get("token")
.expect("response has no field 'token'")
.to_owned()
.to_string(),
)
.unwrap();
assert_eq!(id.len(), crate::ID_LEN);
assert_eq!(token.len(), crate::TOK_LEN);
let filter = super::get_items(store);
let response: warp::reply::Response = warp::test::request()
.path(format!("/api/v1/items/?id={id}").as_str())
.header("Token", token.to_string())
.filter(&filter)
.await
.unwrap()
.into_response();
assert_eq!(response.status(), StatusCode::OK);
let body_raw: Bytes = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body_json: serde_json::Value = serde_json::from_slice(&body_raw).unwrap();
assert!(body_json.is_array());
for i in 0..2 {
assert!(body_json[i].is_object());
let item: crate::Item =
serde_json::from_value(body_json[i].clone().take()).unwrap();
assert_eq!(item.seq, i);
}
}
}