add test with warp
cargo devel CI / cargo CI (push) Failing after 1m46s
Details
cargo devel CI / cargo CI (push) Failing after 1m46s
Details
This commit is contained in:
parent
77ced4b277
commit
b17fcb8c41
|
@ -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 })
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue