Add tests recipe_view and user_edit

This commit is contained in:
Greg Burri 2025-04-30 12:00:41 +02:00
parent 710a134966
commit ee4c2038b8
3 changed files with 132 additions and 56 deletions

View file

@ -13,13 +13,15 @@ use crate::{
#[derive(Debug, Display)] #[derive(Debug, Display)]
pub enum SignUpResult { pub enum SignUpResult {
UserAlreadyExists, UserAlreadyExists,
UserCreatedWaitingForValidation(String), // Validation token. /// Validation token.
UserCreatedWaitingForValidation(String),
} }
#[derive(Debug, Display)] #[derive(Debug, Display)]
pub enum UpdateUserResult { pub enum UpdateUserResult {
EmailAlreadyTaken, EmailAlreadyTaken,
UserUpdatedWaitingForRevalidation(String), // Validation token. /// Validation token.
UserUpdatedWaitingForRevalidation(String),
Ok, Ok,
} }
@ -27,7 +29,7 @@ pub enum UpdateUserResult {
pub enum ValidationResult { pub enum ValidationResult {
UnknownUser, UnknownUser,
ValidationExpired, ValidationExpired,
/// Returns token and user id. /// Returns login token and user id.
Ok(String, i64), Ok(String, i64),
} }
@ -36,7 +38,7 @@ pub enum SignInResult {
UserNotFound, UserNotFound,
WrongPassword, WrongPassword,
AccountNotValidated, AccountNotValidated,
/// Returns token and user id. /// Returns login token and user id.
Ok(String, i64), Ok(String, i64),
} }

View file

@ -1,16 +1,20 @@
use std::error::Error; use std::error::Error;
use axum_test::TestServer; use axum_test::TestServer;
use scraper::Html; use cookie::Cookie;
use scraper::{ElementRef, Html, Selector};
use recipes::{app, config, data::db, log}; use recipes::app;
mod utils;
#[tokio::test] #[tokio::test]
async fn homepage() -> Result<(), Box<dyn Error>> { async fn homepage() -> Result<(), Box<dyn Error>> {
// Arrange. // Arrange.
let state = common_state().await?; let state = utils::common_state().await?;
let user_id = create_user(&state.db_connection, "president@spaceball.planet", "12345").await?; let user_id =
let _recipe_id = create_recipe(&state.db_connection, user_id, "spaghetti").await?; utils::create_user(&state.db_connection, "president@spaceball.planet", "12345").await?;
let _recipe_id = utils::create_recipe(&state.db_connection, user_id, "spaghetti").await?;
let server = TestServer::new(app::make_service(state))?; let server = TestServer::new(app::make_service(state))?;
// Act. // Act.
@ -18,61 +22,73 @@ async fn homepage() -> Result<(), Box<dyn Error>> {
// Assert. // Assert.
response.assert_status_ok(); response.assert_status_ok();
// TODO: check if 'spaghetti' is in the list.
let document = Html::parse_document(&response.text()); let document = Html::parse_document(&response.text());
assert_eq!(document.errors.len(), 0); assert_eq!(document.errors.len(), 0);
// println!("{:?}", document.errors); let first_recipe_title =
// println!("{:?}", response); Selector::parse("#recipes-list .recipes-list-public .recipe-item").unwrap();
let elements: Vec<ElementRef> = document.select(&first_recipe_title).collect();
assert_eq!(elements.len(), 1);
assert_eq!(elements[0].inner_html(), "spaghetti");
Ok(()) Ok(())
} }
async fn create_user( #[tokio::test]
db_connection: &db::Connection, async fn recipe_view() -> Result<(), Box<dyn Error>> {
email: &str, // Arrange.
password: &str, let state = utils::common_state().await?;
) -> Result<i64, Box<dyn Error>> { let user_id =
if let db::user::SignUpResult::UserCreatedWaitingForValidation(token) = db_connection utils::create_user(&state.db_connection, "president@spaceball.planet", "12345").await?;
.sign_up(email, password, chrono::Weekday::Mon) let recipe_id = utils::create_recipe(&state.db_connection, user_id, "spaghetti").await?;
.await? let server = TestServer::new(app::make_service(state))?;
{
if let db::user::ValidationResult::Ok(_, user_id) = db_connection // Act.
.validation( let response = server.get(&format!("/recipe/view/{}", recipe_id)).await;
&token,
chrono::Duration::hours(1), // Assert.
"127.0.0.1", response.assert_status_ok();
"Mozilla/5.0",
) let document = Html::parse_document(&response.text());
.await? assert_eq!(document.errors.len(), 0);
{
Ok(user_id) let recipe_title = Selector::parse("#recipe-view .recipe-title").unwrap();
} else { let elements: Vec<ElementRef> = document.select(&recipe_title).collect();
Err(Box::<dyn Error>::from("Unable to validate user")) assert_eq!(elements.len(), 1);
} assert_eq!(elements[0].inner_html(), "spaghetti");
} else {
Err(Box::<dyn Error>::from("Unable to sign up")) Ok(())
}
} }
async fn create_recipe( #[tokio::test]
db_connection: &db::Connection, async fn user_edit() -> Result<(), Box<dyn Error>> {
user_id: i64, // Arrange.
title: &str, let state = utils::common_state().await?;
) -> Result<i64, Box<dyn Error>> { let _user_id =
let recipe_id = db_connection.create_recipe(user_id).await?; utils::create_user(&state.db_connection, "president@spaceball.planet", "12345").await?;
db_connection.set_recipe_title(recipe_id, title).await?; let token = utils::sign_in(&state.db_connection, "president@spaceball.planet", "12345").await?;
db_connection.set_recipe_is_public(recipe_id, true).await?; let server = TestServer::new(app::make_service(state))?;
Ok(recipe_id)
}
async fn common_state() -> Result<app::AppState, Box<dyn Error>> { // Act.
let db_connection = db::Connection::new_in_memory().await?; let response = server
let config = config::Config::default(); .get("/user/edit")
let log = log::Log::new_stdout_only(); .add_cookie(Cookie::new("auth_token", token))
Ok(app::AppState { .await;
config,
db_connection, // Assert.
log, response.assert_status_ok();
})
let document = Html::parse_document(&response.text());
assert_eq!(document.errors.len(), 0);
let user_email = Selector::parse("#input-email").unwrap();
let elements: Vec<ElementRef> = document.select(&user_email).collect();
assert_eq!(elements.len(), 1);
assert_eq!(
elements[0].attr("value").unwrap(),
"president@spaceball.planet"
);
Ok(())
} }

58
backend/tests/utils.rs Normal file
View file

@ -0,0 +1,58 @@
use std::error::Error;
use recipes::{app, config, data::db, log};
pub async fn common_state() -> Result<app::AppState, Box<dyn Error>> {
let db_connection = db::Connection::new_in_memory().await?;
let config = config::Config::default();
let log = log::Log::new_stdout_only();
Ok(app::AppState {
config,
db_connection,
log,
})
}
pub async fn create_user(
db_connection: &db::Connection,
email: &str,
password: &str,
) -> Result<i64, Box<dyn Error>> {
if let db::user::SignUpResult::UserCreatedWaitingForValidation(token) = db_connection
.sign_up(email, password, chrono::Weekday::Mon)
.await?
{
if let db::user::ValidationResult::Ok(_, user_id) = db_connection
.validation(&token, chrono::Duration::hours(1), "", "")
.await?
{
Ok(user_id)
} else {
Err(Box::<dyn Error>::from("Unable to validate user"))
}
} else {
Err(Box::<dyn Error>::from("Unable to sign up"))
}
}
pub async fn sign_in(
db_connection: &db::Connection,
email: &str,
password: &str,
) -> Result<String, Box<dyn Error>> {
match db_connection.sign_in(email, password, "", "").await? {
db::user::SignInResult::Ok(token, _) => Ok(token),
_ => Err(Box::<dyn Error>::from("Unable to sign in")),
}
}
pub async fn create_recipe(
db_connection: &db::Connection,
user_id: i64,
title: &str,
) -> Result<i64, Box<dyn Error>> {
let recipe_id = db_connection.create_recipe(user_id).await?;
db_connection.set_recipe_title(recipe_id, title).await?;
db_connection.set_recipe_is_public(recipe_id, true).await?;
Ok(recipe_id)
}