diff --git a/Cargo.lock b/Cargo.lock index 1b2b7ece..6733e983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5959,6 +5959,7 @@ dependencies = [ "tokio", "toml 1.1.2+spec-1.1.0", "tracing", + "videre-host", "videre-status-body", "wasmtime", ] diff --git a/crates/videre-host/Cargo.toml b/crates/videre-host/Cargo.toml index a8c6dd11..e45d28d3 100644 --- a/crates/videre-host/Cargo.toml +++ b/crates/videre-host/Cargo.toml @@ -26,7 +26,18 @@ tokio.workspace = true toml.workspace = true tracing.workspace = true +[features] +# Test-only helpers: the direct `VenueRegistry::install_for_test` seam, so an +# out-of-crate test installs a mock adapter without the provider boot path. +# Off by default; the self dev-dependency below turns it on for this crate's +# own tests. +test-utils = [] + [dev-dependencies] +# Self dev-dependency enabling `test-utils` for this crate's own test targets, +# so `cargo test -p videre-host` sees `install_for_test` without every +# invocation passing `--features test-utils`. +videre-host = { path = ".", features = ["test-utils"] } nexum-runtime = { path = "../nexum-runtime", features = ["test-utils"] } nexum-tasks = { path = "../nexum-tasks" } tempfile.workspace = true diff --git a/crates/videre-host/src/registry.rs b/crates/videre-host/src/registry.rs index 88a44392..d62994d6 100644 --- a/crates/videre-host/src/registry.rs +++ b/crates/videre-host/src/registry.rs @@ -347,12 +347,17 @@ impl VenueRegistry { /// adapters answering the same venue would silently shadow one another, /// which is a config error worth failing boot over. A dead incumbent is /// replaced: that is the sweep restarting a trapped adapter. - pub fn install( + /// + /// Crate-internal: adapters install at provider boot, never post-boot + /// through a shared handle clone. + pub(crate) fn install( &self, venue: VenueId, liveness: Liveness, invoker: impl VenueInvoker + 'static, ) -> Result<(), DuplicateVenue> { + // Takes the adapter-map mutex only for the synchronous insert; never + // held across an await. let mut adapters = self.inner.adapters.lock().expect("adapter map poisoned"); if adapters.get(&venue).is_some_and(|v| v.liveness.is_alive()) { return Err(DuplicateVenue { venue }); @@ -367,6 +372,17 @@ impl VenueRegistry { Ok(()) } + /// Test-only direct install, bypassing the provider boot path. + #[cfg(feature = "test-utils")] + pub fn install_for_test( + &self, + venue: VenueId, + liveness: Liveness, + invoker: impl VenueInvoker + 'static, + ) -> Result<(), DuplicateVenue> { + self.install(venue, liveness, invoker) + } + /// Resolve a venue id to its installed adapter slot. An uninstalled /// venue is `unknown-venue`; an installed but dead one is `unavailable` /// pending the supervisor's restart sweep, without touching its diff --git a/crates/videre-host/tests/platform.rs b/crates/videre-host/tests/platform.rs index ff3506f2..2385900a 100644 --- a/crates/videre-host/tests/platform.rs +++ b/crates/videre-host/tests/platform.rs @@ -281,7 +281,7 @@ impl VenueInvoker for ScriptedAdapter { fn scripted_registry(adapter: ScriptedAdapter) -> VenueRegistry { let registry = VenueRegistryBuilder::new(Default::default()).build(); registry - .install( + .install_for_test( VenueId::from("cow"), nexum_runtime::host::actor::Liveness::default(), adapter,