Skip to content

Commit fe9327c

Browse files
shsmso7e3nim4d
authored andcommitted
Reject non-positive resampling_interval in LogicalMeterActor::try_new
Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
1 parent 45621af commit fe9327c

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ ErrorKind!(
5959
(DroppedUnusedFormulas, dropped_unused_formulas),
6060
(FormulaEngineError, formula_engine_error),
6161
(InvalidComponent, invalid_component),
62+
(InvalidConfig, invalid_config),
6263
(Internal, internal),
6364
(APIServerError, api_server_error),
6465
);

src/logical_meter/logical_meter_actor.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)