Admin dashboard and sessions
This commit is contained in:
34
tests/api/admin_dashboard.rs
Normal file
34
tests/api/admin_dashboard.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use crate::helpers::{TestApp, assert_is_redirect_to};
|
||||
|
||||
#[tokio::test]
|
||||
async fn you_must_be_logged_in_to_access_the_admin_dashboard() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let response = app.get_admin_dashboard().await;
|
||||
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn logout_clears_session_state() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let login_body = serde_json::json!({
|
||||
"username": &app.test_user.username,
|
||||
"password": &app.test_user.password,
|
||||
});
|
||||
let response = app.post_login(&login_body).await;
|
||||
assert_is_redirect_to(&response, "/admin/dashboard");
|
||||
|
||||
let html_page = app.get_admin_dashboard_html().await;
|
||||
assert!(html_page.contains(&format!("Welcome {}", app.test_user.username)));
|
||||
|
||||
let response = app.post_logout().await;
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
|
||||
let html_page = app.get_login_html().await;
|
||||
assert!(html_page.contains("You have successfully logged out"));
|
||||
|
||||
let response = app.get_admin_dashboard().await;
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
}
|
||||
115
tests/api/change_password.rs
Normal file
115
tests/api/change_password.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::helpers::{TestApp, assert_is_redirect_to};
|
||||
|
||||
#[tokio::test]
|
||||
async fn you_must_be_logged_in_to_see_the_change_password_form() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let response = app.get_change_password().await;
|
||||
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn you_must_be_logged_in_to_change_your_password() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let new_password = Uuid::new_v4().to_string();
|
||||
let response = app
|
||||
.post_change_password(&serde_json::json!({
|
||||
"current_password": Uuid::new_v4().to_string(),
|
||||
"new_password": new_password,
|
||||
"new_password_check": new_password,
|
||||
}))
|
||||
.await;
|
||||
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn new_password_fields_must_match() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
app.post_login(&serde_json::json!({
|
||||
"username": app.test_user.username,
|
||||
"password": app.test_user.password,
|
||||
}))
|
||||
.await;
|
||||
|
||||
let new_password = Uuid::new_v4().to_string();
|
||||
let another_new_password = Uuid::new_v4().to_string();
|
||||
let response = app
|
||||
.post_change_password(&serde_json::json!({
|
||||
"current_password": app.test_user.password,
|
||||
"new_password": new_password,
|
||||
"new_password_check": another_new_password,
|
||||
}))
|
||||
.await;
|
||||
assert_is_redirect_to(&response, "/admin/password");
|
||||
|
||||
let html_page = app.get_change_password_html().await;
|
||||
assert!(html_page.contains("You entered two different passwords"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn current_password_is_invalid() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
app.post_login(&serde_json::json!({
|
||||
"username": app.test_user.username,
|
||||
"password": app.test_user.password,
|
||||
}))
|
||||
.await;
|
||||
|
||||
let new_password = Uuid::new_v4().to_string();
|
||||
let response = app
|
||||
.post_change_password(&serde_json::json!({
|
||||
"current_password": Uuid::new_v4().to_string(),
|
||||
"new_password": new_password,
|
||||
"new_password_check": new_password,
|
||||
}))
|
||||
.await;
|
||||
assert_is_redirect_to(&response, "/admin/password");
|
||||
|
||||
let html_page = app.get_change_password_html().await;
|
||||
assert!(html_page.contains("The current password is incorrect"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn changing_password_works() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let login_body = &serde_json::json!({
|
||||
"username": app.test_user.username,
|
||||
"password": app.test_user.password,
|
||||
});
|
||||
let response = app.post_login(login_body).await;
|
||||
assert_is_redirect_to(&response, "/admin/dashboard");
|
||||
|
||||
let new_password = Uuid::new_v4().to_string();
|
||||
let response = app
|
||||
.post_change_password(&serde_json::json!({
|
||||
"current_password": app.test_user.password,
|
||||
"new_password": new_password,
|
||||
"new_password_check": new_password,
|
||||
}))
|
||||
.await;
|
||||
assert_is_redirect_to(&response, "/admin/password");
|
||||
|
||||
let html_page = app.get_change_password_html().await;
|
||||
assert!(html_page.contains("Your password has been changed"));
|
||||
|
||||
let response = app.post_logout().await;
|
||||
assert_is_redirect_to(&response, "/login");
|
||||
|
||||
let html_page = app.get_login_html().await;
|
||||
assert!(html_page.contains("You have successfully logged out"));
|
||||
|
||||
let login_body = &serde_json::json!({
|
||||
"username": app.test_user.username,
|
||||
"password": new_password,
|
||||
});
|
||||
let response = app.post_login(login_body).await;
|
||||
assert_is_redirect_to(&response, "/admin/dashboard");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use argon2::{
|
||||
Argon2, PasswordHasher,
|
||||
Algorithm, Argon2, Params, PasswordHasher, Version,
|
||||
password_hash::{SaltString, rand_core::OsRng},
|
||||
};
|
||||
use linkify::LinkFinder;
|
||||
@@ -43,10 +43,14 @@ impl TestUser {
|
||||
|
||||
pub async fn store(&self, connection_pool: &PgPool) {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let password_hash = Argon2::default()
|
||||
.hash_password(self.password.as_bytes(), &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let password_hash = Argon2::new(
|
||||
Algorithm::Argon2id,
|
||||
Version::V0x13,
|
||||
Params::new(1500, 2, 1, None).unwrap(),
|
||||
)
|
||||
.hash_password(self.password.as_bytes(), &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
sqlx::query!(
|
||||
"INSERT INTO users (user_id, username, password_hash) VALUES ($1, $2, $3)",
|
||||
self.user_id,
|
||||
@@ -144,6 +148,30 @@ impl TestApp {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_admin_dashboard(&self) -> reqwest::Response {
|
||||
self.api_client
|
||||
.get(format!("{}/admin/dashboard", &self.address))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to execute request")
|
||||
}
|
||||
|
||||
pub async fn get_admin_dashboard_html(&self) -> String {
|
||||
self.get_admin_dashboard().await.text().await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_change_password(&self) -> reqwest::Response {
|
||||
self.api_client
|
||||
.get(format!("{}/admin/password", &self.address))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to execute request")
|
||||
}
|
||||
|
||||
pub async fn get_change_password_html(&self) -> String {
|
||||
self.get_change_password().await.text().await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn post_subscriptions(&self, body: String) -> reqwest::Response {
|
||||
self.api_client
|
||||
.post(format!("{}/subscriptions", self.address))
|
||||
@@ -173,7 +201,27 @@ impl TestApp {
|
||||
.form(body)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to execute request")
|
||||
.expect("failed to execute request")
|
||||
}
|
||||
|
||||
pub async fn post_logout(&self) -> reqwest::Response {
|
||||
self.api_client
|
||||
.post(format!("{}/admin/logout", self.address))
|
||||
.send()
|
||||
.await
|
||||
.expect("failed to execute request")
|
||||
}
|
||||
|
||||
pub async fn post_change_password<Body>(&self, body: &Body) -> reqwest::Response
|
||||
where
|
||||
Body: serde::Serialize,
|
||||
{
|
||||
self.api_client
|
||||
.post(format!("{}/admin/password", self.address))
|
||||
.form(body)
|
||||
.send()
|
||||
.await
|
||||
.expect("failed to execute request")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,3 +20,19 @@ async fn an_error_flash_message_is_set_on_failure() {
|
||||
let login_page_html = app.get_login_html().await;
|
||||
assert!(!login_page_html.contains("Authentication failed"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn login_redirects_to_admin_dashboard_after_login_success() {
|
||||
let app = TestApp::spawn().await;
|
||||
|
||||
let login_body = serde_json::json!({
|
||||
"username": &app.test_user.username,
|
||||
"password": &app.test_user.password
|
||||
});
|
||||
|
||||
let response = app.post_login(&login_body).await;
|
||||
assert_is_redirect_to(&response, "/admin/dashboard");
|
||||
|
||||
let html_page = app.get_admin_dashboard_html().await;
|
||||
assert!(html_page.contains(&format!("Welcome {}", app.test_user.username)));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
mod admin_dashboard;
|
||||
mod change_password;
|
||||
mod health_check;
|
||||
mod helpers;
|
||||
mod login;
|
||||
|
||||
Reference in New Issue
Block a user