From 8da706a50b51036bf312cdd9550e2338ecb28685 Mon Sep 17 00:00:00 2001 From: EnRaiha <15997552+EnRaiha@users.noreply.github.com> Date: Mon, 28 Sep 2026 01:33:11 +0800 Subject: [PATCH] build(wasm): let the mem, crdt and client test targets build and run on wasip1 The workspace declares `tokio = { version = "1", features = ["full"] }`, and Cargo unifies dev-dependency features across the whole `cargo test` invocation. On `wasm32-wasip1` tokio refuses to build with anything beyond `sync,macros,io-util,rt,time`, so one dev-dependency of that shape breaks every wasm test target in the build: the shared-crate run for these three crates died before reaching a single test (cargo exit 101, `error: Only features sync,macros,io-util,rt,time are supported on wasm.`). - `nodedb-mem` and `nodedb-crdt`: `tokio` becomes a `cfg(not(target_arch = "wasm32"))` dev-dependency. `nodedb-mem` also gates `fluxbench`, which declares its own normal `tokio = { features = ["full"] }`. Native test builds keep both. - Two tests in `nodedb-mem` drive the reserve path from real threads, which this target does not have, and are gated to match. Their `Arc`, `thread`, `DatabaseId` and `TenantId` imports move into the gated function, as the neighbouring budget test already does. - `nodedb-client` keeps its native dev-dependency and gets a second one for wasm: `tokio = { version = "1", default-features = false, features = ["rt", "macros", "sync", "time"] }`. Workspace inheritance is additive, so `{ workspace = true }` always carries `full`; naming the version and features directly is what lets the wasm target keep a Tokio runtime, and `rt`/`macros` are supported there. No test needed gating: all of its tests run on both targets. Measured, per crate, `cargo test --profile ci --target wasm32-wasip1 -p ` with `CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime -W max-wasm-stack=33554432 --dir=."`: nodedb-mem 83 passed, 0 failed (2 thread tests gated) nodedb-crdt 168 passed, 0 failed nodedb-client 34 passed, 0 failed and natively, unchanged: nodedb-mem 86 passed, 0 failed nodedb-crdt 168 passed, 0 failed nodedb-client 34 passed, 0 failed The other twelve shared crates carry neither `tokio` nor `fluxbench` in a dev-dependency, so this is the whole set that blocked the wasm test build for them. `nodedb-wal` has the same problem and its own change in flight. fmt clean; clippy clean for all three crates natively and for `wasm32-wasip1`. --- nodedb-client/Cargo.toml | 12 +++++++++++- nodedb-crdt/Cargo.toml | 9 ++++++++- nodedb-mem/Cargo.toml | 14 ++++++++++++-- nodedb-mem/src/budget.rs | 3 +++ nodedb-mem/src/governor/reserve.rs | 12 ++++++++---- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/nodedb-client/Cargo.toml b/nodedb-client/Cargo.toml index e036aca22..2d729f2cc 100644 --- a/nodedb-client/Cargo.toml +++ b/nodedb-client/Cargo.toml @@ -35,5 +35,15 @@ zerompk = { workspace = true, optional = true } tokio-rustls = { workspace = true, optional = true } rustls-pemfile = { workspace = true, optional = true } -[dev-dependencies] +# The optional `tokio` above is the crate's own dependency and stays as it is; +# these are the dev-dependencies. Native needs nothing new, but the workspace +# entry cannot be used in both: workspace features are additive, so +# `{ workspace = true }` always carries the workspace's `features = ["full"]`, +# and tokio refuses to build on wasm with anything beyond +# `sync,macros,io-util,rt,time`. The wasm entry therefore names the version and +# the features directly, which is what keeps the test targets buildable on both. +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { workspace = true, features = ["rt", "macros"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +tokio = { version = "1", default-features = false, features = ["rt", "macros", "sync", "time"] } diff --git a/nodedb-crdt/Cargo.toml b/nodedb-crdt/Cargo.toml index e5e100bf7..dbd53f948 100644 --- a/nodedb-crdt/Cargo.toml +++ b/nodedb-crdt/Cargo.toml @@ -27,5 +27,12 @@ subtle = { workspace = true } getrandom = { workspace = true } [dev-dependencies] -tokio = { workspace = true } serde_json = { workspace = true } + +# Native-only dev-dependencies. `tokio = { workspace = true }` inherits +# `features = ["full"]`, and tokio refuses fs/io-std/net/process/ +# rt-multi-thread/signal on wasm; Cargo unifies dev-dependency features across +# the whole `cargo test` invocation, so it breaks every wasm test target in the +# build. No test or bench in this crate calls tokio. +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +tokio = { workspace = true } diff --git a/nodedb-mem/Cargo.toml b/nodedb-mem/Cargo.toml index 4ba61e85b..0f0830f1e 100644 --- a/nodedb-mem/Cargo.toml +++ b/nodedb-mem/Cargo.toml @@ -23,7 +23,17 @@ tikv-jemalloc-ctl = { workspace = true } libc = { workspace = true } [dev-dependencies] -tokio = { workspace = true } -fluxbench = { workspace = true } tracing-subscriber = { workspace = true } tempfile = { workspace = true } + +# Native-only dev-dependencies. `tokio = { workspace = true }` inherits +# `features = ["full"]`, `fluxbench` declares its own normal +# `tokio = { features = ["full"] }`, and tokio refuses +# fs/io-std/net/process/rt-multi-thread/signal on wasm; Cargo unifies +# dev-dependency features across the whole `cargo test` invocation, so either +# entry breaks every wasm test target in the build. Neither is reachable from a +# wasm test here: this crate has no tokio call sites, no benches and no tokio +# test attribute. +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +tokio = { workspace = true } +fluxbench = { workspace = true } diff --git a/nodedb-mem/src/budget.rs b/nodedb-mem/src/budget.rs index 8624a145e..9b34e485d 100644 --- a/nodedb-mem/src/budget.rs +++ b/nodedb-mem/src/budget.rs @@ -320,6 +320,9 @@ mod tests { assert_eq!(budget.allocated(), 0); } + // Drives the reserve path from real threads; `wasm32-wasip1` has none. + + #[cfg(not(target_arch = "wasm32"))] #[test] fn concurrent_reserves() { use std::sync::Arc; diff --git a/nodedb-mem/src/governor/reserve.rs b/nodedb-mem/src/governor/reserve.rs index 1b3cd5dfb..a2566111c 100644 --- a/nodedb-mem/src/governor/reserve.rs +++ b/nodedb-mem/src/governor/reserve.rs @@ -167,11 +167,7 @@ impl MemoryGovernor { #[cfg(test)] mod tests { - use std::sync::Arc; use std::sync::atomic::Ordering; - use std::thread; - - use nodedb_types::{DatabaseId, TenantId}; use super::*; use crate::engine_limits::EngineLimits; @@ -345,8 +341,16 @@ mod tests { // ── Concurrent reserves ─────────────────────────────────────────────────── + // Drives the reserve path from real threads; `wasm32-wasip1` has none. + + #[cfg(not(target_arch = "wasm32"))] #[test] fn concurrent_reserves_never_exceed_cap() { + use std::sync::Arc; + use std::thread; + + use nodedb_types::{DatabaseId, TenantId}; + let limits = EngineLimits::zeroed().with(EngineId::Vector, 10_000); let gov = Arc::new( MemoryGovernor::new(GovernorConfig {