2023-10-05 08:46:34 +02:00
use std ::net ::IpAddr ;
2023-07-23 12:17:57 +02:00
use rocket ::{
form ::Form ,
2023-07-27 14:40:05 +02:00
get ,
http ::{ Cookie , CookieJar } ,
post ,
request ::{ self , FlashMessage , FromRequest } ,
2023-07-23 12:17:57 +02:00
response ::{ Flash , Redirect } ,
2023-07-27 14:40:05 +02:00
routes ,
time ::{ Duration , OffsetDateTime } ,
Request , Route , State ,
2023-07-23 12:17:57 +02:00
} ;
2023-07-30 14:13:49 +02:00
use rocket_dyn_templates ::{ context , Template } ;
2023-07-23 12:17:57 +02:00
use sqlx ::SqlitePool ;
use tera ::Context ;
use crate ::model ::{
boat ::Boat ,
2023-10-05 08:46:34 +02:00
log ::Log ,
2023-09-24 09:55:51 +02:00
logbook ::{
LogToAdd , LogToFinalize , Logbook , LogbookCreateError , LogbookDeleteError ,
LogbookUpdateError ,
} ,
2023-07-23 12:17:57 +02:00
logtype ::LogType ,
2023-12-23 21:27:52 +01:00
user ::{ NonGuestUser , User , UserWithRoles , UserWithWaterStatus } ,
2023-07-23 12:17:57 +02:00
} ;
2023-07-27 14:38:46 +02:00
pub struct KioskCookie ( String ) ;
#[ rocket::async_trait ]
impl < ' r > FromRequest < ' r > for KioskCookie {
type Error = std ::convert ::Infallible ;
async fn from_request ( request : & ' r Request < '_ > ) -> request ::Outcome < KioskCookie , Self ::Error > {
match request . cookies ( ) . get_private ( " kiosk " ) {
Some ( cookie ) = > request ::Outcome ::Success ( KioskCookie ( cookie . value ( ) . to_string ( ) ) ) ,
2023-11-08 17:39:39 +01:00
None = > request ::Outcome ::Forward ( rocket ::http ::Status ::SeeOther ) ,
2023-07-27 14:38:46 +02:00
}
}
}
2023-07-27 22:20:40 +02:00
#[ get( " / " , rank = 2) ]
2023-10-24 10:57:51 +02:00
async fn index (
db : & State < SqlitePool > ,
flash : Option < FlashMessage < '_ > > ,
user : NonGuestUser ,
) -> Template {
let boats = Boat ::for_user ( db , & user . user ) . await ;
2023-09-23 22:13:48 +02:00
let coxes : Vec < UserWithWaterStatus > = futures ::future ::join_all (
User ::cox ( db )
. await
. into_iter ( )
. map ( | user | UserWithWaterStatus ::from_user ( user , db ) ) ,
)
. await ;
let users : Vec < UserWithWaterStatus > = futures ::future ::join_all (
User ::all ( db )
. await
. into_iter ( )
. map ( | user | UserWithWaterStatus ::from_user ( user , db ) ) ,
)
. await ;
2023-07-23 12:17:57 +02:00
let logtypes = LogType ::all ( db ) . await ;
2023-07-26 12:56:19 +02:00
let distances = Logbook ::distances ( db ) . await ;
2023-07-23 12:17:57 +02:00
let on_water = Logbook ::on_water ( db ) . await ;
let mut context = Context ::new ( ) ;
if let Some ( msg ) = flash {
context . insert ( " flash " , & msg . into_inner ( ) ) ;
}
context . insert ( " boats " , & boats ) ;
2023-07-24 13:01:39 +02:00
context . insert ( " coxes " , & coxes ) ;
2023-07-23 12:17:57 +02:00
context . insert ( " users " , & users ) ;
context . insert ( " logtypes " , & logtypes ) ;
2023-12-23 21:27:52 +01:00
context . insert (
" loggedin_user " ,
& UserWithRoles ::from_user ( user . user , db ) . await ,
) ;
2023-07-23 12:17:57 +02:00
context . insert ( " on_water " , & on_water ) ;
2023-07-26 12:56:19 +02:00
context . insert ( " distances " , & distances ) ;
2023-07-23 12:17:57 +02:00
Template ::render ( " log " , context . into_json ( ) )
}
2023-07-30 14:13:49 +02:00
#[ get( " /show " , rank = 2) ]
2023-10-24 10:16:26 +02:00
async fn show ( db : & State < SqlitePool > , user : NonGuestUser ) -> Template {
2023-07-30 14:13:49 +02:00
let logs = Logbook ::completed ( db ) . await ;
2023-12-23 21:27:52 +01:00
Template ::render (
" log.completed " ,
context! ( logs , loggedin_user : & UserWithRoles ::from_user ( user . user , db ) . await ) ,
)
2023-07-30 14:13:49 +02:00
}
#[ get( " /show " ) ]
2023-10-01 20:50:23 +02:00
async fn show_kiosk ( db : & State < SqlitePool > , _kiosk : KioskCookie ) -> Template {
2023-07-30 14:13:49 +02:00
let logs = Logbook ::completed ( db ) . await ;
2023-10-23 21:05:59 +02:00
Template ::render ( " log.completed " , context! ( logs , show_kiosk_header : true ) )
2023-07-30 14:13:49 +02:00
}
2023-08-05 13:09:58 +02:00
#[ get( " /kiosk/ekrv2019/<loc> " ) ]
2023-10-05 08:46:34 +02:00
async fn new_kiosk (
db : & State < SqlitePool > ,
cookies : & CookieJar < '_ > ,
loc : String ,
2023-10-05 09:01:55 +02:00
ip : Option < IpAddr > ,
2023-10-05 08:46:34 +02:00
) -> Redirect {
Log ::create (
db ,
format! ( " New kiosk cookie set for loc ' {loc} ' (IP= {ip:?} ) " ) ,
)
. await ;
2023-08-05 13:09:58 +02:00
let mut cookie = Cookie ::new ( " kiosk " , loc ) ;
2023-07-27 14:38:46 +02:00
cookie . set_expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 12 ) ) ;
cookies . add_private ( cookie ) ;
2023-08-05 13:09:58 +02:00
Redirect ::to ( " /log " )
2023-07-27 14:38:46 +02:00
}
2023-08-05 13:09:58 +02:00
#[ get( " / " ) ]
2023-07-27 14:40:05 +02:00
async fn kiosk (
db : & State < SqlitePool > ,
flash : Option < FlashMessage < '_ > > ,
2023-08-05 13:09:58 +02:00
kiosk : KioskCookie ,
2023-07-27 14:40:05 +02:00
) -> Template {
2023-08-05 13:09:58 +02:00
let boats = Boat ::all_at_location ( db , kiosk . 0 ) . await ;
2023-09-24 11:19:10 +02:00
let coxes : Vec < UserWithWaterStatus > = futures ::future ::join_all (
User ::cox ( db )
. await
. into_iter ( )
. map ( | user | UserWithWaterStatus ::from_user ( user , db ) ) ,
)
. await ;
let users : Vec < UserWithWaterStatus > = futures ::future ::join_all (
User ::all ( db )
. await
. into_iter ( )
. map ( | user | UserWithWaterStatus ::from_user ( user , db ) ) ,
)
. await ;
2023-07-27 14:38:46 +02:00
let logtypes = LogType ::all ( db ) . await ;
let distances = Logbook ::distances ( db ) . await ;
let on_water = Logbook ::on_water ( db ) . await ;
let mut context = Context ::new ( ) ;
if let Some ( msg ) = flash {
context . insert ( " flash " , & msg . into_inner ( ) ) ;
}
context . insert ( " boats " , & boats ) ;
context . insert ( " coxes " , & coxes ) ;
context . insert ( " users " , & users ) ;
context . insert ( " logtypes " , & logtypes ) ;
context . insert ( " on_water " , & on_water ) ;
context . insert ( " distances " , & distances ) ;
2023-10-23 21:05:59 +02:00
context . insert ( " show_kiosk_header " , & true ) ;
2023-07-27 14:38:46 +02:00
Template ::render ( " kiosk " , context . into_json ( ) )
}
2023-10-24 10:57:51 +02:00
async fn create_logbook (
db : & SqlitePool ,
data : Form < LogToAdd > ,
user : & NonGuestUser ,
) -> Flash < Redirect > {
2023-07-23 16:49:14 +02:00
match Logbook ::create (
2023-07-23 12:17:57 +02:00
db ,
2023-10-01 15:53:45 +02:00
data . into_inner ( ) ,
2023-10-24 10:57:51 +02:00
& user . user
2023-07-23 12:17:57 +02:00
)
. await
{
2023-07-23 16:49:14 +02:00
Ok ( _ ) = > Flash ::success ( Redirect ::to ( " /log " ) , " Ausfahrt erfolgreich hinzugefügt " ) ,
2023-10-02 12:39:05 +02:00
Err ( LogbookCreateError ::BoatAlreadyOnWater ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Boot schon am Wasser " ) ,
2023-07-27 15:24:29 +02:00
Err ( LogbookCreateError ::RowerAlreadyOnWater ( rower ) ) = > Flash ::error ( Redirect ::to ( " /log " ) , format! ( " Ruderer {} schon am Wasser " , rower . name ) ) ,
2023-10-02 12:39:05 +02:00
Err ( LogbookCreateError ::BoatLocked ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Boot gesperrt " ) ,
Err ( LogbookCreateError ::BoatNotFound ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Boot gibt's ned " ) ,
2023-07-24 13:01:39 +02:00
Err ( LogbookCreateError ::TooManyRowers ( expected , actual ) ) = > Flash ::error ( Redirect ::to ( " /log " ) , format! ( " Zu viele Ruderer (Boot fasst maximal {expected} , es wurden jedoch {actual} Ruderer ausgewählt) " ) ) ,
2023-07-31 16:59:15 +02:00
Err ( LogbookCreateError ::RowerCreateError ( rower , e ) ) = > Flash ::error ( Redirect ::to ( " /log " ) , format! ( " Fehler bei Ruderer {rower} : {e} " ) ) ,
2023-10-02 12:39:05 +02:00
Err ( LogbookCreateError ::ArrivalNotAfterDeparture ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Ankunftszeit kann nicht vor der Abfahrtszeit sein " ) ,
Err ( LogbookCreateError ::UserNotAllowedToUseBoat ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Schiffsführer darf dieses Boot nicht verwenden " ) ,
2023-10-29 20:41:30 +01:00
Err ( LogbookCreateError ::SteeringPersonNotInRowers ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Steuerperson nicht in Liste der Ruderer! " ) ,
Err ( LogbookCreateError ::ShipmasterNotInRowers ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Schiffsführer nicht in Liste der Ruderer! " ) ,
Err ( LogbookCreateError ::NotYourEntry ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Nicht deine Ausfahrt! " ) ,
Err ( LogbookCreateError ::ArrivalSetButNotRemainingTwo ) = > Flash ::error ( Redirect ::to ( " /log " ) , " Ankunftszeit gesetzt aber nicht Distanz + Strecke " ) ,
2023-11-27 12:49:36 +01:00
Err ( LogbookCreateError ::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). " ) ,
2023-09-24 09:48:01 +02:00
2023-07-24 13:01:39 +02:00
}
2023-07-27 14:38:46 +02:00
}
2023-07-27 15:24:29 +02:00
#[ post( " / " , data = " <data> " , rank = 2) ]
2023-10-24 10:57:51 +02:00
async fn create (
db : & State < SqlitePool > ,
data : Form < LogToAdd > ,
user : NonGuestUser ,
) -> Flash < Redirect > {
2023-10-29 18:42:12 +01:00
Log ::create (
db ,
format! (
" User {} tries to create log entry={:?} " ,
user . user . name , data
) ,
)
. await ;
2023-10-01 15:53:45 +02:00
create_logbook ( db , data , & user ) . await
2023-07-27 15:24:29 +02:00
}
#[ post( " / " , data = " <data> " ) ]
2023-07-27 22:20:40 +02:00
async fn create_kiosk (
db : & State < SqlitePool > ,
data : Form < LogToAdd > ,
_kiosk : KioskCookie ,
) -> Flash < Redirect > {
2023-10-31 22:41:12 +01:00
let Some ( boat ) = Boat ::find_by_id ( db , data . boat_id ) . await else {
return Flash ::error ( Redirect ::to ( " /log " ) , " Boot gibt's nicht " ) ;
} ;
let creator = if boat . amount_seats = = 1 & & boat . owner . is_some ( ) {
User ::find_by_id ( db , boat . owner . unwrap ( ) as i32 )
. await
. unwrap ( )
2023-11-02 22:19:13 +01:00
} else if let Some ( shipmaster ) = data . shipmaster {
User ::find_by_id ( db , shipmaster as i32 ) . await . unwrap ( )
2023-10-31 22:16:14 +01:00
} else {
2023-11-02 22:19:13 +01:00
User ::find_by_id ( db , data . rowers [ 0 ] as i32 ) . await . unwrap ( )
2023-10-31 22:16:14 +01:00
} ;
2023-10-28 14:33:22 +02:00
Log ::create (
db ,
format! (
" Kiosk tries to create log for shipmaster {} entry={:?} " ,
creator . name , data
) ,
)
. await ;
2023-12-23 21:27:52 +01:00
create_logbook ( db , data , & NonGuestUser { user : creator } ) . await //TODO: fixme
2023-07-27 15:24:29 +02:00
}
2023-07-27 22:20:40 +02:00
async fn home_logbook (
db : & SqlitePool ,
data : Form < LogToFinalize > ,
logbook_id : i32 ,
2023-10-24 10:57:51 +02:00
user : & NonGuestUser ,
2023-07-27 22:20:40 +02:00
) -> Flash < Redirect > {
2023-07-27 15:24:29 +02:00
let logbook : Option < Logbook > = Logbook ::find_by_id ( db , logbook_id ) . await ;
2023-07-27 14:38:46 +02:00
let Some ( logbook ) = logbook else {
2023-07-31 16:59:15 +02:00
return Flash ::error (
Redirect ::to ( " /admin/log " ) ,
format! ( " Log with ID {} does not exist! " , logbook_id ) ,
) ;
2023-07-27 14:38:46 +02:00
} ;
2023-10-24 10:57:51 +02:00
match logbook . home ( db , & user . user , data . into_inner ( ) ) . await {
2023-10-23 22:52:11 +02:00
Ok ( _ ) = > Flash ::success ( Redirect ::to ( " /log " ) , " Ausfahrt korrekt eingetragen " ) ,
2023-07-27 15:24:29 +02:00
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) " ) ) ,
2023-11-27 12:49:36 +01:00
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). " ) ,
2023-10-29 20:41:30 +01:00
Err ( e ) = > Flash ::error (
2023-07-27 15:24:29 +02:00
Redirect ::to ( " /log " ) ,
2023-10-29 20:41:30 +01:00
format! ( " Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?} )! " ) ,
2023-10-02 12:39:05 +02:00
) ,
2023-07-27 14:38:46 +02:00
}
2023-07-27 15:24:29 +02:00
}
#[ post( " /<logbook_id> " , data = " <data> " ) ]
async fn home_kiosk (
db : & State < SqlitePool > ,
data : Form < LogToFinalize > ,
logbook_id : i32 ,
2023-07-27 22:20:40 +02:00
_kiosk : KioskCookie ,
2023-07-27 15:24:29 +02:00
) -> Flash < Redirect > {
let logbook = Logbook ::find_by_id ( db , logbook_id ) . await . unwrap ( ) ; //TODO: fixme
2023-10-28 14:33:22 +02:00
Log ::create (
db ,
format! ( " Kiosk tries to finish log entry {logbook_id} {data:?} " ) ,
)
. await ;
2023-07-27 22:20:40 +02:00
home_logbook (
db ,
data ,
logbook_id ,
2023-12-23 21:27:52 +01:00
& NonGuestUser {
user : User ::find_by_id ( db , logbook . shipmaster as i32 )
2023-10-24 10:57:51 +02:00
. await
. unwrap ( ) , //TODO: fixme
2023-12-23 21:27:52 +01:00
} ,
2023-07-27 22:20:40 +02:00
)
. await
2023-07-27 14:38:46 +02:00
}
2023-07-27 14:40:05 +02:00
#[ post( " /<logbook_id> " , data = " <data> " , rank = 2) ]
2023-07-23 16:49:14 +02:00
async fn home (
db : & State < SqlitePool > ,
2023-07-25 13:22:11 +02:00
data : Form < LogToFinalize > ,
2023-07-23 16:49:14 +02:00
logbook_id : i32 ,
2023-10-24 10:57:51 +02:00
user : NonGuestUser ,
2023-07-23 16:49:14 +02:00
) -> Flash < Redirect > {
2023-10-28 14:33:22 +02:00
Log ::create (
db ,
format! (
" User {} tries to finish log entry {logbook_id} {data:?} " ,
user . user . name
) ,
)
. await ;
2023-10-01 13:48:21 +02:00
home_logbook ( db , data , logbook_id , & user ) . await
2023-07-23 12:17:57 +02:00
}
2023-10-23 21:32:44 +02:00
#[ get( " /<logbook_id>/delete " , rank = 2) ]
2023-09-24 09:55:51 +02:00
async fn delete ( db : & State < SqlitePool > , logbook_id : i32 , user : User ) -> Flash < Redirect > {
2023-08-05 16:27:51 +02:00
let logbook = Logbook ::find_by_id ( db , logbook_id ) . await ;
if let Some ( logbook ) = logbook {
2023-10-28 14:33:22 +02:00
Log ::create (
db ,
format! ( " User {} tries to delete log entry {logbook_id} " , user . name ) ,
)
. await ;
2023-09-24 09:55:51 +02:00
match logbook . delete ( db , & user ) . await {
Ok ( _ ) = > Flash ::success (
Redirect ::to ( " /log " ) ,
2023-10-23 22:52:11 +02:00
format! ( " Eintrag {} gelöscht! " , logbook_id ) ,
2023-09-24 09:55:51 +02:00
) ,
Err ( LogbookDeleteError ::NotYourEntry ) = > Flash ::error (
Redirect ::to ( " /log " ) ,
" Du hast nicht die Berechtigung, den Eintrag zu löschen! " ,
) ,
}
2023-08-05 16:27:51 +02:00
} else {
Flash ::error (
Redirect ::to ( " /log " ) ,
format! ( " Logbook with ID {} could not be found! " , logbook_id ) ,
)
}
}
2023-10-23 21:32:44 +02:00
#[ get( " /<logbook_id>/delete " ) ]
async fn delete_kiosk (
db : & State < SqlitePool > ,
logbook_id : i32 ,
_kiosk : KioskCookie ,
) -> Flash < Redirect > {
let logbook = Logbook ::find_by_id ( db , logbook_id ) . await ;
if let Some ( logbook ) = logbook {
let cox = User ::find_by_id ( db , logbook . shipmaster as i32 )
. await
. unwrap ( ) ;
2023-10-28 14:33:22 +02:00
Log ::create ( db , format! ( " Kiosk tries to delete log entry {logbook_id} " ) ) . await ;
2023-10-23 21:32:44 +02:00
match logbook . delete ( db , & cox ) . await {
Ok ( _ ) = > Flash ::success (
Redirect ::to ( " /log " ) ,
2023-10-23 22:52:11 +02:00
format! ( " Eintrag {} gelöscht! " , logbook_id ) ,
2023-10-23 21:32:44 +02:00
) ,
Err ( LogbookDeleteError ::NotYourEntry ) = > Flash ::error (
Redirect ::to ( " /log " ) ,
" Du hast nicht die Berechtigung, den Eintrag zu löschen! " ,
) ,
}
} else {
Flash ::error (
Redirect ::to ( " /log " ) ,
format! ( " Logbook with ID {} could not be found! " , logbook_id ) ,
)
}
}
2023-07-23 12:17:57 +02:00
pub fn routes ( ) -> Vec < Route > {
2023-07-27 14:40:05 +02:00
routes! [
index ,
create ,
create_kiosk ,
home ,
kiosk ,
home_kiosk ,
2023-07-30 14:13:49 +02:00
new_kiosk ,
show ,
2023-08-05 16:27:51 +02:00
show_kiosk ,
2023-10-23 21:32:44 +02:00
delete ,
delete_kiosk
2023-07-27 14:40:05 +02:00
]
2023-07-23 12:17:57 +02:00
}
2023-10-26 18:50:43 +02:00
#[ cfg(test) ]
mod test {
use rocket ::http ::ContentType ;
use rocket ::{ http ::Status , local ::asynchronous ::Client } ;
use sqlx ::SqlitePool ;
use crate ::model ::logbook ::Logbook ;
use crate ::tera ::{ log ::Boat , User } ;
use crate ::testdb ;
#[ sqlx::test ]
fn test_kiosk_cookie ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /auth " ) ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Linz " ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::Ok ) ;
let text = response . into_string ( ) . await . unwrap ( ) ;
assert! ( text . contains ( " Logbuch " ) ) ;
assert! ( text . contains ( " Neue Ausfahrt " ) ) ;
assert! ( ! text . contains ( " Ottensheim Boot " ) ) ;
}
#[ sqlx::test ]
fn test_kiosk_cookie_boat ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Ottensheim " ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::Ok ) ;
let text = response . into_string ( ) . await . unwrap ( ) ;
assert! ( text . contains ( " Logbuch " ) ) ;
assert! ( text . contains ( " Neue Ausfahrt " ) ) ;
assert! ( text . contains ( " Ottensheim Boot " ) ) ;
}
#[ sqlx::test ]
fn test_index ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=admin&password=admin " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
assert! ( text . contains ( " Logbuch " ) ) ;
assert! ( text . contains ( " Neue Ausfahrt " ) ) ;
}
#[ sqlx::test ]
fn test_show ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=admin&password=admin " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
let req = client . get ( " /log/show " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
println! ( " {text:?} " ) ;
assert! ( text . contains ( " Logbuch " ) ) ;
assert! ( text . contains ( " Joe " ) ) ;
}
#[ sqlx::test ]
fn test_show_kiosk ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Linz " ) ;
let _ = req . dispatch ( ) . await ;
let req = client . get ( " /log/show " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
assert! ( text . contains ( " Logbuch " ) ) ;
assert! ( text . contains ( " Joe " ) ) ;
}
#[ sqlx::test ]
fn test_create ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=admin&password=admin " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
2023-11-16 15:35:52 +01:00
let req = client . post ( " /log " ) . header ( ContentType ::Form ) . body ( format! (
" boat_id=1&shipmaster=4&departure={0}T10:00&steering_person=4&rowers[]=4 " ,
current_date
) ) ;
2023-10-26 18:50:43 +02:00
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt erfolgreich hinzugefügt "
) ;
}
#[ sqlx::test ]
fn test_home_kiosk ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Linz " ) ;
let _ = req . dispatch ( ) . await ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
let req = client
. post ( " /log/1 " )
. header ( ContentType ::Form )
2023-11-16 15:35:52 +01:00
. body ( format! ( " destination=Ottensheim&distance_in_km=25&shipmaster=2&steering_person=2&departure= {0} T10:00&arrival= {0} T12:00&rowers[]=2 " , current_date ) ) ;
2023-10-26 18:50:43 +02:00
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt korrekt eingetragen "
) ;
}
//Kiosk mode
// i see all boats
#[ sqlx::test ]
fn test_kiosks_sees_all_boats ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Linz " ) ;
let _ = req . dispatch ( ) . await ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
//Sees all boats stationed in Linz
assert! ( text . contains ( " Haichenbach " ) ) ;
assert! ( text . contains ( " Joe " ) ) ;
assert! ( text . contains ( " Kaputtes Boot :-( " ) ) ;
assert! ( text . contains ( " Sehr kaputtes Boot :-(( " ) ) ;
assert! ( text . contains ( " second_private_boat_from_rower " ) ) ;
assert! ( text . contains ( " private_boat_from_rower " ) ) ;
//Doesn't see the one's in Ottensheim
assert! ( ! text . contains ( " Ottensheim Boot " ) ) ;
}
#[ sqlx::test ]
fn test_kiosks_can_start_trips_with_all_boats ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
sqlx ::query ( " DELETE FROM logbook; " )
. execute ( & db )
. await
. unwrap ( ) ;
let mut client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let req = client . get ( " /log/kiosk/ekrv2019/Linz " ) ;
let _ = req . dispatch ( ) . await ;
can_start_and_end_trip ( & db , & mut client , " Haichenbach " . into ( ) , " admin " . into ( ) ) . await ;
can_start_and_end_trip ( & db , & mut client , " Joe " . into ( ) , " admin " . into ( ) ) . await ;
can_start_and_end_trip ( & db , & mut client , " Kaputtes Boot :-( " . into ( ) , " admin " . into ( ) ) . await ;
cant_start_trip (
& db ,
& mut client ,
" Sehr kaputtes Boot :-(( " . into ( ) ,
" admin " . into ( ) ,
" Boot gesperrt " . into ( ) ,
)
. await ;
can_start_and_end_trip (
& db ,
& mut client ,
" second_private_boat_from_rower " . into ( ) ,
" rower " . into ( ) ,
)
. await ;
}
#[ sqlx::test ]
fn test_shipowner_can_allow_others_to_drive ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
sqlx ::query ( " DELETE FROM logbook; " )
. execute ( & db )
. await
. unwrap ( ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=rower&password=rower " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
// Owner can start trip:
let boat_id = Boat ::find_by_name ( & db , " private_boat_from_rower " . into ( ) )
. await
. unwrap ( )
. id ;
let shipmaster_id = User ::find_by_name ( & db , " rower2 " . into ( ) ) . await . unwrap ( ) . id ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
let req = client . post ( " /log " ) . header ( ContentType ::Form ) . body ( format! (
2023-11-16 15:35:52 +01:00
" boat_id={boat_id}&shipmaster={shipmaster_id}&departure={0}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id} " , current_date
2023-10-26 18:50:43 +02:00
) ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt erfolgreich hinzugefügt "
) ;
// Shipmaster can end it
let log_id = Logbook ::highest_id ( & db ) . await ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=rower2&password=rower " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
let req = client
. post ( format! ( " /log/ {log_id} " ) )
. header ( ContentType ::Form )
2023-11-16 15:36:38 +01:00
. body ( format! ( " destination=Ottensheim&distance_in_km=25&shipmaster= {shipmaster_id} &steering_person= {shipmaster_id} &departure= {0} T10:00&arrival= {0} T12:00&rowers[]= {shipmaster_id} " , current_date ) ) ;
2023-10-26 18:50:43 +02:00
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt korrekt eingetragen "
) ;
}
#[ sqlx::test ]
fn test_normal_user_sees_appropriate_boats ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let mut client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=rower&password=rower " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
sqlx ::query ( " DELETE FROM logbook; " )
. execute ( & db )
. await
. unwrap ( ) ;
//Sees all 1x
assert! ( text . contains ( " Haichenbach " ) ) ;
can_start_and_end_trip ( & db , & mut client , " Haichenbach " . into ( ) , " rower " . into ( ) ) . await ;
assert! ( text . contains ( " private_boat_from_rower " ) ) ;
can_start_and_end_trip (
& db ,
& mut client ,
" private_boat_from_rower " . into ( ) ,
" rower " . into ( ) ,
)
. await ;
assert! ( text . contains ( " second_private_boat_from_rower " ) ) ;
can_start_and_end_trip (
& db ,
& mut client ,
" second_private_boat_from_rower " . into ( ) ,
" rower " . into ( ) ,
)
. await ;
//Don't see anything else
assert! ( ! text . contains ( " Joe " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" Joe " . into ( ) ,
" rower " . into ( ) ,
" Schiffsführer darf dieses Boot nicht verwenden " . into ( ) ,
)
. await ;
assert! ( ! text . contains ( " Kaputtes Boot :-( " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" Kaputtes Boot :-( " . into ( ) ,
" rower " . into ( ) ,
" Schiffsführer darf dieses Boot nicht verwenden " . into ( ) ,
)
. await ;
assert! ( ! text . contains ( " Sehr kaputtes Boot :-(( " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" Sehr kaputtes Boot :-(( " . into ( ) ,
" rower " . into ( ) ,
" Boot gesperrt " . into ( ) ,
)
. await ;
assert! ( ! text . contains ( " Ottensheim Boot " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" Ottensheim Boot " . into ( ) ,
" rower " . into ( ) ,
" Schiffsführer darf dieses Boot nicht verwenden " . into ( ) ,
)
. await ;
}
#[ sqlx::test ]
fn test_cox_sees_appropriate_boats ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let mut client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=cox&password=cox " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
sqlx ::query ( " DELETE FROM logbook; " )
. execute ( & db )
. await
. unwrap ( ) ;
let req = client . get ( " /log " ) ;
let response = req . dispatch ( ) . await ;
let text = response . into_string ( ) . await . unwrap ( ) ;
//Sees all 1x
assert! ( text . contains ( " Haichenbach " ) ) ;
can_start_and_end_trip ( & db , & mut client , " Haichenbach " . into ( ) , " cox " . into ( ) ) . await ;
assert! ( text . contains ( " Joe " ) ) ;
can_start_and_end_trip ( & db , & mut client , " Joe " . into ( ) , " cox " . into ( ) ) . await ;
assert! ( text . contains ( " Kaputtes Boot :-( " ) ) ;
can_start_and_end_trip ( & db , & mut client , " Kaputtes Boot :-( " . into ( ) , " cox " . into ( ) ) . await ;
assert! ( text . contains ( " Sehr kaputtes Boot :-(( " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" Sehr kaputtes Boot :-(( " . into ( ) ,
" cox " . into ( ) ,
" Boot gesperrt " . into ( ) ,
)
. await ;
assert! ( text . contains ( " Ottensheim Boot " ) ) ;
can_start_and_end_trip ( & db , & mut client , " Ottensheim Boot " . into ( ) , " cox " . into ( ) ) . await ;
//Can't use private boats
assert! ( ! text . contains ( " private_boat_from_rower " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" private_boat_from_rower " . into ( ) ,
" cox " . into ( ) ,
" Schiffsführer darf dieses Boot nicht verwenden " . into ( ) ,
)
. await ;
assert! ( ! text . contains ( " second_private_boat_from_rower " ) ) ;
cant_start_trip (
& db ,
& mut client ,
" second_private_boat_from_rower " . into ( ) ,
" cox " . into ( ) ,
" Schiffsführer darf dieses Boot nicht verwenden " . into ( ) ,
)
. await ;
}
#[ sqlx::test ]
fn test_cant_end_trip_other_user ( ) {
let db = testdb! ( ) ;
let rocket = rocket ::build ( ) . manage ( db . clone ( ) ) ;
let rocket = crate ::tera ::config ( rocket ) ;
let client = Client ::tracked ( rocket ) . await . unwrap ( ) ;
let login = client
. post ( " /auth " )
. header ( ContentType ::Form ) // Set the content type to form
. body ( " name=rower2&password=rower " ) ; // Add the form data to the request body;
login . dispatch ( ) . await ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
let req = client
. post ( " /log/1 " )
. header ( ContentType ::Form )
2023-11-16 15:35:52 +01:00
. body ( format! ( " destination=Ottensheim&distance_in_km=25&shipmaster=1&steering_person=1&departure= {0} T10:00&arrival= {0} T12:00&rowers[]=1 " , current_date ) ) ;
2023-10-26 18:50:43 +02:00
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
2023-10-29 20:41:30 +01:00
" 5:errorEintrag 1 konnte nicht abgesendet werden (Fehler: NotYourEntry)! "
2023-10-26 18:50:43 +02:00
) ;
}
async fn can_start_and_end_trip (
db : & SqlitePool ,
client : & mut Client ,
boat_name : String ,
shipmaster_name : String ,
) {
let boat_id = Boat ::find_by_name ( db , boat_name ) . await . unwrap ( ) . id ;
let shipmaster_id = User ::find_by_name ( db , & shipmaster_name ) . await . unwrap ( ) . id ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
let req = client . post ( " /log " ) . header ( ContentType ::Form ) . body ( format! (
2023-11-16 15:35:52 +01:00
" boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id} "
2023-10-26 18:50:43 +02:00
) ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt erfolgreich hinzugefügt "
) ;
let log_id = Logbook ::highest_id ( db ) . await ;
let req = client
. post ( format! ( " /log/ {log_id} " ) )
. header ( ContentType ::Form )
2023-11-16 15:35:52 +01:00
. body ( format! ( " destination=Ottensheim&distance_in_km=25&shipmaster= {shipmaster_id} &steering_person= {shipmaster_id} &departure= {current_date} T10:00&arrival= {current_date} T12:00&rowers[]= {shipmaster_id} " ) ) ;
2023-10-26 18:50:43 +02:00
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! (
flash_cookie . value ( ) ,
" 7:successAusfahrt korrekt eingetragen "
) ;
}
async fn cant_start_trip (
db : & SqlitePool ,
client : & mut Client ,
boat_name : String ,
shipmaster_name : String ,
reason : String ,
) {
let boat_id = Boat ::find_by_name ( db , boat_name ) . await . unwrap ( ) . id ;
let shipmaster_id = User ::find_by_name ( db , & shipmaster_name ) . await . unwrap ( ) . id ;
2023-11-16 15:35:52 +01:00
let current_date = chrono ::Local ::now ( ) . format ( " %Y-%m-%d " ) . to_string ( ) ;
2023-10-26 18:50:43 +02:00
let req = client . post ( " /log " ) . header ( ContentType ::Form ) . body ( format! (
2023-11-16 15:35:52 +01:00
" boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id} "
2023-10-26 18:50:43 +02:00
) ) ;
let response = req . dispatch ( ) . await ;
assert_eq! ( response . status ( ) , Status ::SeeOther ) ;
assert_eq! ( response . headers ( ) . get ( " Location " ) . next ( ) , Some ( " /log " ) ) ;
let flash_cookie = response
. cookies ( )
. get ( " _flash " )
. expect ( " Expected flash cookie " ) ;
assert_eq! ( flash_cookie . value ( ) , format! ( " 5:error {} " , reason ) ) ;
}
}