From 98061c94641597ff6ca64a6c06ccbfdd4fd6cc2f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:00:31 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2050=20=E2=80=94=20modulo,=208KB?= =?UTF-8?q?=20echo,=20duplicate=20sends,=20full=20lifecycle=20fuzz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - guest_uses_modulo: i32.rem_u instruction for mod 10 computation - echo_8kb_payload: 8192-byte payload round-trips with pattern check - duplicate_sends_all_deliver: 5 identical messages all deliver - prop_full_lifecycle_never_panics: combined fuzz (1-5 actors, 0-10 msgs, any byte) All 216 tests pass (14 property tests). No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index e9c6827..9a550b0 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -7523,4 +7523,124 @@ fn send_before_first_tick_delivers() { let msg = inbox.try_recv().expect("message sent before first tick should deliver"); assert_eq!(msg.0, b"pre-tick"); +} + +// ── Guest with i32.rem_u (modulo) ────────────────────────────────────────── + +#[test] +fn guest_uses_modulo() { + let wat = r#" + (module + (import "swactor" "send" (func $send (param i32 i32 i32))) + (memory (export "memory") 1) + (func (export "alloc") (param i32) (result i32) i32.const 4096) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Read first payload byte, compute mod 10 + (i32.store8 (i32.const 200) + (i32.rem_u + (i32.load8_u (i32.add (local.get $ptr) (i32.const 32))) + (i32.const 10) + ) + ) + (call $send (local.get $ptr) (i32.const 200) (i32.const 1)) + ) + ) + "#; + let wasm = wat::parse_str(wat).unwrap(); + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wasm).build().unwrap(); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + rt.send_to(addr, framed_msg(inbox.addr(), &[47])).unwrap(); // 47 % 10 = 7 + rt.send_to(addr, framed_msg(inbox.addr(), &[100])).unwrap(); // 100 % 10 = 0 + rt.send_to(addr, framed_msg(inbox.addr(), &[3])).unwrap(); // 3 % 10 = 3 + rt.tick(); + + let results: Vec = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0[0])).collect(); + assert_eq!(results, vec![7, 0, 3]); +} + +// ── Echo with 8KB payload (tests larger than single page alloc) ───────────── + +#[test] +fn echo_8kb_payload() { + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap(); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + let payload: Vec = (0..8192).map(|i| (i % 251) as u8).collect(); + rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().unwrap(); + assert_eq!(msg.0.len(), 8192); + assert_eq!(msg.0, payload); +} + +// ── Multiple sends with exact same payload to same dest ───────────────────── + +#[test] +fn duplicate_sends_all_deliver() { + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap(); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + // Send the exact same message 5 times + for _ in 0..5 { + rt.send_to(addr, framed_msg(inbox.addr(), b"dup")).unwrap(); + } + rt.tick(); + + let mut count = 0; + while let Some(msg) = inbox.try_recv() { + assert_eq!(msg.0, b"dup"); + count += 1; + } + assert_eq!(count, 5, "all 5 duplicate sends should deliver"); +} + +// ── Property: build + tick + stop cycle never leaks (combined fuzz) ────────── + +proptest! { + #[test] + fn prop_full_lifecycle_never_panics( + n_actors in 1u8..5, + n_msgs in 0u8..10, + payload_byte in proptest::num::u8::ANY, + ) { + let engine = SharedEngine::new().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + let mut addrs = Vec::new(); + for _ in 0..n_actors { + let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + addrs.push(rt.spawn(actor).unwrap()); + } + + for addr in &addrs { + for _ in 0..n_msgs { + let _ = rt.send_to(*addr, framed_msg(inbox.addr(), &[payload_byte])); + } + } + rt.tick(); + rt.tick(); + + for addr in &addrs { + rt.stop_actor(*addr); + } + rt.tick(); + rt.tick(); + + while let Some(_) = inbox.try_recv() {} + } } \ No newline at end of file