From 93e3e0ef5c0995d31fc0f7ef0d2ff8c381aa80bc Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 22 May 2024 22:05:03 +0200 Subject: [PATCH 1/2] only allow realistic values for logbook entries --- src/model/logbook.rs | 14 ++++++++++++++ src/tera/log.rs | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/model/logbook.rs b/src/model/logbook.rs index 797a2fc..20585ef 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -104,6 +104,7 @@ pub enum LogbookUpdateError { SteeringPersonNotInRowers, UserNotAllowedToUseBoat, OnlyAllowedToEndTripsEndingToday, + TooFast(i64, i64), } #[derive(Debug, PartialEq)] @@ -127,6 +128,7 @@ pub enum LogbookCreateError { ArrivalSetButNotRemainingTwo, OnlyAllowedToEndTripsEndingToday, CantChangeHandoperatableStatusForThisBoat, + TooFast(i64, i64), } impl From for LogbookCreateError { @@ -150,6 +152,7 @@ impl From for LogbookCreateError { LogbookUpdateError::OnlyAllowedToEndTripsEndingToday => { LogbookCreateError::OnlyAllowedToEndTripsEndingToday } + LogbookUpdateError::TooFast(km, min) => LogbookCreateError::TooFast(km, min), } } } @@ -517,6 +520,17 @@ ORDER BY departure DESC if arr.and_utc().timestamp() < dep.and_utc().timestamp() { return Err(LogbookUpdateError::ArrivalNotAfterDeparture); } + + let duration_in_mins = (arr.and_utc().timestamp() - dep.and_utc().timestamp()) / 60; + // Not possible to row < 1 min / 500 m = < 2 min / km + let possible_distance_km = duration_in_mins / 2; + if log.distance_in_km > possible_distance_km { + return Err(LogbookUpdateError::TooFast( + log.distance_in_km, + duration_in_mins, + )); + } + let today = Local::now().date_naive(); let day_diff = today - arr.date(); let day_diff = day_diff.num_days(); diff --git a/src/tera/log.rs b/src/tera/log.rs index 9b42eba..8fe3131 100644 --- a/src/tera/log.rs +++ b/src/tera/log.rs @@ -227,7 +227,7 @@ async fn create_logbook( Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"), Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."), Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(Redirect::to("/log"), "Handsteuer-Status dieses Boots kann nicht verändert werden."), - + Err(LogbookCreateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")), } } @@ -300,6 +300,7 @@ async fn home_logbook( Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"), Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."), + Err(LogbookUpdateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")), Err(e) => Flash::error( Redirect::to("/log"), format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"), -- 2.45.2 From 446e48020ef114d3f3dc665247cf4d21a92ab600 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 22 May 2024 22:28:33 +0200 Subject: [PATCH 2/2] test fails due to new sanity check (25km in 1 min :-)); started trip 2 hours ago now in tests --- frontend/tests/log.spec.ts | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/frontend/tests/log.spec.ts b/frontend/tests/log.spec.ts index f835e84..bc74ec4 100644 --- a/frontend/tests/log.spec.ts +++ b/frontend/tests/log.spec.ts @@ -67,6 +67,16 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => { await expect(page.getByRole("listbox")).toContainText( "Nur 2 Ruderer können hinzugefügt werden", ); + + // Trip starts 2 hours ago + const datetimeSelector = '#departure'; + const currentValue = await page.$eval(datetimeSelector, el => el.value); + const currentDate = new Date(currentValue); + currentDate.setMinutes(currentDate.getMinutes()); + currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2); + const newDatetime = currentDate.toISOString().slice(0, 16); + await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime); + await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); await expect(page.locator("#steering_person-newrowerjs")).toContainText( "rower2 cox", @@ -80,15 +90,6 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => { await page.goto("/log"); await page.locator("div:nth-child(2) > .border-0").click(); - // Add a minute - const datetimeSelector = '#arrivaljs'; - const currentValue = await page.$eval(datetimeSelector, el => el.value); - const currentDate = new Date(currentValue); - currentDate.setMinutes(currentDate.getMinutes() + 1); - currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60); - const newDatetime = currentDate.toISOString().slice(0, 16); - await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime); - await page.getByRole("combobox", { name: "Destination" }).click(); await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim"); await page.getByRole("button", { name: "Ausfahrt beenden" }).click(); @@ -153,6 +154,16 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => { await expect(page.getByRole("listbox")).toContainText( "Nur 2 Ruderer können hinzugefügt werden", ); + + // Trip starts 2 hours ago + const datetimeSelector = '#departure'; + const currentValue = await page.$eval(datetimeSelector, el => el.value); + const currentDate = new Date(currentValue); + currentDate.setMinutes(currentDate.getMinutes()); + currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2); + const newDatetime = currentDate.toISOString().slice(0, 16); + await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime); + await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); await expect(page.locator("#steering_person-newrowerjs")).toContainText( "rower2 cox", @@ -166,15 +177,6 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => { await page.goto("/log"); await page.locator('div:nth-child(2) > .pt-2 > div > div > div:nth-child(2) > .border-0').click(); // 2 trips currently running, try to close second one - // Add a minute - const datetimeSelector = '#arrivaljs'; - const currentValue = await page.$eval(datetimeSelector, el => el.value); - const currentDate = new Date(currentValue); - currentDate.setMinutes(currentDate.getMinutes() + 1); - currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60); - const newDatetime = currentDate.toISOString().slice(0, 16); - await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime); - await page.getByRole("combobox", { name: "Destination" }).click(); await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim"); await page.getByRole("button", { name: "Ausfahrt beenden" }).click(); -- 2.45.2