108 lines
2.9 KiB
Rust
Raw Normal View History

2026-01-19 08:34:17 +01:00
use testing_framework_core::{
nodes::ApiClient,
scenario::{StartNodeOptions, StartedNode},
topology::{
config::{TopologyBuildError, TopologyBuilder, TopologyConfig},
readiness::{ReadinessCheck, ReadinessError},
},
};
use thiserror::Error;
use crate::node_control::{LocalDynamicError, LocalDynamicNodes, ReadinessNode};
mod readiness;
use readiness::ManualNetworkReadiness;
#[derive(Debug, Error)]
pub enum ManualClusterError {
#[error("failed to build topology: {source}")]
Build {
#[source]
source: TopologyBuildError,
},
#[error(transparent)]
Dynamic(#[from] LocalDynamicError),
}
/// Imperative, in-process cluster that can start nodes on demand.
pub struct ManualCluster {
nodes: LocalDynamicNodes,
}
impl ManualCluster {
pub fn from_config(config: TopologyConfig) -> Result<Self, ManualClusterError> {
let builder = TopologyBuilder::new(config);
let descriptors = builder
.build()
.map_err(|source| ManualClusterError::Build { source })?;
let nodes = LocalDynamicNodes::new(
descriptors,
testing_framework_core::scenario::NodeClients::default(),
);
Ok(Self { nodes })
}
#[must_use]
pub fn node_client(&self, name: &str) -> Option<ApiClient> {
self.nodes.node_client(name)
}
pub async fn start_validator(&self, name: &str) -> Result<StartedNode, ManualClusterError> {
Ok(self
.nodes
.start_validator_with(name, StartNodeOptions::default())
.await?)
}
pub async fn start_executor(&self, name: &str) -> Result<StartedNode, ManualClusterError> {
Ok(self
.nodes
.start_executor_with(name, StartNodeOptions::default())
.await?)
}
pub async fn start_validator_with(
&self,
name: &str,
options: StartNodeOptions,
) -> Result<StartedNode, ManualClusterError> {
Ok(self.nodes.start_validator_with(name, options).await?)
}
pub async fn start_executor_with(
&self,
name: &str,
options: StartNodeOptions,
) -> Result<StartedNode, ManualClusterError> {
Ok(self.nodes.start_executor_with(name, options).await?)
}
pub fn stop_all(&self) {
self.nodes.stop_all();
}
pub async fn wait_network_ready(&self) -> Result<(), ReadinessError> {
let nodes = self.nodes.readiness_nodes();
if self.is_singleton(&nodes) {
return Ok(());
}
self.wait_nodes_ready(nodes).await
}
fn is_singleton(&self, nodes: &[ReadinessNode]) -> bool {
nodes.len() <= 1
}
async fn wait_nodes_ready(&self, nodes: Vec<ReadinessNode>) -> Result<(), ReadinessError> {
ManualNetworkReadiness::new(nodes).wait().await
}
}
2026-01-19 10:05:58 +01:00
impl Drop for ManualCluster {
fn drop(&mut self) {
self.stop_all();
}
}