c38ea811
test: exercise client core wasm canary
a73x 2026-08-16 14:46
Commit message
build.zig
| Old | New | ||
|---|---|---|---|
| @@ -598,13 +598,15 @@ pub fn build(b: *std.Build) void { | |||
| 598 | wasm_core_mod.addImport("client_core", wasm_mods[comptime idxOf("client_core")].?); | 598 | wasm_core_mod.addImport("client_core", wasm_mods[comptime idxOf("client_core")].?); |
| 599 | wasm_core_mod.addImport("replica", wasm_mods[comptime idxOf("replica")].?); | 599 | wasm_core_mod.addImport("replica", wasm_mods[comptime idxOf("replica")].?); |
| 600 | wasm_core_mod.addImport("keymap", wasm_mods[comptime idxOf("keymap")].?); | 600 | wasm_core_mod.addImport("keymap", wasm_mods[comptime idxOf("keymap")].?); |
| 601 | // Compile the semantic decoder as its own wasm object as well as making | 601 | // Compile a tiny, never-embedded canary that calls the semantic decoder |
| 602 | // it available to wasm_core. The object is a build-only cleanliness | 602 | // through typed mode and clipboard result paths. This build-only object |
| 603 | // guard: it forces this module's source and protocol import to undergo | 603 | // forces the production API and its validation helpers through wasm |
| 604 | // wasm semantic analysis without adding runtime exports or behavior. | 604 | // semantic analysis without adding runtime exports or behavior. |
| 605 | const client_core_wasm_check_mod = wasmMod(b, wasm_target, "src/client_core_wasm_check.zig"); | ||
| 606 | client_core_wasm_check_mod.addImport("client_core", client_core_wasm_mod); | ||
| 605 | const client_core_wasm_guard = b.addObject(.{ | 607 | const client_core_wasm_guard = b.addObject(.{ |
| 606 | .name = "client_core_wasm_guard", | 608 | .name = "client_core_wasm_check", |
| 607 | .root_module = client_core_wasm_mod, | 609 | .root_module = client_core_wasm_check_mod, |
| 608 | }); | 610 | }); |
| 609 | const wasm_exe = b.addExecutable(.{ .name = "mux_core", .root_module = wasm_core_mod }); | 611 | const wasm_exe = b.addExecutable(.{ .name = "mux_core", .root_module = wasm_core_mod }); |
| 610 | // A wasm reactor, not a command: no _start, and the exports must | 612 | // A wasm reactor, not a command: no _start, and the exports must |
src/client_core.zig
| Old | New | ||
|---|---|---|---|
| @@ -172,11 +172,11 @@ test "client core refuses BEL as the only invalid clipboard alphabet byte" { | |||
| 172 | 172 | ||
| 173 | test "client core accepts digits plus slash and plus in clipboard alphabet" { | 173 | test "client core accepts digits plus slash and plus in clipboard alphabet" { |
| 174 | var core = ClientCore{}; | 174 | var core = ClientCore{}; |
| 175 | const payload = [_]u8{ 0, 'c', 'A', 'B', '0', '1', '2', '+', '/', '=' }; | 175 | const payload = [_]u8{ 0, 'c', 'A', 'B', '0', '1', '2', '9', '+', '/', '=' }; |
| 176 | 176 | ||
| 177 | switch (core.receive(.term_event, &payload)) { | 177 | switch (core.receive(.term_event, &payload)) { |
| 178 | .effect => |effect| switch (effect) { | 178 | .effect => |effect| switch (effect) { |
| 179 | .clipboard_set => |set| try std.testing.expectEqualStrings("AB012+/=", set.base64), | 179 | .clipboard_set => |set| try std.testing.expectEqualStrings("AB0129+/=", set.base64), |
| 180 | else => return error.ExpectedClipboard, | 180 | else => return error.ExpectedClipboard, |
| 181 | }, | 181 | }, |
| 182 | else => return error.ExpectedClipboard, | 182 | else => return error.ExpectedClipboard, |
src/client_core_wasm_check.zig
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,32 @@ | |||
| 1 | const client_core = @import("client_core"); | ||
| 2 | |||
| 3 | /// Compile-only canary: keep a real typed mode receive call in the wasm | ||
| 4 | /// build, without exporting or embedding this check in muxweb. | ||
| 5 | export fn clientCoreWasmModesCheck() void { | ||
| 6 | var core = client_core.ClientCore{}; | ||
| 7 | const payload = [_]u8{ 1, 0, 0, 0 }; | ||
| 8 | const result = core.receive(.term_modes, &payload); | ||
| 9 | switch (result) { | ||
| 10 | .state => |state| switch (state) { | ||
| 11 | .terminal_modes => |modes| if (!modes.bracketed_paste) unreachable, | ||
| 12 | }, | ||
| 13 | else => unreachable, | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Compile-only canary for clipboard event decoding and validation. The | ||
| 18 | /// payload exercises the accepted target and alphabet through the public API. | ||
| 19 | export fn clientCoreWasmClipboardCheck() void { | ||
| 20 | var core = client_core.ClientCore{}; | ||
| 21 | const payload = [_]u8{ 0, 'c', 'A', 'B', '0', '1', '2', '9', '+', '/', '=' }; | ||
| 22 | const result = core.receive(.term_event, &payload); | ||
| 23 | switch (result) { | ||
| 24 | .effect => |effect| switch (effect) { | ||
| 25 | .clipboard_set => |set| { | ||
| 26 | if (set.target != 'c' or set.base64.len != 9) unreachable; | ||
| 27 | }, | ||
| 28 | else => unreachable, | ||
| 29 | }, | ||
| 30 | else => unreachable, | ||
| 31 | } | ||
| 32 | } | ||