diff --git a/Cargo.toml b/Cargo.toml index e53d1ba..c516d98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,15 @@ tonic = "0.14" tonic-prost = "0.14" tracing = { version = "0.1" } tracing-subscriber = { version = "0.3" } +tokio-stream = { version = "0.1.17", features = ["sync"], optional = true } [dev-dependencies] tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] } tokio = { version = "1.48", features = ["test-util"] } tokio-stream = { version = "0.1.17", features = ["sync"] } - [build-dependencies] tonic-prost-build = { version = "0.14", features = ["cleanup-markdown"] } + +[features] +test-utils = ["dep:tokio-stream"] diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f6da5c2..b678fe0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,6 +13,9 @@ - The microgrid client now tolerates the API server being absent or returning incomplete data at startup. `MicrogridClientHandle::try_new` establishes the gRPC connection lazily, so it succeeds regardless of whether the server is reachable; transient stream errors are then handled by the existing per-stream retry loop. `LogicalMeterHandle::try_new` (and therefore `Microgrid::try_new`) wraps the entire component-graph setup — listing components, listing connections, and building the graph — in a single retry loop that sleeps 3 seconds between attempts, so applications block waiting for the server and a valid graph instead of exiting with an error. - `Bounds::combine_parallel`, `Bounds::intersect`, and `Bounds::merge_if_overlapping` are now public, allowing external callers to combine bounds without going through higher-level types. +- Put test utils under a feature gate. +- Added `MockMicrogridApiClient::augment_electrical_component_bounds`: It captures requests so that these can be used in test cases. Obtain the list of captured requests using `MockMicrogridApiClient::augment_bounds_calls_handle` (also new). +- Added `MockComponent.add_component_bounds`: It allows to add metric bounds to a mock component. ## Bug Fixes diff --git a/src/client.rs b/src/client.rs index 59acbd7..d36e8d4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -18,5 +18,6 @@ pub use proto::common::microgrid::electrical_components::{ ElectricalComponent, ElectricalComponentCategory, }; -#[cfg(test)] -pub(crate) mod test_utils; +#[cfg(any(test, feature = "test-utils"))] +#[expect(clippy::unwrap_used, clippy::panic, clippy::expect_used)] +pub mod test_utils; diff --git a/src/client/proto.rs b/src/client/proto.rs index fab25a5..01ab58e 100644 --- a/src/client/proto.rs +++ b/src/client/proto.rs @@ -17,7 +17,7 @@ mod pb { // Only export what we need pub use pb::frequenz::api::common::v1alpha8 as common; pub use pb::frequenz::api::microgrid::v1alpha18 as microgrid; -#[cfg(test)] +#[cfg(any(test, feature = "test-utils"))] pub use pb::google; mod electrical_component; diff --git a/src/client/test_utils.rs b/src/client/test_utils.rs index e9d4741..1ab50ee 100644 --- a/src/client/test_utils.rs +++ b/src/client/test_utils.rs @@ -6,23 +6,25 @@ mod tokio_synced_clock; pub use tokio_synced_clock::TokioSyncedClock; +use std::sync::Mutex; use std::{sync::Arc, time::SystemTime}; - use tokio_stream::wrappers::ReceiverStream; use tonic::Response; +use super::MicrogridApiClient; use crate::wall_clock_timer::Clock as _; use crate::{ client::proto::{ common::{ metrics::{ - Metric, MetricSample, MetricValueVariant, SimpleMetricValue, metric_value_variant, + Bounds, Metric, MetricSample, MetricValueVariant, SimpleMetricValue, + metric_value_variant, }, microgrid::electrical_components::{ ElectricalComponent, ElectricalComponentCategory, ElectricalComponentCategorySpecificInfo, ElectricalComponentConnection, ElectricalComponentStateCode, ElectricalComponentStateSnapshot, - ElectricalComponentTelemetry, Inverter, InverterType, + ElectricalComponentTelemetry, Inverter, InverterType, MetricConfigBounds, electrical_component_category_specific_info::Kind, }, }, @@ -38,8 +40,6 @@ use crate::{ quantity::{Current, Power, ReactivePower, Voltage}, }; -use super::MicrogridApiClient; - /// A mock implementation of the `MicrogridApiClient` trait for testing purposes. /// /// This mock client allows setting predefined responses for each method, @@ -52,6 +52,7 @@ pub struct MockMicrogridApiClient { /// share a clone with [`LogicalMeterActor`], and pass another in via /// [`MockMicrogridApiClient::new_with_clock`]. clock: TokioSyncedClock, + pub augment_bounds_calls: Arc>>, } /// One row per emitted telemetry frame: `(power, reactive_power, voltage, @@ -182,6 +183,21 @@ impl MockComponent { self } + pub fn add_component_bounds( + mut self, + metric: i32, + lower: Option, + upper: Option, + ) -> Self { + self.component + .metric_config_bounds + .push(MetricConfigBounds { + metric, + config_bounds: Some(Bounds { lower, upper }), + }); + self + } + pub fn with_power(mut self, power: Vec) -> Self { let mut metrics = self.metrics; for (i, p) in power.iter().enumerate() { @@ -288,6 +304,7 @@ impl MockMicrogridApiClient { components: vec![], connections: vec![], clock, + augment_bounds_calls: Arc::new(Mutex::new(Vec::new())), }; fn traverse(node: &MockComponent, client: &mut MockMicrogridApiClient) { @@ -305,6 +322,13 @@ impl MockMicrogridApiClient { this_client } + + /// Return a handle to captured augment bounds requests. + pub fn augment_bounds_calls_handle( + &self, + ) -> Arc>> { + self.augment_bounds_calls.clone() + } } #[async_trait::async_trait] @@ -500,7 +524,13 @@ impl MicrogridApiClient for MockMicrogridApiClient { _request: impl tonic::IntoRequest + Send, ) -> std::result::Result, tonic::Status> { - unimplemented!() + // Capture calls for tests + let req = _request.into_request().into_inner(); + self.augment_bounds_calls.lock().unwrap().push(req); + + Ok(Response::new(AugmentElectricalComponentBoundsResponse { + valid_until_time: None, + })) } } diff --git a/src/lib.rs b/src/lib.rs index 6e11614..565eab9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,3 +38,6 @@ pub(crate) mod wall_clock_timer; mod microgrid; pub use microgrid::{BatteryPool, Microgrid}; + +#[cfg(any(test, feature = "test-utils"))] +pub use client::test_utils;