@@ -274,6 +274,18 @@ impl<C: Clock> LogicalMeterActor<C> {
274274 config : LogicalMeterConfig ,
275275 clock : C ,
276276 ) -> Result < Self , Error > {
277+ if config. resampling_interval <= chrono:: TimeDelta :: zero ( ) {
278+ return Err ( Error :: invalid_config ( format ! (
279+ "resampling_interval must be positive, got {:?}" ,
280+ config. resampling_interval
281+ ) ) ) ;
282+ }
283+ if config. max_age_in_intervals > i32:: MAX as u32 {
284+ return Err ( Error :: invalid_config ( format ! (
285+ "max_age_in_intervals must fit in i32, got {}" ,
286+ config. max_age_in_intervals
287+ ) ) ) ;
288+ }
277289 let timer = WallClockTimer :: try_new ( config. resampling_interval , clock) ?;
278290 // Resamplers created before the first tick use `resampler_ts` as
279291 // their start; setting it one interval before the first scheduled
@@ -400,9 +412,8 @@ impl<C: Clock> LogicalMeterActor<C> {
400412 frequenz_resampling:: Resampler :: new (
401413 self . config . resampling_interval ,
402414 function,
403- // The resampler expects max age to be i32, so we cap it if
404- // the user provided a higher value.
405- self . config . max_age_in_intervals . min ( i32:: MAX as u32 ) as i32 ,
415+ // Validated at construction to fit in `i32`.
416+ self . config . max_age_in_intervals as i32 ,
406417 start,
407418 false ,
408419 )
@@ -704,6 +715,39 @@ mod tests {
704715 ) ;
705716 }
706717
718+ #[ tokio:: test]
719+ async fn test_nonpositive_resampling_interval_rejected ( ) {
720+ let api_client = MockMicrogridApiClient :: new ( MockComponent :: grid ( 1 ) ) ;
721+ let client = MicrogridClientHandle :: new_from_client ( api_client) ;
722+ for bad in [ TimeDelta :: zero ( ) , -TimeDelta :: try_milliseconds ( 1 ) . unwrap ( ) ] {
723+ let ( _tx, rx) = mpsc:: channel ( 1 ) ;
724+ let result = LogicalMeterActor :: try_new (
725+ rx,
726+ client. clone ( ) ,
727+ LogicalMeterConfig :: new ( bad) ,
728+ TokioSyncedClock :: new ( ) ,
729+ ) ;
730+ match result {
731+ Err ( e) => assert_eq ! ( e. kind( ) , crate :: ErrorKind :: InvalidConfig ) ,
732+ Ok ( _) => panic ! ( "expected error for interval {bad:?}" ) ,
733+ }
734+ }
735+ }
736+
737+ #[ tokio:: test]
738+ async fn test_max_age_in_intervals_overflow_rejected ( ) {
739+ let api_client = MockMicrogridApiClient :: new ( MockComponent :: grid ( 1 ) ) ;
740+ let client = MicrogridClientHandle :: new_from_client ( api_client) ;
741+ let ( _tx, rx) = mpsc:: channel ( 1 ) ;
742+ let config = LogicalMeterConfig :: new ( TimeDelta :: try_seconds ( 1 ) . unwrap ( ) )
743+ . with_max_age_in_intervals ( i32:: MAX as u32 + 1 ) ;
744+ let result = LogicalMeterActor :: try_new ( rx, client, config, TokioSyncedClock :: new ( ) ) ;
745+ match result {
746+ Err ( e) => assert_eq ! ( e. kind( ) , crate :: ErrorKind :: InvalidConfig ) ,
747+ Ok ( _) => panic ! ( "expected error for over-i32::MAX max_age_in_intervals" ) ,
748+ }
749+ }
750+
707751 async fn next_sample ( stream : & mut BroadcastStream < Sample < Power > > ) -> Option < Sample < Power > > {
708752 loop {
709753 match tokio:: time:: timeout ( std:: time:: Duration :: from_secs ( 10 ) , stream. next ( ) ) . await {
0 commit comments