init
This commit is contained in:
371
tests/requests/auth.rs
Normal file
371
tests/requests/auth.rs
Normal file
@@ -0,0 +1,371 @@
|
||||
use insta::{assert_debug_snapshot, with_settings};
|
||||
use loco_rs::testing::prelude::*;
|
||||
use music_metadata_manager::{app::App, models::users};
|
||||
use rstest::rstest;
|
||||
use serial_test::serial;
|
||||
|
||||
use super::prepare_data;
|
||||
|
||||
// TODO: see how to dedup / extract this to app-local test utils
|
||||
// not to framework, because that would require a runtime dep on insta
|
||||
macro_rules! configure_insta {
|
||||
($($expr:expr),*) => {
|
||||
let mut settings = insta::Settings::clone_current();
|
||||
settings.set_prepend_module_to_snapshot(false);
|
||||
settings.set_snapshot_suffix("auth_request");
|
||||
let _guard = settings.bind_to_scope();
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_register() {
|
||||
configure_insta!();
|
||||
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
let email = "test@loco.com";
|
||||
let payload = serde_json::json!({
|
||||
"name": "loco",
|
||||
"email": email,
|
||||
"password": "12341234"
|
||||
});
|
||||
|
||||
let response = request.post("/api/auth/register").json(&payload).await;
|
||||
assert_eq!(
|
||||
response.status_code(),
|
||||
200,
|
||||
"Register request should succeed"
|
||||
);
|
||||
let saved_user = users::Model::find_by_email(&ctx.db, email).await;
|
||||
|
||||
with_settings!({
|
||||
filters => cleanup_user_model()
|
||||
}, {
|
||||
assert_debug_snapshot!(saved_user);
|
||||
});
|
||||
|
||||
let deliveries = ctx.mailer.unwrap().deliveries();
|
||||
assert_eq!(deliveries.count, 1, "Exactly one email should be sent");
|
||||
|
||||
// with_settings!({
|
||||
// filters => cleanup_email()
|
||||
// }, {
|
||||
// assert_debug_snapshot!(ctx.mailer.unwrap().deliveries());
|
||||
// });
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("login_with_valid_password", "12341234")]
|
||||
#[case("login_with_invalid_password", "invalid-password")]
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_login_with_verify(#[case] test_name: &str, #[case] password: &str) {
|
||||
configure_insta!();
|
||||
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
let email = "test@loco.com";
|
||||
let register_payload = serde_json::json!({
|
||||
"name": "loco",
|
||||
"email": email,
|
||||
"password": "12341234"
|
||||
});
|
||||
|
||||
//Creating a new user
|
||||
let register_response = request
|
||||
.post("/api/auth/register")
|
||||
.json(®ister_payload)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
register_response.status_code(),
|
||||
200,
|
||||
"Register request should succeed"
|
||||
);
|
||||
|
||||
let user = users::Model::find_by_email(&ctx.db, email).await.unwrap();
|
||||
let email_verification_token = user
|
||||
.email_verification_token
|
||||
.expect("Email verification token should be generated");
|
||||
request
|
||||
.get(&format!("/api/auth/verify/{email_verification_token}"))
|
||||
.await;
|
||||
|
||||
//verify user request
|
||||
let response = request
|
||||
.post("/api/auth/login")
|
||||
.json(&serde_json::json!({
|
||||
"email": email,
|
||||
"password": password
|
||||
}))
|
||||
.await;
|
||||
|
||||
// Make sure email_verified_at is set
|
||||
let user = users::Model::find_by_email(&ctx.db, email)
|
||||
.await
|
||||
.expect("Failed to find user by email");
|
||||
|
||||
assert!(
|
||||
user.email_verified_at.is_some(),
|
||||
"Expected the email to be verified, but it was not. User: {:?}",
|
||||
user
|
||||
);
|
||||
|
||||
with_settings!({
|
||||
filters => cleanup_user_model()
|
||||
}, {
|
||||
assert_debug_snapshot!(test_name, (response.status_code(), response.text()));
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_login_without_verify() {
|
||||
configure_insta!();
|
||||
|
||||
request::<App, _, _>(|request, _ctx| async move {
|
||||
let email = "test@loco.com";
|
||||
let password = "12341234";
|
||||
let register_payload = serde_json::json!({
|
||||
"name": "loco",
|
||||
"email": email,
|
||||
"password": password
|
||||
});
|
||||
|
||||
//Creating a new user
|
||||
let register_response = request
|
||||
.post("/api/auth/register")
|
||||
.json(®ister_payload)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
register_response.status_code(),
|
||||
200,
|
||||
"Register request should succeed"
|
||||
);
|
||||
|
||||
//verify user request
|
||||
let login_response = request
|
||||
.post("/api/auth/login")
|
||||
.json(&serde_json::json!({
|
||||
"email": email,
|
||||
"password": password
|
||||
}))
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
login_response.status_code(),
|
||||
200,
|
||||
"Login request should succeed"
|
||||
);
|
||||
|
||||
with_settings!({
|
||||
filters => cleanup_user_model()
|
||||
}, {
|
||||
assert_debug_snapshot!(login_response.text());
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_reset_password() {
|
||||
configure_insta!();
|
||||
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
let login_data = prepare_data::init_user_login(&request, &ctx).await;
|
||||
|
||||
let forgot_payload = serde_json::json!({
|
||||
"email": login_data.user.email,
|
||||
});
|
||||
let forget_response = request.post("/api/auth/forgot").json(&forgot_payload).await;
|
||||
assert_eq!(
|
||||
forget_response.status_code(),
|
||||
200,
|
||||
"Forget request should succeed"
|
||||
);
|
||||
|
||||
let user = users::Model::find_by_email(&ctx.db, &login_data.user.email)
|
||||
.await
|
||||
.expect("Failed to find user by email");
|
||||
|
||||
assert!(
|
||||
user.reset_token.is_some(),
|
||||
"Expected reset_token to be set, but it was None. User: {user:?}"
|
||||
);
|
||||
assert!(
|
||||
user.reset_sent_at.is_some(),
|
||||
"Expected reset_sent_at to be set, but it was None. User: {user:?}"
|
||||
);
|
||||
|
||||
let new_password = "new-password";
|
||||
let reset_payload = serde_json::json!({
|
||||
"token": user.reset_token,
|
||||
"password": new_password,
|
||||
});
|
||||
|
||||
let reset_response = request.post("/api/auth/reset").json(&reset_payload).await;
|
||||
assert_eq!(
|
||||
reset_response.status_code(),
|
||||
200,
|
||||
"Reset password request should succeed"
|
||||
);
|
||||
|
||||
let user = users::Model::find_by_email(&ctx.db, &user.email)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(user.reset_token.is_none());
|
||||
assert!(user.reset_sent_at.is_none());
|
||||
|
||||
assert_debug_snapshot!(reset_response.text());
|
||||
|
||||
let login_response = request
|
||||
.post("/api/auth/login")
|
||||
.json(&serde_json::json!({
|
||||
"email": user.email,
|
||||
"password": new_password
|
||||
}))
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
login_response.status_code(),
|
||||
200,
|
||||
"Login request should succeed"
|
||||
);
|
||||
|
||||
let deliveries = ctx.mailer.unwrap().deliveries();
|
||||
assert_eq!(deliveries.count, 2, "Exactly one email should be sent");
|
||||
// with_settings!({
|
||||
// filters => cleanup_email()
|
||||
// }, {
|
||||
// assert_debug_snapshot!(deliveries.messages);
|
||||
// });
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_get_current_user() {
|
||||
configure_insta!();
|
||||
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
let user = prepare_data::init_user_login(&request, &ctx).await;
|
||||
|
||||
let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
|
||||
let response = request
|
||||
.get("/api/auth/current")
|
||||
.add_header(auth_key, auth_value)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
response.status_code(),
|
||||
200,
|
||||
"Current request should succeed"
|
||||
);
|
||||
|
||||
with_settings!({
|
||||
filters => cleanup_user_model()
|
||||
}, {
|
||||
assert_debug_snapshot!((response.status_code(), response.text()));
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_auth_with_magic_link() {
|
||||
configure_insta!();
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
seed::<App>(&ctx).await.unwrap();
|
||||
|
||||
let payload = serde_json::json!({
|
||||
"email": "user1@example.com",
|
||||
});
|
||||
let response = request.post("/api/auth/magic-link").json(&payload).await;
|
||||
assert_eq!(
|
||||
response.status_code(),
|
||||
200,
|
||||
"Magic link request should succeed"
|
||||
);
|
||||
|
||||
let deliveries = ctx.mailer.unwrap().deliveries();
|
||||
assert_eq!(deliveries.count, 1, "Exactly one email should be sent");
|
||||
|
||||
// let redact_token = format!("[a-zA-Z0-9]{{{}}}", users::MAGIC_LINK_LENGTH);
|
||||
// with_settings!({
|
||||
// filters => {
|
||||
// let mut combined_filters = cleanup_email().clone();
|
||||
// combined_filters.extend(vec![(r"(\\r\\n|=\\r\\n)", ""), (redact_token.as_str(), "[REDACT_TOKEN]") ]);
|
||||
// combined_filters
|
||||
// }
|
||||
// }, {
|
||||
// assert_debug_snapshot!(deliveries.messages);
|
||||
// });
|
||||
|
||||
let user = users::Model::find_by_email(&ctx.db, "user1@example.com")
|
||||
.await
|
||||
.expect("User should be found");
|
||||
|
||||
let magic_link_token = user
|
||||
.magic_link_token
|
||||
.expect("Magic link token should be generated");
|
||||
let magic_link_response = request
|
||||
.get(&format!("/api/auth/magic-link/{magic_link_token}"))
|
||||
.await;
|
||||
assert_eq!(
|
||||
magic_link_response.status_code(),
|
||||
200,
|
||||
"Magic link authentication should succeed"
|
||||
);
|
||||
|
||||
with_settings!({
|
||||
filters => cleanup_user_model()
|
||||
}, {
|
||||
assert_debug_snapshot!(magic_link_response.text());
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_reject_invalid_email() {
|
||||
configure_insta!();
|
||||
request::<App, _, _>(|request, _ctx| async move {
|
||||
let invalid_email = "user1@temp-mail.com";
|
||||
let payload = serde_json::json!({
|
||||
"email": invalid_email,
|
||||
});
|
||||
let response = request.post("/api/auth/magic-link").json(&payload).await;
|
||||
assert_eq!(
|
||||
response.status_code(),
|
||||
400,
|
||||
"Expected request with invalid email '{invalid_email}' to be blocked, but it was allowed."
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn can_reject_invalid_magic_link_token() {
|
||||
configure_insta!();
|
||||
request::<App, _, _>(|request, ctx| async move {
|
||||
seed::<App>(&ctx).await.unwrap();
|
||||
|
||||
let magic_link_response = request.get("/api/auth/magic-link/invalid-token").await;
|
||||
assert_eq!(
|
||||
magic_link_response.status_code(),
|
||||
401,
|
||||
"Magic link authentication should be rejected"
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
Reference in New Issue
Block a user