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());
+ }
}