Skip to content

Commit

Permalink
more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JieningYu committed Dec 27, 2023
1 parent 828cc44 commit d4c4204
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions src/handle/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub struct LoginReq {
pub password: String,
}

#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct LoginRes {
pub id: u64,
pub token: String,
Expand Down Expand Up @@ -167,7 +167,11 @@ pub async fn reset_password<Io: IoHandle>(
let unverified = Unverified::new(email.to_string())?;
let select = sa!(worlds.account, unverified.email_hash());
let mut lazy = ga!(select, unverified.email_hash()).ok_or(Error::PermissionDenied)?;
lazy.get_mut().await?.reset_password(captcha, new_password)
let account = lazy.get_mut().await?;
account.reset_password(captcha, new_password)?;
// Clear all tokens after reseting password
account.clear_tokens();
Ok(())
}

#[derive(Serialize)]
Expand Down
8 changes: 6 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ fn main() {}
mod routes {
pub const SEND_CAPTCHA: &str = "/account/send-captcha";
pub const REGISTER: &str = "/account/register";
pub const LOGIN: &str = "/account/send-reset-password-captcha";
pub const LOGIN: &str = "/account/login";
pub const SEND_RESET_PASSWORD_CAPTCHA: &str = "/account/send-reset-password-captcha";
pub const RESET_PASSWORD: &str = "/account/reset-password";
pub const SELF_ACCOUNT_INFO: &str = "/account/self-info";
pub const MODIFY_ACCOUNT: &str = "/account/modify";
Expand Down Expand Up @@ -49,13 +50,15 @@ pub struct Worlds<Io: IoHandle> {

mod handle {
/// Selects an account.
#[macro_export]
macro_rules! sa {
($w:expr, $id:expr) => {
$w.select(0, $id).hint($id)
};
}

/// Gets an account from selection.
#[macro_export]
macro_rules! ga {
($s:expr, $id:expr) => {{
let mut iter = $s.iter();
Expand All @@ -70,9 +73,10 @@ mod handle {
}

/// Validates an account.
#[macro_export]
macro_rules! va {
($a:expr, $s:expr => $($p:expr),*$(,)?) => {{
let lazy = ga!($s, $a.account).ok_or(Error::PermissionDenied)?;
let lazy = ga!($s, $a.account).ok_or(crate::Error::PermissionDenied)?;
let a = lazy.get().await?;
if a.is_token_valid(&$a.token) {
let _tags = a.tags();
Expand Down
118 changes: 110 additions & 8 deletions src/tests/account.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use std::time::Duration;

use dmds::StreamExt;
use libaccount::Phone;
use serde_json::json;
use sms4_backend::account::{Account, Permission, Tag, TagEntry};

use crate::{routes::*, tests::router};
use crate::{ga, handle::account::LoginRes, routes::*, sa, tests::router, va, Auth};

/// Send captcha and register an account.
#[tokio::test]
async fn creation() {
let (state, route) = router();
let res = req!(route => SEND_CAPTCHA,
json!({ "email": "[email protected]" }) => json
);
assert!(!res.status().is_success());
let res = req!(route => SEND_CAPTCHA,
json!({ "email": "[email protected]" }) => json
);
Expand Down Expand Up @@ -47,10 +49,6 @@ async fn creation() {
"password": "shanlilinghuo",
"captcha": captcha,
"tags": [
{
"entry": "Permission",
"tag": "Op"
},
{
"entry": "Department",
"tag": "SubIT"
Expand Down Expand Up @@ -96,5 +94,109 @@ async fn creation() {
#[tokio::test]
async fn login() {
let (state, route) = router();
init_acc!(state, DCK);
state.worlds.account.insert(acc_exp!(DCK)).await.unwrap();

// Login with wrong password
let res = req!(route => LOGIN,
json!({
"email": "[email protected]",
"password": "123456",
}) => json
);
assert!(!res.status().is_success());

// Login successfully
let res = req!(route => LOGIN,
json!({
"email": "[email protected]",
"password": "shanlilinghuo",
}) => json
);
assert!(res.status().is_success());
let LoginRes { id, token, .. } = p_json!(res);
let auth = Auth { account: id, token };

let select = sa!(state.worlds.account, id);
assert!((|| async { Ok(va!(auth, select)) })().await.is_ok());
}

#[tokio::test]
async fn logout() {
let (state, route) = router();
let mut account: Account = acc_exp!(DCK);
let id = account.id();
let (token0, _) = account.login("shanlilinghuo").unwrap();
let (token1, _) = account.login("shanlilinghuo").unwrap();
state.worlds.account.insert(account).await.unwrap();

let auth_wrong = Auth {
account: id,
token: "wrong_token".to_owned(),
};
let auth = Auth {
account: id,
token: token0,
};
let wrong_res = req!(route => LOGOUT, auth_wrong, axum::body::Body::empty() => bytes);
assert!(!wrong_res.status().is_success());
let select = sa!(state.worlds.account, id);
assert!((|| async { Ok(va!(auth, select)) })().await.is_ok());

let res = req!(route => LOGOUT, auth, axum::body::Body::empty() => bytes);
assert!(res.status().is_success());
assert!((|| async { Ok(va!(auth, select)) })().await.is_err());
let auth1 = Auth {
account: id,
token: token1,
};
assert!((|| async { Ok(va!(auth1, select)) })().await.is_ok());
}

#[tokio::test]
async fn reset_password() {
let (state, route) = router();
let account: Account = acc_exp!(DCK);
let id = account.id();
state.worlds.account.insert(account).await.unwrap();

let res = req!(route => SEND_RESET_PASSWORD_CAPTCHA,
json!({
"email": "[email protected]",
}) => json
);
assert!(res.status().is_success());
let captcha = state
.test_cx
.captcha
.lock()
.await
.expect("captcha not sent");
let wrong_captcha = sms4_backend::account::verify::Captcha::from(captcha.into_inner() + 1);
let res = req!(route => RESET_PASSWORD,
json!({
"email": "[email protected]",
"captcha": wrong_captcha,
"new_password": "nerd",
}) => json
);
assert!(!res.status().is_success());
let select = sa!(state.worlds.account, id);
// Will dead lock if unblocked.
{
let lazy = ga!(select, id).unwrap();
let account = lazy.get().await.unwrap();
assert!(account.password_matches("shanlilinghuo"));
}

let res = req!(route => RESET_PASSWORD,
json!({
"email": "[email protected]",
"captcha": captcha,
"new_password": "NERD",
}) => json
);
assert!(res.status().is_success());
let lazy = ga!(select, id).unwrap();
let account = lazy.get().await.unwrap();
assert!(account.password_matches("NERD"));
}
93 changes: 59 additions & 34 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ fn router() -> (Global<MemStorage>, Router) {
.route(SEND_CAPTCHA, post(handle::account::send_captcha))
.route(REGISTER, post(handle::account::register))
.route(LOGIN, post(handle::account::login))
.route(
SEND_RESET_PASSWORD_CAPTCHA,
post(handle::account::send_reset_password_captcha),
)
.route(RESET_PASSWORD, post(handle::account::reset_password))
.route(SELF_ACCOUNT_INFO, post(handle::account::self_info))
.route(SELF_ACCOUNT_INFO, get(handle::account::self_info))
.route(MODIFY_ACCOUNT, post(handle::account::modify))
.route(LOGOUT, post(handle::account::logout))
.route(SET_PERMISSIONS, post(handle::account::set_permissions))
Expand Down Expand Up @@ -70,12 +74,16 @@ macro_rules! req {
.unwrap();
tower::ServiceExt::oneshot($r.clone(), req).await.unwrap()
};
($r:expr => $u:expr, $a:expr, $b:expr => bytes) => {
let mut b = Some(Request::builder().uri($u).method(axum::http::Method::POST));
$a.append_to_req_builder(&mut $b);
($r:expr => $u:expr, $a:expr, $b:expr => bytes) => {{
let mut b = Some(
axum::http::Request::builder()
.uri($u)
.method(axum::http::Method::POST),
);
$a.append_to_req_builder(&mut b);
let req = b.unwrap().body(axum::body::Body::from($b)).unwrap();
tower::ServiceExt::oneshot($r.clone(), req).await.unwrap()
};
}};
($r:expr => $u:expr, $b:expr => json) => {{
let req = axum::http::Request::builder()
.uri($u)
Expand All @@ -88,39 +96,56 @@ macro_rules! req {
.unwrap();
tower::ServiceExt::oneshot($r.clone(), req).await.unwrap()
}};
($r:expr => $u:expr, $b:expr => bytes) => {{
let req = axum::http::Request::builder()
.uri($u)
.method(axum::http::Method::POST)
.body(axum::body::Body::from($b))
.unwrap();
tower::ServiceExt::oneshot($r.clone(), req).await.unwrap()
}};
}

macro_rules! init_acc {
($s:expr, DCK, $($p:ident),*$(,)?) => {
$s
.worlds
.account
.insert(
libaccount::Account::new(
"[email protected]".to_owned(),
"Genshine Player".to_owned(),
2525505.to_string(),
Some(12345678901.into()),
Default::default(),
{
use sms4_backend::account::Tag;
let mut tags = libaccount::tag::Tags::new();
$(tags.insert(Tag::Permission(sms4_backend::account::Permission::$p));)*
tags.insert(Tag::Department("SubIT".to_owned()));
tags.insert(Tag::Department("击剑批".to_owned()));
tags.insert(Tag::House(libaccount::House::MingDe));
tags
},
"shanlilinghuo".to_owned(),
std::time::Duration::from_secs(60),
siphasher::sip::SipHasher24::new(),
)
.into(),
macro_rules! acc_exp {
(DCK, $($p:ident),*$(,)?) => {
libaccount::Account::new(
"[email protected]".to_owned(),
"Genshine Player".to_owned(),
2525505.to_string(),
Some(12345678901.into()),
Default::default(),
{
use sms4_backend::account::Tag;
let mut tags = libaccount::tag::Tags::new();
$(tags.insert(Tag::Permission(sms4_backend::account::Permission::$p));)*
tags.insert(Tag::Department("SubIT".to_owned()));
tags.insert(Tag::Department("击剑批".to_owned()));
tags.insert(Tag::House(libaccount::House::MingDe));
tags
},
"shanlilinghuo".to_owned(),
std::time::Duration::from_secs(60),
siphasher::sip::SipHasher24::new(),
)
.into()
};
($t:tt) => { acc_exp!($t,) }
}

macro_rules! p_json {
($r:expr) => {
serde_json::from_slice(
&http_body_util::BodyExt::collect($r.into_body())
.await
.unwrap()
.to_bytes(),
)
.await
.unwrap();
.unwrap()
};
($s:expr, $t:tt) => { init_acc!($s, $t,) }
($r:expr => $t:ty) => {{
let val: $t = p_json!($r);
val
}};
}

mod account;

0 comments on commit d4c4204

Please sign in to comment.