Add some HTTP tests for recipe fields and recipe tags

This commit is contained in:
Greg Burri 2025-05-15 00:36:07 +02:00
parent 9c2d89e981
commit e4ec80e118
6 changed files with 179 additions and 11 deletions

View file

@ -115,7 +115,15 @@ pub async fn rm_tags(
ExtractRon(ron): ExtractRon<ron_api::Tags>,
) -> Result<impl IntoResponse> {
check_user_rights_recipe(&connection, &context.user, ron.recipe_id).await?;
connection.rm_recipe_tags(ron.recipe_id, &ron.tags).await?;
connection
.rm_recipe_tags(
ron.recipe_id,
&ron.tags
.into_iter()
.map(|tag| tag.to_lowercase())
.collect::<Vec<_>>(),
)
.await?;
Ok(StatusCode::OK)
}
@ -157,7 +165,7 @@ pub async fn set_language(
pub async fn set_is_public(
State(connection): State<db::Connection>,
Extension(context): Extension<Context>,
ExtractRon(ron): ExtractRon<ron_api::SetIsPublic>,
ExtractRon(ron): ExtractRon<ron_api::SetRecipeIsPublic>,
) -> Result<StatusCode> {
check_user_rights_recipe(&connection, &context.user, ron.recipe_id).await?;
connection

View file

@ -301,7 +301,65 @@ async fn create_recipe_and_edit_it() -> Result<(), Box<dyn Error>> {
.await;
response.assert_status_ok();
// TODO: Set other recipe fields.
let response = server
.patch("/ron-api/recipe/estimated_time")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::SetRecipeEstimatedTime {
recipe_id,
estimated_time: Some(420),
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
let response = server
.patch("/ron-api/recipe/difficulty")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::SetRecipeDifficulty {
recipe_id,
difficulty: ron_api::Difficulty::Hard,
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
let response = server
.patch("/ron-api/recipe/language")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::SetRecipeLanguage {
recipe_id,
lang: "fr".into(),
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
let response = server
.patch("/ron-api/recipe/is_public")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::SetRecipeIsPublic {
recipe_id,
is_public: true,
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
// Assert.
let response = server
@ -327,7 +385,112 @@ async fn create_recipe_and_edit_it() -> Result<(), Box<dyn Error>> {
let element_servings = document.select(&servings_selector).next().unwrap();
assert_eq!(element_servings.attr("value").unwrap(), "42");
// dbg!(response);
let estimated_time_selector = Selector::parse("#input-estimated-time").unwrap();
let element_estimated_time = document.select(&estimated_time_selector).next().unwrap();
assert_eq!(element_estimated_time.attr("value").unwrap(), "420");
let selected_difficulty_selector =
Selector::parse("#select-difficulty option[selected]").unwrap();
let element_selected_difficulty = document
.select(&selected_difficulty_selector)
.next()
.unwrap();
assert_eq!(element_selected_difficulty.inner_html(), "Hard");
let selected_language_selector = Selector::parse("#select-language option[selected]").unwrap();
let element_selected_language = document.select(&selected_language_selector).next().unwrap();
assert_eq!(element_selected_language.inner_html(), "Français");
let is_public_selector = Selector::parse("#input-is-public").unwrap();
let element_is_public = document.select(&is_public_selector).next().unwrap();
assert!(element_is_public.attr("checked").is_some());
Ok(())
}
#[tokio::test]
async fn recipe_tags() -> Result<(), Box<dyn Error>> {
// Arrange.
let state = utils::common_state().await?;
let user_id = utils::create_user(
&state.db_connection,
"president@spaceball.planet",
"12345678",
)
.await?;
let token = utils::sign_in(
&state.db_connection,
"president@spaceball.planet",
"12345678",
)
.await?;
let recipe_id = utils::create_recipe(&state.db_connection, user_id, "spaghetti").await?;
let server = TestServer::new(app::make_service(state))?;
let cookie = Cookie::new("auth_token", token);
// Act.
// Tags list must be empty.
let response = server
.get("/ron-api/recipe/tags")
.add_cookie(cookie.clone())
.add_query_param("id", recipe_id)
.await;
response.assert_status_ok();
let tags: ron_api::Tags = ron::de::from_bytes(response.as_bytes()).unwrap();
assert!(tags.tags.is_empty());
// Add some tags.
let response = server
.post("/ron-api/recipe/tags")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::Tags {
recipe_id,
tags: vec!["ABC".into(), "xyz".into()],
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
let response = server
.get("/ron-api/recipe/tags")
.add_cookie(cookie.clone())
.add_query_param("id", recipe_id)
.await;
response.assert_status_ok();
let tags: ron_api::Tags = ron::de::from_bytes(response.as_bytes()).unwrap();
assert_eq!(tags.tags.len(), 2);
assert!(tags.tags.contains(&"abc".to_string())); // Tags are in lower case.
assert!(tags.tags.contains(&"xyz".to_string()));
// Remove some tags.
let response = server
.delete("/ron-api/recipe/tags")
.add_cookie(cookie.clone())
.add_header(http::header::CONTENT_TYPE, common::consts::MIME_TYPE_RON)
.bytes(
ron_api::to_string(ron_api::Tags {
recipe_id,
tags: vec!["XYZ".into(), "qwe".into()],
})
.unwrap()
.into(),
)
.await;
response.assert_status_ok();
let response = server
.get("/ron-api/recipe/tags")
.add_cookie(cookie.clone())
.add_query_param("id", recipe_id)
.await;
response.assert_status_ok();
let tags: ron_api::Tags = ron::de::from_bytes(response.as_bytes()).unwrap();
assert_eq!(tags.tags.len(), 1);
assert_eq!(tags.tags[0], "abc".to_string());
Ok(())
}

View file

@ -13,7 +13,7 @@ pub async fn common_state_with_email_service(
) -> 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_no_log();
let log = log::Log::new_to_stdout_only_with_max_level(Some(tracing::Level::ERROR));
Ok(app::AppState {
config,
db_connection,

View file

@ -109,7 +109,7 @@ pub struct SetRecipeLanguage {
}
#[derive(Serialize, Deserialize, Clone)]
pub struct SetIsPublic {
pub struct SetRecipeIsPublic {
pub recipe_id: i64,
pub is_public: bool,
}

View file

@ -231,7 +231,7 @@ pub fn setup_page(recipe_id: i64) {
{
let is_public: HtmlInputElement = by_id("input-is-public");
EventListener::new(&is_public.clone(), "input", move |_event| {
let body = ron_api::SetIsPublic {
let body = ron_api::SetRecipeIsPublic {
recipe_id,
is_public: is_public.checked(),
};

View file

@ -59,10 +59,7 @@ where
{
let mut url = format!("/ron-api/{}?", api_name);
serde_html_form::ser::push_to_string(&mut url, params).unwrap();
let request_builder = method_fn(&url).header(
CONTENT_TYPE,
common::consts::MIME_TYPE_RON.to_str().unwrap(),
);
let request_builder = method_fn(&url);
send_req(request_builder.build()?).await
}