@@ -4,13 +4,13 @@ use assertions::is_removal_cookie;
44use fixtures:: { SessionFixture , spy_store, store} ;
55use googletest:: {
66 assert_that,
7- prelude:: { eq , none, not} ,
7+ prelude:: { empty , eq , len , none, not} ,
88} ;
99use helpers:: SetCookie ;
1010use itertools:: Itertools ;
1111use pavex_session:: {
1212 IncomingSession , Session , SessionConfig , SessionId ,
13- config:: { MissingServerState , ServerStateCreation } ,
13+ config:: { MissingServerState , ServerStateCreation , TtlExtensionTrigger } ,
1414} ;
1515
1616mod assertions;
@@ -200,8 +200,109 @@ async fn server_state_can_be_cleared_without_invalidating_the_session() {
200200 assert_eq ! ( cookie. id( ) , fixture. id( ) ) ;
201201
202202 // The server state is present, but empty.
203- let server_state = store. load ( & fixture. id ) . await . unwrap ( ) ;
204- assert ! ( server_state. is_some( ) ) ;
203+ let server_state = store. load ( & fixture. id ) . await . unwrap ( ) . unwrap ( ) ;
204+ assert_that ! ( server_state. state, empty( ) ) ;
205+ }
206+
207+ #[ tokio:: test]
208+ async fn store_is_not_touched_if_you_clear_an_empty_server_state_and_ttl_is_configured_to_update_on_changes ( )
209+ {
210+ let ( ( store, call_tracker) , mut config) = ( spy_store ( ) , SessionConfig :: default ( ) ) ;
211+ config. state . extend_ttl = TtlExtensionTrigger :: OnStateChanges ;
212+
213+ let fixture = SessionFixture :: default ( ) ;
214+ let incoming = fixture. setup ( & store) . await ;
215+ let mut session = Session :: new ( & store, & config, Some ( incoming) ) ;
216+ assert ! ( session. server_mut( ) . is_empty( ) . await . unwrap( ) ) ;
217+ // Otherwise `create` and `load` will show up in the operation log.
218+ call_tracker. reset_operation_log ( ) . await ;
219+
220+ session. server_mut ( ) . clear ( ) . await . unwrap ( ) ;
221+
222+ let cookie = session. finalize ( ) . await . unwrap ( ) . unwrap ( ) ;
223+
224+ // It's not a removal cookie!
225+ let cookie = SetCookie :: parse ( cookie) ;
226+ assert_eq ! ( cookie. id( ) , fixture. id( ) ) ;
227+
228+ call_tracker. assert_store_was_untouched ( ) . await ;
229+ }
230+
231+ #[ tokio:: test]
232+ async fn ttl_is_updated_if_server_state_is_loaded_but_unchanged ( ) {
233+ let ( ( store, call_tracker) , mut config) = ( spy_store ( ) , SessionConfig :: default ( ) ) ;
234+ // Always extend TTL
235+ config. state . ttl_extension_threshold = None ;
236+
237+ let mut fixture = SessionFixture :: default ( ) ;
238+ fixture. server_ttl = Some ( config. state . ttl ) ;
239+ let incoming = fixture. setup ( & store) . await ;
240+ let mut session = Session :: new ( & store, & config, Some ( incoming) ) ;
241+ assert ! ( session. server_mut( ) . is_empty( ) . await . unwrap( ) ) ;
242+ // Otherwise `create` and `load` will show up in the operation log.
243+ call_tracker. reset_operation_log ( ) . await ;
244+
245+ let cookie = session. finalize ( ) . await . unwrap ( ) . unwrap ( ) ;
246+
247+ // It's not a removal cookie!
248+ let cookie = SetCookie :: parse ( cookie) ;
249+ assert_eq ! ( cookie. id( ) , fixture. id( ) ) ;
250+
251+ let oplog = call_tracker. operation_log ( ) . await ;
252+ assert_that ! ( oplog, len( eq( 1 ) ) ) ;
253+ assert ! ( oplog[ 0 ] . starts_with( "update-ttl" ) ) ;
254+ }
255+
256+ #[ tokio:: test]
257+ async fn ttl_is_not_updated_if_server_state_is_unchanged_but_ttl_threshold_is_not_met ( ) {
258+ let ( ( store, call_tracker) , config) = ( spy_store ( ) , SessionConfig :: default ( ) ) ;
259+ let ttl_extension_threshold = config. state . ttl_extension_threshold . unwrap ( ) ;
260+ assert ! ( ttl_extension_threshold. inner( ) < 0.9 ) ;
261+
262+ let mut fixture = SessionFixture :: default ( ) ;
263+ // We start at full TTL
264+ fixture. server_ttl = Some ( config. state . ttl ) ;
265+ let incoming = fixture. setup ( & store) . await ;
266+ let mut session = Session :: new ( & store, & config, Some ( incoming) ) ;
267+ assert ! ( session. server_mut( ) . is_empty( ) . await . unwrap( ) ) ;
268+ // Otherwise `create` and `load` will show up in the operation log.
269+ call_tracker. reset_operation_log ( ) . await ;
270+
271+ let cookie = session. finalize ( ) . await . unwrap ( ) . unwrap ( ) ;
272+
273+ // It's not a removal cookie!
274+ let cookie = SetCookie :: parse ( cookie) ;
275+ assert_eq ! ( cookie. id( ) , fixture. id( ) ) ;
276+
277+ let oplog = call_tracker. operation_log ( ) . await ;
278+ assert_that ! ( oplog, empty( ) ) ;
279+ }
280+
281+ #[ tokio:: test]
282+ async fn ttl_is_updated_if_server_state_is_unchanged_and_ttl_threshold_is_met ( ) {
283+ let ( ( store, call_tracker) , config) = ( spy_store ( ) , SessionConfig :: default ( ) ) ;
284+ let ttl_extension_threshold = config. state . ttl_extension_threshold . unwrap ( ) ;
285+
286+ let mut fixture = SessionFixture :: default ( ) ;
287+ // We start below the threshold
288+ let ttl = config. state . ttl . as_secs_f32 ( ) * ( ttl_extension_threshold. inner ( ) - 0.1 ) ;
289+ assert ! ( ttl > 0. ) ;
290+ fixture. server_ttl = Some ( std:: time:: Duration :: from_secs_f32 ( ttl) ) ;
291+ let incoming = fixture. setup ( & store) . await ;
292+ let mut session = Session :: new ( & store, & config, Some ( incoming) ) ;
293+ assert ! ( session. server_mut( ) . is_empty( ) . await . unwrap( ) ) ;
294+ // Otherwise `create` and `load` will show up in the operation log.
295+ call_tracker. reset_operation_log ( ) . await ;
296+
297+ let cookie = session. finalize ( ) . await . unwrap ( ) . unwrap ( ) ;
298+
299+ // It's not a removal cookie!
300+ let cookie = SetCookie :: parse ( cookie) ;
301+ assert_eq ! ( cookie. id( ) , fixture. id( ) ) ;
302+
303+ let oplog = call_tracker. operation_log ( ) . await ;
304+ assert_that ! ( oplog, len( eq( 1 ) ) ) ;
305+ assert ! ( oplog[ 0 ] . starts_with( "update-ttl" ) ) ;
205306}
206307
207308#[ tokio:: test]
0 commit comments