From fb4c58cc489ab17dca303ecd7471054cac70876e Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:20:12 +0100 Subject: [PATCH 01/13] Unify manual cluster control surface --- testing-framework/core/src/runtime/manual.rs | 12 ++--------- .../deployers/local/src/manual/mod.rs | 20 ++++++------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/testing-framework/core/src/runtime/manual.rs b/testing-framework/core/src/runtime/manual.rs index dfa0292..4e8af53 100644 --- a/testing-framework/core/src/runtime/manual.rs +++ b/testing-framework/core/src/runtime/manual.rs @@ -1,15 +1,7 @@ use async_trait::async_trait; -use crate::scenario::{Application, DynError, NodeControlHandle, StartNodeOptions, StartedNode}; +use crate::scenario::{Application, ClusterWaitHandle, NodeControlHandle}; /// Interface for imperative, deployer-backed manual clusters. #[async_trait] -pub trait ManualClusterHandle: NodeControlHandle { - async fn start_node_with( - &self, - name: &str, - options: StartNodeOptions, - ) -> Result, DynError>; - - async fn wait_network_ready(&self) -> Result<(), DynError>; -} +pub trait ManualClusterHandle: NodeControlHandle + ClusterWaitHandle {} diff --git a/testing-framework/deployers/local/src/manual/mod.rs b/testing-framework/deployers/local/src/manual/mod.rs index 36a4019..028a690 100644 --- a/testing-framework/deployers/local/src/manual/mod.rs +++ b/testing-framework/deployers/local/src/manual/mod.rs @@ -1,8 +1,8 @@ use testing_framework_core::{ manual::ManualClusterHandle, scenario::{ - DynError, ExternalNodeSource, NodeClients, NodeControlHandle, ReadinessError, - StartNodeOptions, StartedNode, + ClusterWaitHandle, DynError, ExternalNodeSource, NodeClients, NodeControlHandle, + ReadinessError, StartNodeOptions, StartedNode, }, }; use thiserror::Error; @@ -157,19 +157,11 @@ impl NodeControlHandle for ManualCluster { } #[async_trait::async_trait] -impl ManualClusterHandle for ManualCluster { - async fn start_node_with( - &self, - name: &str, - options: StartNodeOptions, - ) -> Result, DynError> { - self.nodes - .start_node_with(name, options) - .await - .map_err(|err| err.into()) - } - +impl ClusterWaitHandle for ManualCluster { async fn wait_network_ready(&self) -> Result<(), DynError> { self.wait_network_ready().await.map_err(|err| err.into()) } } + +#[async_trait::async_trait] +impl ManualClusterHandle for ManualCluster {} From 365526d23609107b6253b6b9f4ea1a63095203a2 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:21:21 +0100 Subject: [PATCH 02/13] Reduce runtime wait surface --- .../core/src/scenario/runtime/context.rs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/testing-framework/core/src/scenario/runtime/context.rs b/testing-framework/core/src/scenario/runtime/context.rs index 65f28a5..2eaa256 100644 --- a/testing-framework/core/src/scenario/runtime/context.rs +++ b/testing-framework/core/src/scenario/runtime/context.rs @@ -121,22 +121,12 @@ impl RunContext { self.node_control.clone() } - #[must_use] - pub fn cluster_wait(&self) -> Option>> { - self.cluster_wait.clone() - } - #[must_use] pub const fn controls_nodes(&self) -> bool { self.node_control.is_some() } - #[must_use] - pub const fn can_wait_network_ready(&self) -> bool { - self.cluster_wait.is_some() - } - - pub async fn wait_network_ready(&self) -> Result<(), DynError> { + pub(crate) async fn wait_network_ready(&self) -> Result<(), DynError> { self.require_cluster_wait()?.wait_network_ready().await } @@ -146,7 +136,9 @@ impl RunContext { } fn require_cluster_wait(&self) -> Result>, DynError> { - self.cluster_wait() + self.cluster_wait + .as_ref() + .map(Arc::clone) .ok_or_else(|| RunContextCapabilityError::MissingClusterWait.into()) } } @@ -192,10 +184,6 @@ impl RunHandle { pub fn context(&self) -> &RunContext { &self.run_context } - - pub async fn wait_network_ready(&self) -> Result<(), DynError> { - self.run_context.wait_network_ready().await - } } /// Derived metrics about the current run timing. From 743e31fa3c853aaeac52e9d367c3f3cfad086704 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:22:16 +0100 Subject: [PATCH 03/13] Hide runner context storage details --- testing-framework/core/src/scenario/runtime/runner.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing-framework/core/src/scenario/runtime/runner.rs b/testing-framework/core/src/scenario/runtime/runner.rs index 652d740..16c9002 100644 --- a/testing-framework/core/src/scenario/runtime/runner.rs +++ b/testing-framework/core/src/scenario/runtime/runner.rs @@ -45,8 +45,8 @@ impl Runner { /// Access the underlying run context. #[must_use] - pub fn context(&self) -> Arc> { - Arc::clone(&self.context) + pub fn context(&self) -> &RunContext { + self.context.as_ref() } pub async fn wait_network_ready(&self) -> Result<(), DynError> { @@ -71,7 +71,7 @@ impl Runner { where Caps: Send + Sync, { - let context = self.context(); + let context = Arc::clone(&self.context); let run_duration = scenario.duration(); let workloads = scenario.workloads().to_vec(); let expectation_count = scenario.expectations().len(); From 034e56efa5b4cf25030a0dece4e74f43472cc0bc Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:24:47 +0100 Subject: [PATCH 04/13] Reduce source-mode leakage in run context --- .../core/src/scenario/runtime/context.rs | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/testing-framework/core/src/scenario/runtime/context.rs b/testing-framework/core/src/scenario/runtime/context.rs index 2eaa256..a136fb4 100644 --- a/testing-framework/core/src/scenario/runtime/context.rs +++ b/testing-framework/core/src/scenario/runtime/context.rs @@ -1,10 +1,7 @@ use std::{sync::Arc, time::Duration}; use super::{metrics::Metrics, node_clients::ClusterClient}; -use crate::scenario::{ - Application, BorrowedNode, ClusterWaitHandle, DynError, ManagedNode, NodeClients, - NodeControlHandle, -}; +use crate::scenario::{Application, ClusterWaitHandle, DynError, NodeClients, NodeControlHandle}; #[derive(Debug, thiserror::Error)] enum RunContextCapabilityError { @@ -71,26 +68,6 @@ impl RunContext { self.node_clients.random_client() } - #[must_use] - pub fn managed_nodes(&self) -> Vec> { - self.node_clients.managed_nodes() - } - - #[must_use] - pub fn borrowed_nodes(&self) -> Vec> { - self.node_clients.borrowed_nodes() - } - - #[must_use] - pub fn find_managed_node(&self, identity: &str) -> Option> { - self.node_clients.find_managed(identity) - } - - #[must_use] - pub fn find_borrowed_node(&self, identity: &str) -> Option> { - self.node_clients.find_borrowed(identity) - } - #[must_use] pub fn feed(&self) -> ::Feed { self.feed.clone() From 23838867c22bff99b0b81c3a6f31be2e5a3d4ed4 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:26:33 +0100 Subject: [PATCH 05/13] Trim node client public surface --- .../core/src/scenario/runtime/node_clients.rs | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/testing-framework/core/src/scenario/runtime/node_clients.rs b/testing-framework/core/src/scenario/runtime/node_clients.rs index 363e80d..ce31dcf 100644 --- a/testing-framework/core/src/scenario/runtime/node_clients.rs +++ b/testing-framework/core/src/scenario/runtime/node_clients.rs @@ -1,6 +1,6 @@ use rand::{seq::SliceRandom as _, thread_rng}; -use super::inventory::{BorrowedNode, ManagedNode, NodeInventory}; +use super::inventory::NodeInventory; use crate::scenario::{Application, DynError}; /// Collection of API clients for the node set. @@ -79,30 +79,6 @@ impl NodeClients { self.inventory.clear(); } - #[must_use] - /// Returns a cloned snapshot of managed node handles. - pub fn managed_nodes(&self) -> Vec> { - self.inventory.managed_nodes() - } - - #[must_use] - /// Returns a cloned snapshot of borrowed node handles. - pub fn borrowed_nodes(&self) -> Vec> { - self.inventory.borrowed_nodes() - } - - #[must_use] - /// Finds a managed node by canonical identity. - pub fn find_managed(&self, identity: &str) -> Option> { - self.inventory.find_managed(identity) - } - - #[must_use] - /// Finds a borrowed node by canonical identity. - pub fn find_borrowed(&self, identity: &str) -> Option> { - self.inventory.find_borrowed(identity) - } - fn shuffled_snapshot(&self) -> Vec { let mut clients = self.snapshot(); clients.shuffle(&mut thread_rng()); From da2f51d46f968f446ab7c7111c38811e4149e19e Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:32:10 +0100 Subject: [PATCH 06/13] Make attach source construction explicit --- .../core/src/scenario/sources/model.rs | 22 +++++++------------ .../deployers/compose/src/deployer/mod.rs | 10 +++++++-- .../compose/src/deployer/orchestrator.rs | 2 +- .../deployers/k8s/src/deployer/mod.rs | 5 ++++- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/testing-framework/core/src/scenario/sources/model.rs b/testing-framework/core/src/scenario/sources/model.rs index 0e14091..0a3dc69 100644 --- a/testing-framework/core/src/scenario/sources/model.rs +++ b/testing-framework/core/src/scenario/sources/model.rs @@ -21,13 +21,10 @@ impl AttachSource { } #[must_use] - pub fn with_namespace(self, namespace: String) -> Self { - match self { - Self::K8s { label_selector, .. } => Self::K8s { - namespace: Some(namespace), - label_selector, - }, - other => other, + pub fn k8s_in_namespace(label_selector: String, namespace: String) -> Self { + Self::K8s { + namespace: Some(namespace), + label_selector, } } @@ -40,13 +37,10 @@ impl AttachSource { } #[must_use] - pub fn with_project(self, project: String) -> Self { - match self { - Self::Compose { services, .. } => Self::Compose { - project: Some(project), - services, - }, - other => other, + pub fn compose_in_project(services: Vec, project: String) -> Self { + Self::Compose { + project: Some(project), + services, } } } diff --git a/testing-framework/deployers/compose/src/deployer/mod.rs b/testing-framework/deployers/compose/src/deployer/mod.rs index 60a88de..2f809c7 100644 --- a/testing-framework/deployers/compose/src/deployer/mod.rs +++ b/testing-framework/deployers/compose/src/deployer/mod.rs @@ -50,7 +50,10 @@ impl ComposeDeploymentMetadata { .project_name() .ok_or(ComposeMetadataError::MissingProjectName)?; - Ok(AttachSource::compose(Vec::new()).with_project(project_name.to_owned())) + Ok(AttachSource::compose_in_project( + Vec::new(), + project_name.to_owned(), + )) } /// Builds an attach source for the same compose project. @@ -62,7 +65,10 @@ impl ComposeDeploymentMetadata { .project_name() .ok_or(ComposeMetadataError::MissingProjectName)?; - Ok(AttachSource::compose(services).with_project(project_name.to_owned())) + Ok(AttachSource::compose_in_project( + services, + project_name.to_owned(), + )) } } diff --git a/testing-framework/deployers/compose/src/deployer/orchestrator.rs b/testing-framework/deployers/compose/src/deployer/orchestrator.rs index 831f3fe..ddd88f0 100644 --- a/testing-framework/deployers/compose/src/deployer/orchestrator.rs +++ b/testing-framework/deployers/compose/src/deployer/orchestrator.rs @@ -321,7 +321,7 @@ impl DeploymentOrchestrator { fn managed_cluster_wait(&self, project_name: String) -> Arc> { Arc::new(ComposeAttachedClusterWait::::new( compose_runner_host(), - AttachSource::compose(Vec::new()).with_project(project_name), + AttachSource::compose_in_project(Vec::new(), project_name), )) } diff --git a/testing-framework/deployers/k8s/src/deployer/mod.rs b/testing-framework/deployers/k8s/src/deployer/mod.rs index e16ec45..43f0ff5 100644 --- a/testing-framework/deployers/k8s/src/deployer/mod.rs +++ b/testing-framework/deployers/k8s/src/deployer/mod.rs @@ -41,6 +41,9 @@ impl K8sDeploymentMetadata { .label_selector() .ok_or(K8sMetadataError::MissingLabelSelector)?; - Ok(AttachSource::k8s(label_selector.to_owned()).with_namespace(namespace.to_owned())) + Ok(AttachSource::k8s_in_namespace( + label_selector.to_owned(), + namespace.to_owned(), + )) } } From 7e0cdb54f8064277074bca0b51c0c2399d0bf531 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:34:07 +0100 Subject: [PATCH 07/13] Make source orchestration plan opaque --- .../core/src/scenario/definition.rs | 12 ++------ .../src/scenario/runtime/orchestration/mod.rs | 4 +-- .../source_orchestration_plan.rs | 30 +++++++------------ .../runtime/orchestration/source_resolver.rs | 5 ++-- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/testing-framework/core/src/scenario/definition.rs b/testing-framework/core/src/scenario/definition.rs index e8b3a4e..44b210c 100644 --- a/testing-framework/core/src/scenario/definition.rs +++ b/testing-framework/core/src/scenario/definition.rs @@ -11,7 +11,7 @@ use super::{ expectation::Expectation, runtime::{ context::RunMetrics, - orchestration::{SourceModeName, SourceOrchestrationPlan, SourceOrchestrationPlanError}, + orchestration::{SourceOrchestrationPlan, SourceOrchestrationPlanError}, }, workload::Workload, }; @@ -724,19 +724,11 @@ fn build_source_orchestration_plan( fn source_plan_error_to_build_error(error: SourceOrchestrationPlanError) -> ScenarioBuildError { match error { SourceOrchestrationPlanError::SourceModeNotWiredYet { mode } => { - ScenarioBuildError::SourceModeNotWiredYet { - mode: source_mode_name(mode), - } + ScenarioBuildError::SourceModeNotWiredYet { mode } } } } -const fn source_mode_name(mode: SourceModeName) -> &'static str { - match mode { - SourceModeName::Attached => "Attached", - } -} - impl Builder { #[must_use] pub fn enable_node_control(self) -> Builder { diff --git a/testing-framework/core/src/scenario/runtime/orchestration/mod.rs b/testing-framework/core/src/scenario/runtime/orchestration/mod.rs index 9e71458..a17ce28 100644 --- a/testing-framework/core/src/scenario/runtime/orchestration/mod.rs +++ b/testing-framework/core/src/scenario/runtime/orchestration/mod.rs @@ -3,9 +3,9 @@ mod source_orchestration_plan; #[allow(dead_code)] mod source_resolver; +pub(crate) use source_orchestration_plan::SourceOrchestrationMode; pub use source_orchestration_plan::{ - ManagedSource, SourceModeName, SourceOrchestrationMode, SourceOrchestrationPlan, - SourceOrchestrationPlanError, + ManagedSource, SourceOrchestrationPlan, SourceOrchestrationPlanError, }; pub use source_resolver::{ build_source_orchestration_plan, orchestrate_sources, orchestrate_sources_with_providers, diff --git a/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs b/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs index dd57ee9..ed72ae3 100644 --- a/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs +++ b/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs @@ -1,5 +1,3 @@ -use std::fmt; - use crate::scenario::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy}; /// Explicit descriptor for managed node sourcing. @@ -15,7 +13,7 @@ pub enum ManagedSource { /// This is scaffolding-only and is intentionally not executed by deployers /// yet. #[derive(Clone, Debug, Eq, PartialEq)] -pub enum SourceOrchestrationMode { +pub(crate) enum SourceOrchestrationMode { Managed { managed: ManagedSource, external: Vec, @@ -34,28 +32,15 @@ pub enum SourceOrchestrationMode { /// This captures only mapping-time source intent and readiness policy. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SourceOrchestrationPlan { - pub mode: SourceOrchestrationMode, - pub readiness_policy: SourceReadinessPolicy, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum SourceModeName { - Attached, -} - -impl fmt::Display for SourceModeName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Attached => f.write_str("Attached"), - } - } + mode: SourceOrchestrationMode, + readiness_policy: SourceReadinessPolicy, } /// Validation failure while building orchestration plan from sources. #[derive(Debug, thiserror::Error)] pub enum SourceOrchestrationPlanError { #[error("source mode '{mode}' is not wired into deployers yet")] - SourceModeNotWiredYet { mode: SourceModeName }, + SourceModeNotWiredYet { mode: &'static str }, } impl SourceOrchestrationPlan { @@ -71,6 +56,11 @@ impl SourceOrchestrationPlan { }) } + #[must_use] + pub(crate) fn mode(&self) -> &SourceOrchestrationMode { + &self.mode + } + #[must_use] pub fn external_sources(&self) -> &[ExternalNodeSource] { match &self.mode { @@ -94,7 +84,7 @@ mod tests { .expect("attached sources should build a source orchestration plan"); assert!(matches!( - plan.mode, + plan.mode(), SourceOrchestrationMode::Attached { .. } )); } diff --git a/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs b/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs index 6ebd646..8ead379 100644 --- a/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs +++ b/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs @@ -52,7 +52,7 @@ pub async fn resolve_sources( plan: &SourceOrchestrationPlan, providers: &SourceProviders, ) -> Result, SourceResolveError> { - match &plan.mode { + match plan.mode() { SourceOrchestrationMode::Managed { managed, .. } => { let managed_nodes = providers.managed.provide(managed).await?; let external_nodes = providers.external.provide(plan.external_sources()).await?; @@ -115,7 +115,8 @@ pub async fn orchestrate_sources_with_providers( ) -> Result, DynError> { let resolved = resolve_sources(plan, &providers).await?; - if matches!(plan.mode, SourceOrchestrationMode::Managed { .. }) && resolved.managed.is_empty() { + if matches!(plan.mode(), SourceOrchestrationMode::Managed { .. }) && resolved.managed.is_empty() + { return Err(SourceResolveError::ManagedNodesMissing.into()); } From d2665bdb71c8ee4b3db5cd65be676989cf31114f Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:35:11 +0100 Subject: [PATCH 08/13] Hide runtime construction helpers from docs --- testing-framework/core/src/scenario/runtime/context.rs | 3 +++ testing-framework/core/src/scenario/runtime/runner.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/testing-framework/core/src/scenario/runtime/context.rs b/testing-framework/core/src/scenario/runtime/context.rs index a136fb4..9e40486 100644 --- a/testing-framework/core/src/scenario/runtime/context.rs +++ b/testing-framework/core/src/scenario/runtime/context.rs @@ -24,6 +24,7 @@ pub struct RunContext { impl RunContext { /// Builds a run context from prepared deployment/runtime artifacts. #[must_use] + #[doc(hidden)] pub fn new( descriptors: E::Deployment, node_clients: NodeClients, @@ -48,6 +49,7 @@ impl RunContext { } #[must_use] + #[doc(hidden)] pub fn with_cluster_wait(mut self, cluster_wait: Arc>) -> Self { self.cluster_wait = Some(cluster_wait); self @@ -137,6 +139,7 @@ impl Drop for RunHandle { impl RunHandle { #[must_use] /// Build a handle from owned context and optional cleanup guard. + #[doc(hidden)] pub fn new(context: RunContext, cleanup_guard: Option>) -> Self { Self { run_context: Arc::new(context), diff --git a/testing-framework/core/src/scenario/runtime/runner.rs b/testing-framework/core/src/scenario/runtime/runner.rs index 16c9002..d9e3f5e 100644 --- a/testing-framework/core/src/scenario/runtime/runner.rs +++ b/testing-framework/core/src/scenario/runtime/runner.rs @@ -36,6 +36,7 @@ impl Drop for Runner { impl Runner { /// Construct a runner from the run context and optional cleanup guard. #[must_use] + #[doc(hidden)] pub fn new(context: RunContext, cleanup_guard: Option>) -> Self { Self { context: Arc::new(context), From 0ff1ae1904416eb049dd817f258a3501c76a6834 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:37:01 +0100 Subject: [PATCH 09/13] Trim scenario source mutators --- testing-framework/core/src/scenario/sources/model.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing-framework/core/src/scenario/sources/model.rs b/testing-framework/core/src/scenario/sources/model.rs index 0a3dc69..903aff1 100644 --- a/testing-framework/core/src/scenario/sources/model.rs +++ b/testing-framework/core/src/scenario/sources/model.rs @@ -118,7 +118,7 @@ impl ScenarioSources { Self::ExternalOnly { external } } - pub fn add_external_node(&mut self, node: ExternalNodeSource) { + pub(crate) fn add_external_node(&mut self, node: ExternalNodeSource) { match self { Self::Managed { external } | Self::Attached { external, .. } @@ -126,12 +126,12 @@ impl ScenarioSources { } } - pub fn set_attach(&mut self, attach: AttachSource) { + pub(crate) fn set_attach(&mut self, attach: AttachSource) { let external = self.external_nodes().to_vec(); *self = Self::Attached { attach, external }; } - pub fn set_external_only(&mut self) { + pub(crate) fn set_external_only(&mut self) { let external = self.external_nodes().to_vec(); *self = Self::ExternalOnly { external }; } From 74290327a32a28021a2c1f197f8dc817a9190ed9 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:38:10 +0100 Subject: [PATCH 10/13] Encapsulate external node source fields --- logos/runtime/ext/src/lib.rs | 2 +- .../runtime/providers/external_provider.rs | 4 ++-- .../core/src/scenario/sources/model.rs | 14 ++++++++++++-- testing-framework/deployers/local/src/external.rs | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/logos/runtime/ext/src/lib.rs b/logos/runtime/ext/src/lib.rs index 177823b..8701c6c 100644 --- a/logos/runtime/ext/src/lib.rs +++ b/logos/runtime/ext/src/lib.rs @@ -44,7 +44,7 @@ impl Application for LbcExtEnv { type FeedRuntime = ::FeedRuntime; fn external_node_client(source: &ExternalNodeSource) -> Result { - let base_url = Url::parse(&source.endpoint)?; + let base_url = Url::parse(source.endpoint())?; Ok(NodeHttpClient::from_urls(base_url, None)) } diff --git a/testing-framework/core/src/scenario/runtime/providers/external_provider.rs b/testing-framework/core/src/scenario/runtime/providers/external_provider.rs index 343a35c..1a1beb6 100644 --- a/testing-framework/core/src/scenario/runtime/providers/external_provider.rs +++ b/testing-framework/core/src/scenario/runtime/providers/external_provider.rs @@ -71,11 +71,11 @@ impl ExternalProvider for ApplicationExternalProvider { .map(|source| { E::external_node_client(source) .map(|client| ExternalNode { - identity_hint: Some(source.label.clone()), + identity_hint: Some(source.label().to_string()), client, }) .map_err(|build_error| ExternalProviderError::Build { - source_label: source.label.clone(), + source_label: source.label().to_string(), source: build_error, }) }) diff --git a/testing-framework/core/src/scenario/sources/model.rs b/testing-framework/core/src/scenario/sources/model.rs index 903aff1..c107d7c 100644 --- a/testing-framework/core/src/scenario/sources/model.rs +++ b/testing-framework/core/src/scenario/sources/model.rs @@ -49,8 +49,8 @@ impl AttachSource { /// inventory. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExternalNodeSource { - pub label: String, - pub endpoint: String, + label: String, + endpoint: String, } impl ExternalNodeSource { @@ -58,6 +58,16 @@ impl ExternalNodeSource { pub fn new(label: String, endpoint: String) -> Self { Self { label, endpoint } } + + #[must_use] + pub fn label(&self) -> &str { + &self.label + } + + #[must_use] + pub fn endpoint(&self) -> &str { + &self.endpoint + } } /// Planned readiness strategy for mixed managed/attached/external sources. diff --git a/testing-framework/deployers/local/src/external.rs b/testing-framework/deployers/local/src/external.rs index 713c5be..696d05e 100644 --- a/testing-framework/deployers/local/src/external.rs +++ b/testing-framework/deployers/local/src/external.rs @@ -35,8 +35,8 @@ pub fn build_external_client( } fn resolve_api_socket(source: &ExternalNodeSource) -> Result { - let source_label = source.label.clone(); - let endpoint = source.endpoint.trim(); + let source_label = source.label().to_string(); + let endpoint = source.endpoint().trim(); if endpoint.is_empty() { return Err(ExternalClientBuildError::EmptyEndpoint { label: source_label, From 6888c18275d5b43e64b4d809cbedf758dc5fed5b Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:40:27 +0100 Subject: [PATCH 11/13] Simplify node client inventory --- testing-framework/core/src/scenario/mod.rs | 5 +- .../src/scenario/runtime/inventory/mod.rs | 2 +- .../runtime/inventory/node_inventory.rs | 314 ++---------------- .../core/src/scenario/runtime/mod.rs | 3 +- .../core/src/scenario/runtime/node_clients.rs | 4 +- 5 files changed, 27 insertions(+), 301 deletions(-) diff --git a/testing-framework/core/src/scenario/mod.rs b/testing-framework/core/src/scenario/mod.rs index f2e43d6..cc3b300 100644 --- a/testing-framework/core/src/scenario/mod.rs +++ b/testing-framework/core/src/scenario/mod.rs @@ -37,9 +37,8 @@ pub use deployment_policy::{CleanupPolicy, DeploymentPolicy, RetryPolicy}; pub use expectation::Expectation; pub use observability::{ObservabilityCapabilityProvider, ObservabilityInputs}; pub use runtime::{ - ApplicationExternalProvider, AttachProvider, AttachProviderError, AttachedNode, BorrowedNode, - BorrowedOrigin, CleanupGuard, Deployer, Feed, FeedHandle, FeedRuntime, - HttpReadinessRequirement, ManagedNode, ManagedSource, NodeClients, NodeHandle, NodeInventory, + ApplicationExternalProvider, AttachProvider, AttachProviderError, AttachedNode, CleanupGuard, + Deployer, Feed, FeedHandle, FeedRuntime, HttpReadinessRequirement, ManagedSource, NodeClients, ReadinessError, RunContext, RunHandle, RunMetrics, Runner, ScenarioError, SourceOrchestrationPlan, SourceProviders, StabilizationConfig, StaticManagedProvider, build_source_orchestration_plan, diff --git a/testing-framework/core/src/scenario/runtime/inventory/mod.rs b/testing-framework/core/src/scenario/runtime/inventory/mod.rs index 6bc0334..575f53f 100644 --- a/testing-framework/core/src/scenario/runtime/inventory/mod.rs +++ b/testing-framework/core/src/scenario/runtime/inventory/mod.rs @@ -1,3 +1,3 @@ mod node_inventory; -pub use node_inventory::{BorrowedNode, BorrowedOrigin, ManagedNode, NodeHandle, NodeInventory}; +pub(crate) use node_inventory::NodeInventory; diff --git a/testing-framework/core/src/scenario/runtime/inventory/node_inventory.rs b/testing-framework/core/src/scenario/runtime/inventory/node_inventory.rs index c45385e..d9b45ce 100644 --- a/testing-framework/core/src/scenario/runtime/inventory/node_inventory.rs +++ b/testing-framework/core/src/scenario/runtime/inventory/node_inventory.rs @@ -1,91 +1,18 @@ -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use parking_lot::RwLock; -use crate::scenario::{Application, DynError, NodeControlHandle, StartNodeOptions, StartedNode}; +use crate::scenario::Application; -/// Origin for borrowed (non-managed) nodes in the runtime inventory. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum BorrowedOrigin { - /// Node discovered from an attached cluster provider. - Attached, - /// Node provided explicitly as an external endpoint. - External, -} - -/// Managed node handle with full lifecycle capabilities. -pub struct ManagedNode { - /// Canonical node identity used for deduplication and lookups. - pub identity: String, - /// Application-specific API client for this node. - pub client: E::NodeClient, -} - -/// Borrowed node handle (attached or external), query-only by default. -pub struct BorrowedNode { - /// Canonical node identity used for deduplication and lookups. - pub identity: String, - /// Application-specific API client for this node. - pub client: E::NodeClient, - /// Borrowed source kind used for diagnostics and selection. - pub origin: BorrowedOrigin, -} - -/// Unified node handle variant used by runtime inventory snapshots. -pub enum NodeHandle { - /// Managed node variant. - Managed(ManagedNode), - /// Borrowed node variant. - Borrowed(BorrowedNode), -} - -impl Clone for ManagedNode { - fn clone(&self) -> Self { - Self { - identity: self.identity.clone(), - client: self.client.clone(), - } - } -} - -impl Clone for BorrowedNode { - fn clone(&self) -> Self { - Self { - identity: self.identity.clone(), - client: self.client.clone(), - origin: self.origin, - } - } -} - -impl Clone for NodeHandle { - fn clone(&self) -> Self { - match self { - Self::Managed(node) => Self::Managed(node.clone()), - Self::Borrowed(node) => Self::Borrowed(node.clone()), - } - } -} - -/// Thread-safe node inventory with identity-based upsert semantics. -pub struct NodeInventory { - inner: Arc>>, -} - -struct NodeInventoryInner { - nodes: Vec>, - indices_by_identity: HashMap, - next_synthetic_id: usize, +/// Thread-safe node client storage used by runtime handles. +pub(crate) struct NodeInventory { + clients: Arc>>, } impl Default for NodeInventory { fn default() -> Self { Self { - inner: Arc::new(RwLock::new(NodeInventoryInner { - nodes: Vec::new(), - indices_by_identity: HashMap::new(), - next_synthetic_id: 0, - })), + clients: Arc::new(RwLock::new(Vec::new())), } } } @@ -93,243 +20,44 @@ impl Default for NodeInventory { impl Clone for NodeInventory { fn clone(&self) -> Self { Self { - inner: Arc::clone(&self.inner), + clients: Arc::clone(&self.clients), } } } impl NodeInventory { #[must_use] - /// Builds an inventory from managed clients. - pub fn from_managed_clients(clients: Vec) -> Self { - let inventory = Self::default(); - - for client in clients { - inventory.add_managed_node(client, None); - } - - inventory - } - - #[must_use] - /// Returns a cloned snapshot of all node clients. - pub fn snapshot_clients(&self) -> Vec { - self.inner.read().nodes.iter().map(clone_client).collect() - } - - #[must_use] - /// Returns cloned managed node handles from the current inventory. - pub fn managed_nodes(&self) -> Vec> { - self.inner - .read() - .nodes - .iter() - .filter_map(|handle| match handle { - NodeHandle::Managed(node) => Some(node.clone()), - NodeHandle::Borrowed(_) => None, - }) - .collect() - } - - #[must_use] - /// Returns cloned borrowed node handles from the current inventory. - pub fn borrowed_nodes(&self) -> Vec> { - self.inner - .read() - .nodes - .iter() - .filter_map(|handle| match handle { - NodeHandle::Managed(_) => None, - NodeHandle::Borrowed(node) => Some(node.clone()), - }) - .collect() - } - - #[must_use] - /// Finds a managed node by canonical identity. - pub fn find_managed(&self, identity: &str) -> Option> { - let guard = self.inner.read(); - match node_by_identity(&guard, identity)? { - NodeHandle::Managed(node) => Some(node.clone()), - NodeHandle::Borrowed(_) => None, + pub(crate) fn from_clients(clients: Vec) -> Self { + Self { + clients: Arc::new(RwLock::new(clients)), } } #[must_use] - /// Finds a borrowed node by canonical identity. - pub fn find_borrowed(&self, identity: &str) -> Option> { - let guard = self.inner.read(); - match node_by_identity(&guard, identity)? { - NodeHandle::Managed(_) => None, - NodeHandle::Borrowed(node) => Some(node.clone()), - } + pub(crate) fn snapshot_clients(&self) -> Vec { + self.clients.read().clone() } #[must_use] - /// Finds any node handle by canonical identity. - pub fn find_node(&self, identity: &str) -> Option> { - let guard = self.inner.read(); - node_by_identity(&guard, identity).cloned() + pub(crate) fn len(&self) -> usize { + self.clients.read().len() } #[must_use] - /// Returns current number of nodes in inventory. - pub fn len(&self) -> usize { - self.inner.read().nodes.len() - } - - #[must_use] - /// Returns true when no nodes are registered. - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.len() == 0 } - /// Clears all nodes and identity indexes. - pub fn clear(&self) { - let mut guard = self.inner.write(); - guard.nodes.clear(); - guard.indices_by_identity.clear(); - guard.next_synthetic_id = 0; + pub(crate) fn clear(&self) { + self.clients.write().clear(); } - /// Adds or replaces a managed node entry using canonical identity - /// resolution. Re-adding the same node identity updates the stored handle. - pub fn add_managed_node(&self, client: E::NodeClient, identity_hint: Option) { - let mut guard = self.inner.write(); - let identity = canonical_identity::(&client, identity_hint, &mut guard); - let handle = NodeHandle::Managed(ManagedNode { - identity: identity.clone(), - client, - }); - upsert_node(&mut guard, identity, handle); + pub(crate) fn add_client(&self, client: E::NodeClient) { + self.clients.write().push(client); } - /// Adds or replaces an attached node entry. - pub fn add_attached_node(&self, client: E::NodeClient, identity_hint: Option) { - self.add_borrowed_node(client, BorrowedOrigin::Attached, identity_hint); - } - - /// Adds or replaces an external static node entry. - pub fn add_external_node(&self, client: E::NodeClient, identity_hint: Option) { - self.add_borrowed_node(client, BorrowedOrigin::External, identity_hint); - } - - /// Executes a synchronous read over a cloned client slice. - pub fn with_clients(&self, f: impl FnOnce(&[E::NodeClient]) -> R) -> R { - let guard = self.inner.read(); - let clients = guard.nodes.iter().map(clone_client).collect::>(); + pub(crate) fn with_clients(&self, f: impl FnOnce(&[E::NodeClient]) -> R) -> R { + let clients = self.clients.read(); f(&clients) } - - fn add_borrowed_node( - &self, - client: E::NodeClient, - origin: BorrowedOrigin, - identity_hint: Option, - ) { - let mut guard = self.inner.write(); - let identity = canonical_identity::(&client, identity_hint, &mut guard); - let handle = NodeHandle::Borrowed(BorrowedNode { - identity: identity.clone(), - client, - origin, - }); - upsert_node(&mut guard, identity, handle); - } -} - -impl ManagedNode { - #[must_use] - /// Returns the node client. - pub const fn client(&self) -> &E::NodeClient { - &self.client - } - - /// Delegates restart to the deployer's control surface for this node name. - pub async fn restart( - &self, - control: &dyn NodeControlHandle, - node_name: &str, - ) -> Result<(), DynError> { - control.restart_node(node_name).await - } - - /// Delegates stop to the deployer's control surface for this node name. - pub async fn stop( - &self, - control: &dyn NodeControlHandle, - node_name: &str, - ) -> Result<(), DynError> { - control.stop_node(node_name).await - } - - /// Delegates dynamic node start with options to the control surface. - pub async fn start_with( - &self, - control: &dyn NodeControlHandle, - node_name: &str, - options: StartNodeOptions, - ) -> Result, DynError> { - control.start_node_with(node_name, options).await - } - - #[must_use] - /// Returns process id if the backend can expose it for this node name. - pub fn pid(&self, control: &dyn NodeControlHandle, node_name: &str) -> Option { - control.node_pid(node_name) - } -} - -impl BorrowedNode { - #[must_use] - /// Returns the node client. - pub const fn client(&self) -> &E::NodeClient { - &self.client - } -} - -fn upsert_node( - inner: &mut NodeInventoryInner, - identity: String, - handle: NodeHandle, -) { - if let Some(existing_index) = inner.indices_by_identity.get(&identity).copied() { - inner.nodes[existing_index] = handle; - return; - } - - let index = inner.nodes.len(); - inner.nodes.push(handle); - inner.indices_by_identity.insert(identity, index); -} - -fn canonical_identity( - _client: &E::NodeClient, - identity_hint: Option, - inner: &mut NodeInventoryInner, -) -> String { - // Priority: explicit hint -> synthetic. - if let Some(identity) = identity_hint.filter(|value| !value.trim().is_empty()) { - return identity; - } - - let synthetic = format!("node:{}", inner.next_synthetic_id); - inner.next_synthetic_id += 1; - - synthetic -} - -fn clone_client(handle: &NodeHandle) -> E::NodeClient { - match handle { - NodeHandle::Managed(node) => node.client.clone(), - NodeHandle::Borrowed(node) => node.client.clone(), - } -} - -fn node_by_identity<'a, E: Application>( - inner: &'a NodeInventoryInner, - identity: &str, -) -> Option<&'a NodeHandle> { - let index = *inner.indices_by_identity.get(identity)?; - inner.nodes.get(index) } diff --git a/testing-framework/core/src/scenario/runtime/mod.rs b/testing-framework/core/src/scenario/runtime/mod.rs index 97bd2d6..5682ccc 100644 --- a/testing-framework/core/src/scenario/runtime/mod.rs +++ b/testing-framework/core/src/scenario/runtime/mod.rs @@ -1,6 +1,6 @@ pub mod context; mod deployer; -pub mod inventory; +mod inventory; pub mod metrics; mod node_clients; pub mod orchestration; @@ -11,7 +11,6 @@ mod runner; use async_trait::async_trait; pub use context::{CleanupGuard, RunContext, RunHandle, RunMetrics}; pub use deployer::{Deployer, ScenarioError}; -pub use inventory::{BorrowedNode, BorrowedOrigin, ManagedNode, NodeHandle, NodeInventory}; pub use node_clients::NodeClients; #[doc(hidden)] pub use orchestration::{ diff --git a/testing-framework/core/src/scenario/runtime/node_clients.rs b/testing-framework/core/src/scenario/runtime/node_clients.rs index ce31dcf..b35a0ed 100644 --- a/testing-framework/core/src/scenario/runtime/node_clients.rs +++ b/testing-framework/core/src/scenario/runtime/node_clients.rs @@ -29,7 +29,7 @@ impl NodeClients { /// Build clients from preconstructed vectors. pub fn new(nodes: Vec) -> Self { Self { - inventory: NodeInventory::from_managed_clients(nodes), + inventory: NodeInventory::from_clients(nodes), } } @@ -72,7 +72,7 @@ impl NodeClients { } pub fn add_node(&self, client: E::NodeClient) { - self.inventory.add_managed_node(client, None); + self.inventory.add_client(client); } pub fn clear(&self) { From 3f8e287c68d3b11ba15309982846f09dbab23067 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:44:25 +0100 Subject: [PATCH 12/13] Make scenario source transitions explicit --- .../core/src/scenario/definition.rs | 6 +++--- .../core/src/scenario/sources/model.rs | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/testing-framework/core/src/scenario/definition.rs b/testing-framework/core/src/scenario/definition.rs index 44b210c..f656a98 100644 --- a/testing-framework/core/src/scenario/definition.rs +++ b/testing-framework/core/src/scenario/definition.rs @@ -569,13 +569,13 @@ impl Builder { #[must_use] pub fn with_attach_source(mut self, attach: AttachSource) -> Self { - self.sources.set_attach(attach); + self.sources = self.sources.with_attach(attach); self } #[must_use] pub fn with_external_node(mut self, node: ExternalNodeSource) -> Self { - self.sources.add_external_node(node); + self.sources = self.sources.with_external_node(node); self } @@ -591,7 +591,7 @@ impl Builder { #[must_use] pub fn with_external_only_sources(mut self) -> Self { - self.sources.set_external_only(); + self.sources = self.sources.into_external_only(); self } diff --git a/testing-framework/core/src/scenario/sources/model.rs b/testing-framework/core/src/scenario/sources/model.rs index c107d7c..84dc89f 100644 --- a/testing-framework/core/src/scenario/sources/model.rs +++ b/testing-framework/core/src/scenario/sources/model.rs @@ -128,22 +128,29 @@ impl ScenarioSources { Self::ExternalOnly { external } } - pub(crate) fn add_external_node(&mut self, node: ExternalNodeSource) { - match self { + #[must_use] + pub fn with_external_node(mut self, node: ExternalNodeSource) -> Self { + match &mut self { Self::Managed { external } | Self::Attached { external, .. } | Self::ExternalOnly { external } => external.push(node), } + + self } - pub(crate) fn set_attach(&mut self, attach: AttachSource) { + #[must_use] + pub fn with_attach(self, attach: AttachSource) -> Self { let external = self.external_nodes().to_vec(); - *self = Self::Attached { attach, external }; + + Self::Attached { attach, external } } - pub(crate) fn set_external_only(&mut self) { + #[must_use] + pub fn into_external_only(self) -> Self { let external = self.external_nodes().to_vec(); - *self = Self::ExternalOnly { external }; + + Self::ExternalOnly { external } } #[must_use] From 3ea3fffd1f2e11291bcb061719e48249e40d3d23 Mon Sep 17 00:00:00 2001 From: andrussal Date: Sun, 8 Mar 2026 13:45:39 +0100 Subject: [PATCH 13/13] Drop unused source readiness policy --- .../core/src/scenario/definition.rs | 43 +------------------ testing-framework/core/src/scenario/mod.rs | 2 +- .../source_orchestration_plan.rs | 16 +++---- .../runtime/orchestration/source_resolver.rs | 5 +-- .../core/src/scenario/sources/mod.rs | 2 +- .../core/src/scenario/sources/model.rs | 13 ------ 6 files changed, 10 insertions(+), 71 deletions(-) diff --git a/testing-framework/core/src/scenario/definition.rs b/testing-framework/core/src/scenario/definition.rs index f656a98..b16b6a7 100644 --- a/testing-framework/core/src/scenario/definition.rs +++ b/testing-framework/core/src/scenario/definition.rs @@ -6,7 +6,6 @@ use tracing::{debug, info}; use super::{ Application, AttachSource, DeploymentPolicy, DynError, ExternalNodeSource, HttpReadinessRequirement, NodeControlCapability, ObservabilityCapability, ScenarioSources, - SourceReadinessPolicy, builder_ops::CoreBuilderAccess, expectation::Expectation, runtime::{ @@ -44,7 +43,6 @@ pub struct Scenario { expectation_cooldown: Duration, deployment_policy: DeploymentPolicy, sources: ScenarioSources, - source_readiness_policy: SourceReadinessPolicy, source_orchestration_plan: SourceOrchestrationPlan, capabilities: Caps, } @@ -58,7 +56,6 @@ impl Scenario { expectation_cooldown: Duration, deployment_policy: DeploymentPolicy, sources: ScenarioSources, - source_readiness_policy: SourceReadinessPolicy, source_orchestration_plan: SourceOrchestrationPlan, capabilities: Caps, ) -> Self { @@ -70,7 +67,6 @@ impl Scenario { expectation_cooldown, deployment_policy, sources, - source_readiness_policy, source_orchestration_plan, capabilities, } @@ -116,15 +112,6 @@ impl Scenario { self.deployment_policy } - #[must_use] - /// Selected source readiness policy. - /// - /// This is currently reserved for future mixed-source orchestration and - /// does not change runtime behavior yet. - pub const fn source_readiness_policy(&self) -> SourceReadinessPolicy { - self.source_readiness_policy - } - #[must_use] pub fn sources(&self) -> &ScenarioSources { &self.sources @@ -151,7 +138,6 @@ pub struct Builder { expectation_cooldown: Option, deployment_policy: DeploymentPolicy, sources: ScenarioSources, - source_readiness_policy: SourceReadinessPolicy, capabilities: Caps, } @@ -256,11 +242,6 @@ macro_rules! impl_common_builder_methods { self.map_core_builder(|builder| builder.with_external_node(node)) } - #[must_use] - pub fn with_source_readiness_policy(self, policy: SourceReadinessPolicy) -> Self { - self.map_core_builder(|builder| builder.with_source_readiness_policy(policy)) - } - #[must_use] pub fn with_external_only_sources(self) -> Self { self.map_core_builder(|builder| builder.with_external_only_sources()) @@ -350,7 +331,6 @@ impl Builder { expectation_cooldown: None, deployment_policy: DeploymentPolicy::default(), sources: ScenarioSources::default(), - source_readiness_policy: SourceReadinessPolicy::default(), capabilities: Caps::default(), } } @@ -453,7 +433,6 @@ impl Builder { expectation_cooldown, deployment_policy, sources, - source_readiness_policy, .. } = self; @@ -466,7 +445,6 @@ impl Builder { expectation_cooldown, deployment_policy, sources, - source_readiness_policy, capabilities, } } @@ -579,16 +557,6 @@ impl Builder { self } - #[must_use] - /// Configure source readiness policy metadata. - /// - /// This is currently reserved for future mixed-source orchestration and - /// does not change runtime behavior yet. - pub fn with_source_readiness_policy(mut self, policy: SourceReadinessPolicy) -> Self { - self.source_readiness_policy = policy; - self - } - #[must_use] pub fn with_external_only_sources(mut self) -> Self { self.sources = self.sources.into_external_only(); @@ -612,8 +580,7 @@ impl Builder { let descriptors = parts.resolve_deployment()?; let run_plan = parts.run_plan(); let run_metrics = RunMetrics::new(run_plan.duration); - let source_orchestration_plan = - build_source_orchestration_plan(parts.sources(), parts.source_readiness_policy)?; + let source_orchestration_plan = build_source_orchestration_plan(parts.sources())?; initialize_components( &descriptors, @@ -640,7 +607,6 @@ impl Builder { run_plan.expectation_cooldown, parts.deployment_policy, parts.sources, - parts.source_readiness_policy, source_orchestration_plan, parts.capabilities, )) @@ -661,7 +627,6 @@ struct BuilderParts { expectation_cooldown: Option, deployment_policy: DeploymentPolicy, sources: ScenarioSources, - source_readiness_policy: SourceReadinessPolicy, capabilities: Caps, } @@ -676,7 +641,6 @@ impl BuilderParts { expectation_cooldown, deployment_policy, sources, - source_readiness_policy, capabilities, .. } = builder; @@ -690,7 +654,6 @@ impl BuilderParts { expectation_cooldown, deployment_policy, sources, - source_readiness_policy, capabilities, } } @@ -715,10 +678,8 @@ impl BuilderParts { fn build_source_orchestration_plan( sources: &ScenarioSources, - readiness_policy: SourceReadinessPolicy, ) -> Result { - SourceOrchestrationPlan::try_from_sources(sources, readiness_policy) - .map_err(source_plan_error_to_build_error) + SourceOrchestrationPlan::try_from_sources(sources).map_err(source_plan_error_to_build_error) } fn source_plan_error_to_build_error(error: SourceOrchestrationPlanError) -> ScenarioBuildError { diff --git a/testing-framework/core/src/scenario/mod.rs b/testing-framework/core/src/scenario/mod.rs index cc3b300..8b570ad 100644 --- a/testing-framework/core/src/scenario/mod.rs +++ b/testing-framework/core/src/scenario/mod.rs @@ -51,7 +51,7 @@ pub use runtime::{ wait_for_http_ports_with_host_and_requirement, wait_for_http_ports_with_requirement, wait_http_readiness, wait_until_stable, }; -pub use sources::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy}; +pub use sources::{AttachSource, ExternalNodeSource, ScenarioSources}; pub use workload::Workload; pub use crate::env::Application; diff --git a/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs b/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs index ed72ae3..f1dd9b9 100644 --- a/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs +++ b/testing-framework/core/src/scenario/runtime/orchestration/source_orchestration_plan.rs @@ -1,4 +1,4 @@ -use crate::scenario::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy}; +use crate::scenario::{AttachSource, ExternalNodeSource, ScenarioSources}; /// Explicit descriptor for managed node sourcing. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -33,7 +33,6 @@ pub(crate) enum SourceOrchestrationMode { #[derive(Clone, Debug, Eq, PartialEq)] pub struct SourceOrchestrationPlan { mode: SourceOrchestrationMode, - readiness_policy: SourceReadinessPolicy, } /// Validation failure while building orchestration plan from sources. @@ -46,14 +45,10 @@ pub enum SourceOrchestrationPlanError { impl SourceOrchestrationPlan { pub fn try_from_sources( sources: &ScenarioSources, - readiness_policy: SourceReadinessPolicy, ) -> Result { let mode = mode_from_sources(sources); - Ok(Self { - mode, - readiness_policy, - }) + Ok(Self { mode }) } #[must_use] @@ -74,14 +69,13 @@ impl SourceOrchestrationPlan { #[cfg(test)] mod tests { use super::{SourceOrchestrationMode, SourceOrchestrationPlan}; - use crate::scenario::{AttachSource, ScenarioSources, SourceReadinessPolicy}; + use crate::scenario::{AttachSource, ScenarioSources}; #[test] fn attached_sources_are_planned() { let sources = ScenarioSources::attached(AttachSource::compose(vec!["node-0".to_string()])); - let plan = - SourceOrchestrationPlan::try_from_sources(&sources, SourceReadinessPolicy::AllReady) - .expect("attached sources should build a source orchestration plan"); + let plan = SourceOrchestrationPlan::try_from_sources(&sources) + .expect("attached sources should build a source orchestration plan"); assert!(matches!( plan.mode(), diff --git a/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs b/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs index 8ead379..ebf2d2d 100644 --- a/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs +++ b/testing-framework/core/src/scenario/runtime/orchestration/source_resolver.rs @@ -41,10 +41,7 @@ pub enum SourceResolveError { pub fn build_source_orchestration_plan( scenario: &Scenario, ) -> Result { - SourceOrchestrationPlan::try_from_sources( - scenario.sources(), - scenario.source_readiness_policy(), - ) + SourceOrchestrationPlan::try_from_sources(scenario.sources()) } /// Resolves runtime source nodes via unified providers from orchestration plan. diff --git a/testing-framework/core/src/scenario/sources/mod.rs b/testing-framework/core/src/scenario/sources/mod.rs index 3585d8a..98324dd 100644 --- a/testing-framework/core/src/scenario/sources/mod.rs +++ b/testing-framework/core/src/scenario/sources/mod.rs @@ -1,3 +1,3 @@ mod model; -pub use model::{AttachSource, ExternalNodeSource, ScenarioSources, SourceReadinessPolicy}; +pub use model::{AttachSource, ExternalNodeSource, ScenarioSources}; diff --git a/testing-framework/core/src/scenario/sources/model.rs b/testing-framework/core/src/scenario/sources/model.rs index 84dc89f..98c60ad 100644 --- a/testing-framework/core/src/scenario/sources/model.rs +++ b/testing-framework/core/src/scenario/sources/model.rs @@ -70,19 +70,6 @@ impl ExternalNodeSource { } } -/// Planned readiness strategy for mixed managed/attached/external sources. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] -pub enum SourceReadinessPolicy { - /// Phase 1 default: require every known node to pass readiness checks. - #[default] - AllReady, - /// Optional relaxed policy for large/partial environments. - Quorum, - /// Future policy for per-source constraints (for example managed minimum - /// plus overall quorum). - SourceAware, -} - /// Source model that makes invalid managed+attached combinations /// unrepresentable by type. #[derive(Clone, Debug, Eq, PartialEq)]