From dda7b1f1e9e0b351984dd50d7f8c842c96b5ec28 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:55:03 +0000 Subject: [PATCH] =?UTF-8?q?test(wasm-actor):=20cycle=2085=20=E2=80=94=20no?= =?UTF-8?q?n-aligned=20payload,=205x=20grow,=20budget=20overflow=20(379=20?= =?UTF-8?q?tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 5 tests: 7-byte non-aligned payload, tick empty runtime then spawn, grow memory 5 times, ByteMessage clone independence, budget leaves remaining for next tick. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 94807f7..14f931e 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -12114,4 +12114,110 @@ fn echo_determinism_ten_runs() { assert!(outputs.iter().all(|o| o == &outputs[0]), "all 10 runs should produce identical output"); +} + +// ── Cycle 85 ───────────────────────────────────────────────────────────────── + +// Echo receives 7-byte payload (non-aligned) — tests non-power-of-2 sizes +#[test] +fn echo_seven_byte_non_aligned_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![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]; + rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap(); + rt.tick(); + + let resp = inbox.try_recv().expect("7-byte echo"); + assert_eq!(resp.0, payload); +} + +// Send to actor after runtime tick with no actors — just tests tick robustness +#[test] +fn tick_empty_runtime_then_spawn_and_use() { + let engine = SharedEngine::new().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + + // Tick empty runtime + for _ in 0..5 { + rt.tick(); + } + + // Now spawn and use + let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap(); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, framed_msg(inbox.addr(), b"after-empty")).unwrap(); + rt.tick(); + + assert_eq!(inbox.try_recv().unwrap().0, b"after-empty"); +} + +// Guest that does nothing but grow memory 5 times +#[test] +fn guest_grows_memory_five_times() { + let wat = r#"(module + (memory (export "memory") 1) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + (drop (memory.grow (i32.const 1))) + (drop (memory.grow (i32.const 1))) + (drop (memory.grow (i32.const 1))) + (drop (memory.grow (i32.const 1))) + (drop (memory.grow (i32.const 1))) + ) + )"#; + 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![0])).unwrap(); + rt.tick(); // grows from 1 to 6 pages — no panic +} + +// ByteMessage implements Clone — test that cloned messages are independent +#[test] +fn byte_message_clone_independence() { + let original = ByteMessage(vec![1, 2, 3]); + let clone = original.clone(); + assert_eq!(original.0, clone.0); + // They should be equal but independent + drop(original); + assert_eq!(clone.0, vec![1, 2, 3]); +} + +// Guest echo processes max-budget messages, leaves rest for next tick +#[test] +fn budget_leaves_remaining_for_next_tick() { + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap(); + let mut cfg = RuntimeConfig::default(); + cfg.actor_message_budget = 5; + let rt = Runtime::new(cfg); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + // Send 12 messages + for i in 0u8..12 { + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + } + + // First tick: up to 5 + rt.tick(); + let t1 = std::iter::from_fn(|| inbox.try_recv()).count(); + + // Second tick: up to 5 more + rt.tick(); + let t2 = std::iter::from_fn(|| inbox.try_recv()).count(); + + // Third tick: remaining + rt.tick(); + let t3 = std::iter::from_fn(|| inbox.try_recv()).count(); + + assert_eq!(t1 + t2 + t3, 12, "all 12 processed across ticks"); + assert!(t1 <= 5, "budget limits first tick"); } \ No newline at end of file