use std::sync::Arc;
use tensor_wasm_core::types::TenantId;
use tensor_wasm_exec::engine::{EngineConfig, MemoryBackend, TensorWasmEngine};
use tensor_wasm_exec::executor::{ExecError, SpawnConfig, TensorWasmExecutor};
fn classifier_recognizes(err_chain_lowercased: &str) -> bool {
err_chain_lowercased.contains("memory")
&& (err_chain_lowercased.contains("exceeds the limit")
|| err_chain_lowercased.contains("exceeds the configured"))
}
fn provoke_real_pooling_memory_error() -> wasmtime::Error {
use wasmtime::{Config, Engine, InstanceAllocationStrategy, Module, PoolingAllocationConfig};
let mut pool = PoolingAllocationConfig::default();
pool.total_memories(1);
pool.max_memory_size(64 * 1024);
let mut cfg = Config::new();
cfg.memory_reservation(4 * 1024 * 1024);
cfg.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
let engine = Engine::new(&cfg).expect("pooling engine builds");
let wasm = wat::parse_str(r#"(module (memory (export "mem") 64))"#).expect("valid wat");
match Module::new(&engine, &wasm) {
Err(err) => err,
Ok(module) => {
let mut store = wasmtime::Store::new(&engine, ());
match wasmtime::Instance::new(&mut store, &module, &[]) {
Ok(_) => panic!(
"expected the pooling allocator to REFUSE a 4 MiB memory in a 64 KiB slot; \
if instantiation now succeeds the test fixture no longer provokes the \
memory-size refusal classify_instantiation_error keys off"
),
Err(err) => err,
}
}
}
}
#[test]
fn test_classifier_phrasing_still_present_in_real_wasmtime_error() {
let err = provoke_real_pooling_memory_error();
let chain = format!("{err:#}").to_ascii_lowercase();
assert!(
classifier_recognizes(&chain),
"WASMTIME PHRASING DRIFT (audit L-3): the genuine pooling-allocator \
memory-size error no longer matches the substring rule in \
executor::classify_instantiation_error, so that function will SILENTLY \
stop refining wasmtime errors into the typed \
ExecError::ModuleMemoryTooLarge. Re-read the pooling memory_pool error \
strings for the current wasmtime release and update BOTH the substring \
match in executor.rs AND `classifier_recognizes` in this test. \
Real error chain was: {chain:?}"
);
let has_limit = chain.contains("exceeds the limit");
let has_configured = chain.contains("exceeds the configured");
assert!(
has_limit || has_configured,
"neither limit phrasing present in real wasmtime error: {chain:?}"
);
}
const ENGINE_CAP: usize = 256 * 1024 * 1024;
const FOUR_GIB_PAGES: u32 = 65_536;
fn make_executor() -> TensorWasmExecutor {
let cfg = EngineConfig {
max_memory_bytes: ENGINE_CAP,
backend: MemoryBackend::PoolingMpk {
max_memories: 4,
memory_bytes: ENGINE_CAP,
},
..EngineConfig::default()
};
let engine = Arc::new(TensorWasmEngine::with_config(cfg).expect("engine"));
TensorWasmExecutor::new(engine)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn spawn_oversized_module_yields_typed_variant_not_generic_wasmtime() {
let exec = make_executor();
let wasm = wat::parse_str(format!(
"(module (memory (export \"mem\") {FOUR_GIB_PAGES}))"
))
.expect("valid wat");
let err = exec
.spawn_instance(SpawnConfig::for_tenant(TenantId(1)), &wasm)
.await
.expect_err("a 4 GiB memory declaration must be rejected");
assert!(
!matches!(err, ExecError::Wasmtime(_)),
"audit L-3: oversized-memory module surfaced the OPAQUE \
ExecError::Wasmtime instead of the typed ModuleMemoryTooLarge — the \
refinement contract callers depend on has broken. Got: {err:?}"
);
match err {
ExecError::ModuleMemoryTooLarge {
requested_bytes,
limit_bytes,
} => {
assert_eq!(limit_bytes as usize, ENGINE_CAP);
assert!(
requested_bytes as usize > ENGINE_CAP,
"requested {requested_bytes} must exceed cap {ENGINE_CAP}"
);
}
other => panic!("expected typed ModuleMemoryTooLarge, got {other:?}"),
}
assert_eq!(
exec.live_count(),
0,
"no instance must survive a rejected oversized spawn"
);
}