package wasmguest // End-to-end proof that the guest capability stubs (package caps, wired into // CoreServices by WO-WZ-003) marshal to the ABI, cross into a host over the // generic host_call import, and return decoded values the plugin's unchanged // service code consumes — all inside a real wazero-compiled wasm module. // // The host_call handler below is a throwaway fake capability table (like the // WO-WZ-002 throwaway host), not the cms host. It answers exactly the three // families the fixture's Load hook exercises. import ( "context" "testing" abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" "google.golang.org/protobuf/proto" ) // installBlockninjaHost instantiates the "blockninja" import module exporting // host_call, mirroring the guest calling convention in reverse (read a // HostCallRequest from guest memory, answer it, write a HostCallResponse back // via the guest's bn_alloc, return the packed pointer). func installBlockninjaHost(t *testing.T, ctx context.Context, r wazero.Runtime) { t.Helper() _, err := r.NewHostModuleBuilder("blockninja"). NewFunctionBuilder(). WithFunc(func(ctx context.Context, mod api.Module, ptr, size uint32) uint64 { data, ok := mod.Memory().Read(ptr, size) if !ok { return 0 } req := &abiv1.HostCallRequest{} if err := proto.Unmarshal(data, req); err != nil { return 0 } out, err := proto.Marshal(fakeCapability(req.GetMethod(), req.GetPayload())) if err != nil { return 0 } // Allocate the response in guest memory (bn_alloc may grow memory, // so req is already decoded — data is no longer referenced). res, err := mod.ExportedFunction("bn_alloc").Call(ctx, uint64(len(out))) if err != nil || len(res) == 0 || res[0] == 0 { return 0 } respPtr := uint32(res[0]) if !mod.Memory().Write(respPtr, out) { return 0 } return uint64(respPtr)<<32 | uint64(uint32(len(out))) }). Export("host_call"). Instantiate(ctx) if err != nil { t.Fatalf("instantiate blockninja host module: %v", err) } } // fakeCapability answers the three capability methods the fixture Load hook // calls; everything else is UNIMPLEMENTED. func fakeCapability(method string, payload []byte) *abiv1.HostCallResponse { pack := func(m proto.Message) *abiv1.HostCallResponse { b, err := proto.Marshal(m) if err != nil { return &abiv1.HostCallResponse{Error: &abiv1.AbiError{ Code: abiv1.AbiErrorCode_ABI_ERROR_CODE_INTERNAL, Message: err.Error(), }} } return &abiv1.HostCallResponse{Payload: b} } switch method { case "content.get_page": return pack(&abiv1.ContentGetPageResponse{Page: &abiv1.PageInfo{Slug: "about", Title: "About Us"}}) case "settings.get_site_settings": return pack(&abiv1.SettingsGetSiteSettingsResponse{SettingsJson: []byte(`{"site_name":"Fixture Site"}`)}) case "bridge.get_service": return pack(&abiv1.BridgeGetServiceResponse{Available: false}) default: return &abiv1.HostCallResponse{Error: &abiv1.AbiError{ Code: abiv1.AbiErrorCode_ABI_ERROR_CODE_UNIMPLEMENTED, Message: "no fake for " + method, }} } } // TestWasmFixtureCapabilityRoundTrip drives HOOK_LOAD (which calls the // capability stubs) then renders the report block that surfaces the results, // proving deps.Content / deps.Settings / deps.Bridge cross the ABI end-to-end. func TestWasmFixtureCapabilityRoundTrip(t *testing.T) { if testing.Short() { t.Skip("compiles a wasm module; skipped in -short") } w := instantiateFixture(t) resp := w.invoke(t, abiv1.Hook_HOOK_LOAD, &abiv1.LoadRequest{}) if resp.GetError() != nil { t.Fatalf("LOAD error: %v", resp.GetError()) } resp = w.invoke(t, abiv1.Hook_HOOK_RENDER_BLOCK, &abiv1.RenderBlockRequest{ BlockKey: "wasmfixture:greeting", ContentJson: []byte(`{"report":true}`), }) if resp.GetError() != nil { t.Fatalf("RENDER_BLOCK error: %v", resp.GetError()) } rb := &abiv1.RenderBlockResponse{} if err := proto.Unmarshal(resp.GetPayload(), rb); err != nil { t.Fatalf("unmarshal RenderBlockResponse: %v", err) } const want = "

capreport:page=About Us site=Fixture Site bridge_nil=true

" if rb.GetHtml() != want { t.Errorf("capability report html = %q, want %q", rb.GetHtml(), want) } t.Logf("capability round trip through wasm: %s", rb.GetHtml()) }