Bootstrap the plugin-facing SDK module so the fleet migration (P2) becomes a mechanical block/core/X -> block/pluginsdk/X import rewrite. - abi/proto/v1: the single source-of-truth ABI proto tree, copied from the cms authoring source (cms/backend/abi/proto/v1) with go_package retargeted to git.dev.alexdunmow.com/block/pluginsdk/abi/v1;abiv1. Kills the hand-kept cms/core proto duplication (audit gap 4). - abi/v1: generated Go bindings (buf generate); byte-identical to core's abi/v1 save the embedded go_package path. - plugin/ (registration + DI surface), plugin/wasmguest/** (transport shim, caps stubs, bnwasm db driver, testdata fixtures + golden .pb), and the guest-facing type packages: blocks (+builtin/shared/tags), templates (+pongo/bn), auth, settings, content, gating, crypto, rbac, video, ai, subscriptions, menus, datasources. Internal imports rewritten core -> pluginsdk; zero block/core references remain. - README/AGENTS(+CLAUDE symlink)/Makefile: proto is the contract, Go is one binding; no replace directives; templates/bn is a synced copy authored in cms. Spec: cms docs/superpowers/specs/2026-07-07-proto-first-plugin-sdk-design.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
75 lines
2.4 KiB
Go
75 lines
2.4 KiB
Go
package bnwasm
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// fakeHostErr mirrors *wasmguest.HostError's AbiErrorCode() method so the
|
|
// transport's cross-boundary classification (mapTransportErr) can be exercised
|
|
// natively, without importing the wasip1-only wasmguest package.
|
|
type fakeHostErr struct {
|
|
code abiv1.AbiErrorCode
|
|
msg string
|
|
}
|
|
|
|
func (e *fakeHostErr) Error() string { return e.msg }
|
|
func (e *fakeHostErr) AbiErrorCode() abiv1.AbiErrorCode { return e.code }
|
|
|
|
// transportReturning builds a Transport whose every call fails with err.
|
|
func transportReturning(err error) Transport {
|
|
return func(_ string, _, _ proto.Message) error { return err }
|
|
}
|
|
|
|
func TestTransportMapsTxExpiredToSentinel(t *testing.T) {
|
|
hostErr := &fakeHostErr{code: abiv1.AbiErrorCode_ABI_ERROR_CODE_TX_EXPIRED, msg: "host: tx handle expired"}
|
|
tr := transportReturning(hostErr)
|
|
ctx := context.Background()
|
|
|
|
// Every db.* verb that crosses the transport must classify TX_EXPIRED.
|
|
t.Run("query", func(t *testing.T) {
|
|
_, err := tr.query(ctx, "SELECT 1", 7, nil)
|
|
if !errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("query err = %v, want ErrTxExpired", err)
|
|
}
|
|
})
|
|
t.Run("exec", func(t *testing.T) {
|
|
_, err := tr.exec(ctx, "DELETE FROM t", 7, nil)
|
|
if !errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("exec err = %v, want ErrTxExpired", err)
|
|
}
|
|
})
|
|
t.Run("txBegin", func(t *testing.T) {
|
|
_, err := tr.txBegin(ctx)
|
|
if !errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("txBegin err = %v, want ErrTxExpired", err)
|
|
}
|
|
})
|
|
t.Run("txCommit", func(t *testing.T) {
|
|
if err := tr.txCommit(ctx, 7); !errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("txCommit err = %v, want ErrTxExpired", err)
|
|
}
|
|
})
|
|
t.Run("txRollback", func(t *testing.T) {
|
|
if err := tr.txRollback(ctx, 7); !errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("txRollback err = %v, want ErrTxExpired", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTransportLeavesOtherAbiErrorsUntouched(t *testing.T) {
|
|
hostErr := &fakeHostErr{code: abiv1.AbiErrorCode_ABI_ERROR_CODE_INTERNAL, msg: "boom"}
|
|
tr := transportReturning(hostErr)
|
|
_, err := tr.query(context.Background(), "SELECT 1", 0, nil)
|
|
if errors.Is(err, ErrTxExpired) {
|
|
t.Fatalf("non-tx-expired error was misclassified as ErrTxExpired: %v", err)
|
|
}
|
|
if !errors.Is(err, error(hostErr)) {
|
|
t.Fatalf("original host error not preserved: %v", err)
|
|
}
|
|
}
|