Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 3 additions & 2 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
2 changes: 1 addition & 1 deletion src/client/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 36 additions & 6 deletions src/client/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
Expand All @@ -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,
Expand All @@ -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<Mutex<Vec<AugmentElectricalComponentBoundsRequest>>>,
}

/// One row per emitted telemetry frame: `(power, reactive_power, voltage,
Expand Down Expand Up @@ -182,6 +183,21 @@ impl MockComponent {
self
}

pub fn add_component_bounds(
mut self,
metric: i32,
lower: Option<f32>,
upper: Option<f32>,
) -> Self {
self.component
.metric_config_bounds
.push(MetricConfigBounds {
metric,
config_bounds: Some(Bounds { lower, upper }),
});
self
}

pub fn with_power(mut self, power: Vec<f32>) -> Self {
let mut metrics = self.metrics;
for (i, p) in power.iter().enumerate() {
Expand Down Expand Up @@ -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) {
Expand All @@ -305,6 +322,13 @@ impl MockMicrogridApiClient {

this_client
}

/// Return a handle to captured augment bounds requests.
pub fn augment_bounds_calls_handle(
&self,
) -> Arc<Mutex<Vec<AugmentElectricalComponentBoundsRequest>>> {
self.augment_bounds_calls.clone()
}
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -500,7 +524,13 @@ impl MicrogridApiClient for MockMicrogridApiClient {
_request: impl tonic::IntoRequest<AugmentElectricalComponentBoundsRequest> + Send,
) -> std::result::Result<tonic::Response<AugmentElectricalComponentBoundsResponse>, 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,
}))
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Loading