From 18875a9e0ce7de669501d6f0219141a8c90305c8 Mon Sep 17 00:00:00 2001 From: Max Hohlfeld Date: Sat, 26 Jul 2025 22:54:55 +0200 Subject: [PATCH] test: crossarea and delete availability --- ...real_for_availabilities_in_his_area-2.snap | 13 + ..._areal_for_availabilities_in_his_area.snap | 13 + web/src/endpoints/availability/delete.rs | 125 +++++++- .../endpoints/availability/put_cross_areal.rs | 292 +++++++++++++++--- 4 files changed, 397 insertions(+), 46 deletions(-) create mode 100644 web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area-2.snap create mode 100644 web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area.snap diff --git a/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area-2.snap b/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area-2.snap new file mode 100644 index 00000000..ca3ef7be --- /dev/null +++ b/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area-2.snap @@ -0,0 +1,13 @@ +--- +source: web/src/endpoints/availability/put_cross_areal.rs +expression: disable_body +snapshot_kind: text +--- + diff --git a/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area.snap b/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area.snap new file mode 100644 index 00000000..d7875200 --- /dev/null +++ b/web/snapshots/brass_web__endpoints__availability__put_cross_areal__tests__inner_area_manager_can_toggle_cross_areal_for_availabilities_in_his_area.snap @@ -0,0 +1,13 @@ +--- +source: web/src/endpoints/availability/put_cross_areal.rs +expression: enable_body +snapshot_kind: text +--- + diff --git a/web/src/endpoints/availability/delete.rs b/web/src/endpoints/availability/delete.rs index ceb5e4fa..15c18516 100644 --- a/web/src/endpoints/availability/delete.rs +++ b/web/src/endpoints/availability/delete.rs @@ -10,11 +10,14 @@ pub async fn delete( pool: web::Data, path: web::Path, ) -> Result { - let Some(availability) = Availability::read(pool.get_ref(), path.id).await? else { + let Some(availability) = Availability::read_including_user(pool.get_ref(), path.id).await? + else { return Ok(HttpResponse::NotFound().finish()); }; - if user.role != Role::Admin && user.role != Role::AreaManager && availability.user_id != user.id + if availability.user_id != user.id + && user.role != Role::Admin + && (user.role != Role::AreaManager || user.area_id != availability.user.unwrap().area_id) { return Err(ApplicationError::Unauthorized); } @@ -27,16 +30,17 @@ pub async fn delete( #[cfg(test)] mod tests { use actix_http::StatusCode; - use brass_db::models::{Availability, AvailabilityChangeset}; + use brass_db::models::{Area, Availability, AvailabilityChangeset, Role, User, UserChangeset}; use brass_macros::db_test; use chrono::NaiveDateTime; + use fake::{Fake, Faker}; use crate::utils::test_helper::{ create_test_login_user, test_delete, DbTestContext, NaiveDateTimeExt, RequestConfig, }; #[db_test] - async fn deletes_when_availability_is_from_user(context: &DbTestContext) { + async fn deletes_when_availability_is_from_user_itself(context: &DbTestContext) { let app = context.app().await; let config = RequestConfig::new("/availability/delete/1"); create_test_login_user(&context.db_pool, &config).await; @@ -57,10 +61,115 @@ mod tests { let response = test_delete(&app, &config).await; assert_eq!(StatusCode::OK, response.status()); + + let availability = Availability::read(&context.db_pool, 1).await.unwrap(); + assert!(availability.is_none()); } - // test deletes availability_from_another_But_area_manager_in_area - // test doesnt_delete availability_from_another_But_area_manager_is_not_in_area - // test deletest_availability_from_another_user_when_user_admin - // test doesnt_delete availability_when_not_existing + #[db_test] + async fn deletes_when_availability_is_from_other_user_and_requester_is_area_manager_in_area( + context: &DbTestContext, + ) { + let app = context.app().await; + let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager); + create_test_login_user(&context.db_pool, &config).await; + + User::create(&context.db_pool, &Faker.fake()).await.unwrap(); + + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let response = test_delete(&app, &config).await; + assert_eq!(StatusCode::OK, response.status()); + + let availability = Availability::read(&context.db_pool, 1).await.unwrap(); + assert!(availability.is_none()); + } + + #[db_test] + async fn deletes_not_when_availability_is_from_other_user_and_requester_is_area_manager_in_other_area( + context: &DbTestContext, + ) { + let app = context.app().await; + let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager); + create_test_login_user(&context.db_pool, &config).await; + + Area::create(&context.db_pool, "Süd").await.unwrap(); + let mut changeset: UserChangeset = Faker.fake(); + changeset.area_id = 2; + + User::create(&context.db_pool, &changeset).await.unwrap(); + + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let response = test_delete(&app, &config).await; + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + + let availability = Availability::read(&context.db_pool, 1).await.unwrap(); + assert!(availability.is_some()); + } + + #[db_test] + async fn deletes_when_availability_is_from_other_user_and_requester_is_admin( + context: &DbTestContext, + ) { + let app = context.app().await; + let config = RequestConfig::new("/availability/delete/1").with_role(Role::Admin); + create_test_login_user(&context.db_pool, &config).await; + + User::create(&context.db_pool, &Faker.fake()).await.unwrap(); + + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let response = test_delete(&app, &config).await; + assert_eq!(StatusCode::OK, response.status()); + + let availability = Availability::read(&context.db_pool, 1).await.unwrap(); + assert!(availability.is_none()); + } + + #[db_test] + async fn deletes_not_when_availability_doesnt_exist(context: &DbTestContext) { + let app = context.app().await; + let config = RequestConfig::new("/availability/delete/1"); + create_test_login_user(&context.db_pool, &config).await; + + let response = test_delete(&app, &config).await; + assert_eq!(StatusCode::NOT_FOUND, response.status()); + } } diff --git a/web/src/endpoints/availability/put_cross_areal.rs b/web/src/endpoints/availability/put_cross_areal.rs index be683a09..2b61e01e 100644 --- a/web/src/endpoints/availability/put_cross_areal.rs +++ b/web/src/endpoints/availability/put_cross_areal.rs @@ -81,42 +81,258 @@ async fn handle_cross_areal( #[cfg(test)] mod tests { - // use crate::utils::test_helper::{ - // assert_snapshot, create_test_login_user, read_body, test_put, DbTestContext, RequestConfig, - // StatusCode, - // }; - // use brass_macros::db_test; - // - // #[db_test] - // async fn user_can_toggle_subscription_for_himself(context: &DbTestContext) { - // let app = context.app().await; - // - // let unsubscribe_config = RequestConfig::new("/users/1/unsubscribeNotifications"); - // create_test_login_user(&context.db_pool, &unsubscribe_config).await; - // let unsubscribe_response = test_put::<_, _, String>(&app, &unsubscribe_config, None).await; - // - // assert_eq!(StatusCode::OK, unsubscribe_response.status()); - // - // let unsubscribe_body = read_body(unsubscribe_response).await; - // assert_snapshot!(unsubscribe_body); - // - // let subscribe_config = RequestConfig::new("/users/1/subscribeNotifications"); - // let subscribe_response = test_put::<_, _, String>(&app, &subscribe_config, None).await; - // - // assert_eq!(StatusCode::OK, subscribe_response.status()); - // - // let subscribe_body = read_body(subscribe_response).await; - // assert_snapshot!(subscribe_body); - // } - // - // #[db_test] - // async fn user_cant_toggle_subscription_for_someone_else(context: &DbTestContext) { - // let app = context.app().await; - // - // let unsubscribe_config = RequestConfig::new("/users/3/unsubscribeNotifications"); - // create_test_login_user(&context.db_pool, &unsubscribe_config).await; - // let unsubscribe_response = test_put::<_, _, String>(&app, &unsubscribe_config, None).await; - // - // assert_eq!(StatusCode::UNAUTHORIZED, unsubscribe_response.status()); - // } + use crate::utils::test_helper::{ + assert_snapshot, create_test_login_user, test_put, DbTestContext, NaiveDateTimeExt, + RequestConfig, ServiceResponseExt, StatusCode, + }; + use brass_db::models::{ + Area, Assignment, AssignmentChangeset, Availability, AvailabilityChangeset, Event, + EventChangeset, Location, Role, User, UserChangeset, + }; + use brass_macros::db_test; + use chrono::NaiveDateTime; + use fake::{Fake, Faker}; + + #[db_test] + async fn area_manager_can_toggle_cross_areal_for_availabilities_in_his_area( + context: &DbTestContext, + ) { + let app = context.app().await; + let mut config = + RequestConfig::new("/availability/1/makeCrossAreal").with_role(Role::AreaManager); + create_test_login_user(&context.db_pool, &config).await; + + User::create(&context.db_pool, &Faker.fake()).await.unwrap(); + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let (enable_status, enable_body) = test_put::<_, _, String>(&app, &config, None) + .await + .into_status_and_body() + .await; + + assert_eq!(StatusCode::OK, enable_status); + assert_snapshot!(enable_body); + + assert!( + Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + + config.uri = "/availability/1/makeNonCrossAreal".to_string(); + + let (disable_status, disable_body) = test_put::<_, _, String>(&app, &config, None) + .await + .into_status_and_body() + .await; + + assert_eq!(StatusCode::OK, disable_status); + assert_snapshot!(disable_body); + + assert!( + !Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + } + + #[db_test] + async fn area_manager_cant_toggle_cross_areal_for_availabilities_outside_his_area( + context: &DbTestContext, + ) { + let app = context.app().await; + let mut config = RequestConfig::new("/availability/1/makeCrossAreal"); + create_test_login_user(&context.db_pool, &config).await; + + Area::create(&context.db_pool, "Süd").await.unwrap(); + let mut changeset: UserChangeset = Faker.fake(); + changeset.area_id = 2; + + User::create(&context.db_pool, &changeset).await.unwrap(); + + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + assert!( + !Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + + Availability::update_cross_areal(&context.db_pool, 1, true) + .await + .unwrap(); + config.uri = "/availability/1/makeNonCrossAreal".to_string(); + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + assert!( + Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + } + + #[db_test] + async fn user_cant_toggle_cross_areal(context: &DbTestContext) { + let app = context.app().await; + let mut config = RequestConfig::new("/availability/1/makeCrossAreal"); + create_test_login_user(&context.db_pool, &config).await; + + Availability::create( + &context.db_pool, + 1, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + assert!( + !Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + + Availability::update_cross_areal(&context.db_pool, 1, true) + .await + .unwrap(); + config.uri = "/availability/1/makeNonCrossAreal".to_string(); + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + assert!( + Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + } + + #[db_test] + async fn area_manager_cant_toggle_cross_areal_for_availability_already_planned( + context: &DbTestContext, + ) { + let app = context.app().await; + let config = + RequestConfig::new("/availability/1/makeNonCrossAreal").with_role(Role::AreaManager); + create_test_login_user(&context.db_pool, &config).await; + + User::create(&context.db_pool, &Faker.fake()).await.unwrap(); + Availability::create( + &context.db_pool, + 2, + AvailabilityChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + comment: None, + }, + ) + .await + .unwrap(); + + Availability::update_cross_areal(&context.db_pool, 1, true) + .await + .unwrap(); + + Location::create(&context.db_pool, "Ort", 1).await.unwrap(); + + Event::create( + &context.db_pool, + EventChangeset::create_for_test( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + ) + .await + .unwrap(); + + Assignment::create( + &context.db_pool, + 1, + 1, + AssignmentChangeset { + time: ( + NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(), + NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(), + ), + function: brass_db::models::Function::Posten, + }, + ) + .await + .unwrap(); + + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::UNPROCESSABLE_ENTITY, response.status()); + + assert!( + Availability::read(&context.db_pool, 1) + .await + .unwrap() + .unwrap() + .cross_areal + ); + } + + #[db_test] + async fn area_manager_cant_toggle_cross_areal_when_availability_doesnt_exist( + context: &DbTestContext, + ) { + let app = context.app().await; + let config = + RequestConfig::new("/availability/1/makeNonCrossAreal").with_role(Role::AreaManager); + create_test_login_user(&context.db_pool, &config).await; + + let response = test_put::<_, _, String>(&app, &config, None).await; + + assert_eq!(StatusCode::NOT_FOUND, response.status()); + } }