diff --git a/README.md b/README.md index ad01892..710086d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ # Backend tests - [x] model/user.rs - [x] model/tripdetails.rs -- [ ] model/planned_event.rs +- [x] model/planned_event.rs - [ ] model/trip.rs - [ ] model/usertrip.rs - [ ] Rest? diff --git a/seeds.sql b/seeds.sql index 51710b6..b10d7f5 100644 --- a/seeds.sql +++ b/seeds.sql @@ -3,3 +3,6 @@ INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('rower', false, INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('guest', false, false, true, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$GF6gizbI79Bh0zA9its8S0gram956v+YIV8w8VpwJnQ'); INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('cox', true, false, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs'); INSERT INTO "user" (name) VALUES('new'); + +INSERT INTO "trip_details" (planned_starting_time, max_people, day) VALUES("10:00", 1, "1970-01-01"); +INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES("test-planned-event", 2, 1); diff --git a/src/model/planned_event.rs b/src/model/planned_event.rs index f837149..3fc1bf9 100644 --- a/src/model/planned_event.rs +++ b/src/model/planned_event.rs @@ -32,6 +32,21 @@ pub struct Registration { } impl PlannedEvent { + pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option { + sqlx::query_as!( + Self, + " +SELECT planned_event.id, name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes +FROM planned_event +INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id +WHERE planned_event.id like ? + ", + id + ) + .fetch_one(db) + .await + .ok() + } pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec { let day = format!("{day}"); let events = sqlx::query_as!( @@ -48,53 +63,31 @@ WHERE day=?", let mut ret = Vec::new(); for event in events { - let cox = Self::get_all_cox_for_id(db, event.id).await; + let cox = event.get_all_cox(db).await; ret.push(PlannedEventWithUser { planned_event: event.clone(), cox_needed: event.planned_amount_cox > cox.len() as i64, cox, - rower: Self::get_all_rower_for_id(db, event.id).await, + rower: event.get_all_rower(db).await, }); } ret } - pub async fn is_full(db: &SqlitePool, trip_details_id: i64) -> bool { - let amount_currently_registered = sqlx::query!( - "SELECT COUNT(*) as count FROM user_trip WHERE trip_details_id = ?", - trip_details_id - ) - .fetch_one(db) - .await - .unwrap(); //TODO: fixme - let amount_currently_registered = i64::from(amount_currently_registered.count); - - let amount_allowed_to_register = sqlx::query!( - "SELECT max_people FROM trip_details WHERE id = ?", - trip_details_id - ) - .fetch_one(db) - .await - .unwrap(); //TODO: fixme - let amount_allowed_to_register = amount_allowed_to_register.max_people; - - amount_currently_registered >= amount_allowed_to_register - } - - async fn get_all_cox_for_id(db: &SqlitePool, id: i64) -> Vec { + async fn get_all_cox(&self, db: &SqlitePool) -> Vec { sqlx::query_as!( Registration, " SELECT (SELECT name FROM user WHERE cox_id = id) as name, (SELECT created_at FROM user WHERE cox_id = id) as registered_at FROM trip WHERE planned_event_id = ? ", - id + self.id ) .fetch_all(db) .await - .unwrap() //TODO: fixme + .unwrap() //Okay, as PlannedEvent can only be created with proper DB backing } - async fn get_all_rower_for_id(db: &SqlitePool, id: i64) -> Vec { + async fn get_all_rower(&self, db: &SqlitePool) -> Vec { sqlx::query_as!( Registration, " @@ -103,11 +96,11 @@ SELECT (SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at FROM user_trip WHERE trip_details_id = (SELECT trip_details_id FROM planned_event WHERE id = ?) ", - id + self.id ) .fetch_all(db) .await - .unwrap() //TODO: fixme + .unwrap() //Okay, as PlannedEvent can only be created with proper DB backing } pub async fn create( @@ -126,10 +119,51 @@ FROM user_trip WHERE trip_details_id = (SELECT trip_details_id FROM planned_even .unwrap(); //TODO: fixme } - pub async fn delete(db: &SqlitePool, id: i64) { - sqlx::query!("DELETE FROM planned_event WHERE id = ?", id) + pub async fn delete(&self, db: &SqlitePool) { + sqlx::query!("DELETE FROM planned_event WHERE id = ?", self.id) .execute(db) .await - .unwrap(); //TODO: fixme + .unwrap(); //Okay, as PlannedEvent can only be created with proper DB backing + } +} + +#[cfg(test)] +mod test { + use crate::testdb; + + use super::PlannedEvent; + use chrono::NaiveDate; + use sqlx::SqlitePool; + + #[sqlx::test] + fn test_get_day() { + let pool = testdb!(); + + let res = + PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; + assert_eq!(res.len(), 1); + } + + #[sqlx::test] + fn test_create() { + let pool = testdb!(); + + PlannedEvent::create(&pool, "new-event".into(), 2, false, 1).await; + + let res = + PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; + assert_eq!(res.len(), 2); + } + + #[sqlx::test] + fn test_delete() { + let pool = testdb!(); + let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap(); + + planned_event.delete(&pool).await; + + let res = + PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; + assert_eq!(res.len(), 0); } } diff --git a/src/model/tripdetails.rs b/src/model/tripdetails.rs index 62d9721..912e195 100644 --- a/src/model/tripdetails.rs +++ b/src/model/tripdetails.rs @@ -5,12 +5,27 @@ use sqlx::{FromRow, SqlitePool}; pub struct TripDetails { pub id: i64, planned_starting_time: String, - max_people: i32, + max_people: i64, day: String, notes: Option, } impl TripDetails { + pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option { + sqlx::query_as!( + TripDetails, + " +SELECT id, planned_starting_time, max_people, day, notes +FROM trip_details +WHERE id like ? + ", + id + ) + .fetch_one(db) + .await + .ok() + } + /// Creates a new entry in `trip_details` and returns its id. pub async fn create( db: &SqlitePool, @@ -28,9 +43,29 @@ impl TripDetails { ) .execute(db) .await - .unwrap(); //TODO: fixme + .unwrap(); //Okay, TripDetails can only be created if self.id exists query.last_insert_rowid() } + + pub async fn is_full(&self, db: &SqlitePool) -> bool { + let amount_currently_registered = sqlx::query!( + "SELECT COUNT(*) as count FROM user_trip WHERE trip_details_id = ?", + self.id + ) + .fetch_one(db) + .await + .unwrap(); //TODO: fixme + let amount_currently_registered = i64::from(amount_currently_registered.count); + + let amount_allowed_to_register = + sqlx::query!("SELECT max_people FROM trip_details WHERE id = ?", self.id) + .fetch_one(db) + .await + .unwrap(); //Okay, TripDetails can only be created if self.id exists + let amount_allowed_to_register = amount_allowed_to_register.max_people; + + amount_currently_registered >= amount_allowed_to_register + } } #[cfg(test)] @@ -41,16 +76,43 @@ mod test { use sqlx::SqlitePool; #[sqlx::test] - fn test() { + fn test_find_true() { + let pool = testdb!(); + + assert!(TripDetails::find_by_id(&pool, 1).await.is_some()); + } + + #[sqlx::test] + fn test_find_false() { + let pool = testdb!(); + + assert!(TripDetails::find_by_id(&pool, 1337).await.is_none()); + } + + #[sqlx::test] + fn test_create() { let pool = testdb!(); assert_eq!( TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None).await, - 1, + 2, ); assert_eq!( TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None).await, - 2, + 3, ); } + + #[sqlx::test] + fn test_false_full() { + let pool = testdb!(); + + let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); + assert_eq!(trip_details.is_full(&pool).await, false); + } + + #[sqlx::test] + fn test_true_full() { + //TODO: register user for trip_details = 1; check if is_full returns true + } } diff --git a/src/model/usertrip.rs b/src/model/usertrip.rs index 060e278..593cf9a 100644 --- a/src/model/usertrip.rs +++ b/src/model/usertrip.rs @@ -1,6 +1,6 @@ use sqlx::SqlitePool; -use super::planned_event::PlannedEvent; +use super::tripdetails::TripDetails; pub struct UserTrip {} @@ -10,7 +10,11 @@ impl UserTrip { user_id: i64, trip_details_id: i64, ) -> Result<(), UserTripError> { - if PlannedEvent::is_full(db, trip_details_id).await { + let trip_details = TripDetails::find_by_id(&db, trip_details_id) + .await + .ok_or(UserTripError::TripDetailsNotFound)?; + + if trip_details.is_full(db).await { return Err(UserTripError::EventAlreadyFull); } @@ -78,4 +82,5 @@ pub enum UserTripError { AlreadyRegistered, AlreadyRegisteredAsCox, EventAlreadyFull, + TripDetailsNotFound, } diff --git a/src/rest/admin/planned_event.rs b/src/rest/admin/planned_event.rs index b3d678b..7b2ee52 100644 --- a/src/rest/admin/planned_event.rs +++ b/src/rest/admin/planned_event.rs @@ -51,9 +51,13 @@ async fn create( #[get("/planned-event//delete")] async fn delete(db: &State, id: i64, _admin: AdminUser) -> Flash { - PlannedEvent::delete(db, id).await; - - Flash::success(Redirect::to("/"), "Successfully deleted the event") + match PlannedEvent::find_by_id(db, id).await { + Some(planned_event) => { + planned_event.delete(db).await; + Flash::success(Redirect::to("/"), "Successfully deleted the event") + } + None => Flash::error(Redirect::to("/"), "PlannedEvent does not exist"), + } } pub fn routes() -> Vec { diff --git a/src/rest/mod.rs b/src/rest/mod.rs index 004fbbe..c41374a 100644 --- a/src/rest/mod.rs +++ b/src/rest/mod.rs @@ -72,6 +72,9 @@ async fn join(db: &State, trip_details_id: i64, user: User) -> Flash Err(UserTripError::AlreadyRegisteredAsCox) => { Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!") } + Err(UserTripError::TripDetailsNotFound) => { + Flash::error(Redirect::to("/"), "Trip_details do not exist.") + } } }