From 204196420005b9e8b9df0dab24e0ef1e354b25c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 11:05:17 +0000 Subject: [PATCH] =?UTF-8?q?test(wasm-actor):=20cycle=2090=20=E2=80=94=20mi?= =?UTF-8?q?xed=20payloads,=20i64=20global,=20stopped-never-responds=20fuzz?= =?UTF-8?q?=20(410=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 5 tests: mixed payload types, i64 global, all three guest types, stopped actor never responds property test, exact 33-byte echo. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 124 ++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 33112ee..8a67afc 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -12966,4 +12966,128 @@ fn alloc_works_then_returns_negative() { rt.send_to(addr, framed_msg(inbox.addr(), b"drop")).unwrap(); rt.tick(); assert!(inbox.try_recv().is_none()); +} + +// ── Cycle 90 ───────────────────────────────────────────────────────────────── + +// Mixed payload types: binary + text + numbers — all echo correctly +#[test] +fn echo_mixed_payload_types() { + 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 payloads: Vec> = vec![ + b"hello world".to_vec(), + vec![0x00, 0xFF, 0x80], + (0..100).collect(), + vec![0u8; 1], + b"\x00\x01\x02\x03\x04".to_vec(), + ]; + + for p in &payloads { + rt.send_to(addr, framed_msg(inbox.addr(), p)).unwrap(); + } + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + assert_eq!(msgs.len(), payloads.len()); + for (got, expected) in msgs.iter().zip(payloads.iter()) { + assert_eq!(got, expected); + } +} + +// Guest with global initialized to i64 +#[test] +fn guest_i64_global() { + let wat = r#"(module + (memory (export "memory") 1) + (global $g (mut i64) (i64.const 9999999999)) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + (global.set $g (i64.add (global.get $g) (i64.const 1))) + (i64.store (local.get $ptr) (global.get $g)) + ) + )"#; + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap()) + .build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, ByteMessage(vec![0u8; 8])).unwrap(); + rt.tick(); +} + +// Spawn echo, double, and silent — each processes its type-specific behavior +#[test] +fn all_three_guest_types_in_one_runtime() { + let engine = SharedEngine::new().unwrap(); + let echo = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + let double = WasmActorBuilder::new(engine.clone(), guest_wasm("double")).build().unwrap(); + let silent = WasmActorBuilder::new(engine, guest_wasm("silent")).build().unwrap(); + + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + let e = rt.spawn(echo).unwrap(); + let d = rt.spawn(double).unwrap(); + let s = rt.spawn(silent).unwrap(); + + rt.send_to(e, framed_msg(inbox.addr(), b"e")).unwrap(); + rt.send_to(d, framed_msg(inbox.addr(), b"d")).unwrap(); + rt.send_to(s, ByteMessage(b"s".to_vec())).unwrap(); + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + assert_eq!(msgs.len(), 3, "echo(1) + double(2) + silent(0) = 3"); +} + +// Property: stopping an actor always makes it unresponsive +proptest! { + #[test] + fn prop_stopped_actor_never_responds( + n_before in 0usize..10, + n_after in 1usize..10, + ) { + 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(); + + for _ in 0..n_before { + let _ = rt.send_to(addr, framed_msg(inbox.addr(), b"x")); + } + rt.tick(); + // Drain inbox + while inbox.try_recv().is_some() {} + + rt.stop_actor(addr); + rt.tick(); + + for _ in 0..n_after { + let _ = rt.send_to(addr, framed_msg(inbox.addr(), b"y")); + } + rt.tick(); + rt.tick(); + + assert!(inbox.try_recv().is_none(), "stopped actor should never respond"); + } +} + +// Echo with exactly 33 bytes (32 addr + 1 payload byte) +#[test] +fn echo_exactly_thirty_three_bytes() { + 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(); + + rt.send_to(addr, framed_msg(inbox.addr(), &[0x42])).unwrap(); + rt.tick(); + + let resp = inbox.try_recv().expect("single byte payload echo"); + assert_eq!(resp.0, vec![0x42]); } \ No newline at end of file