`ninja plugin build` compiles a plugin to reactor-mode wasip1 wasm (Go >= 1.24 enforced), extracts manifest.pb by driving one HOOK_DESCRIBE over wazero with failing host stubs, and packs a tar.zst .bnp (plugin.wasm, plugin.mod, manifest.pb + migrations/schemas/assets/web-dist when present) with a summary table. A describe-time capability call (e.g. db.* from Register) fails with an actionable error naming the offending method. `ninja plugin verify` re-runs the CMS reader's layout/name/abi/path-safety/size checks standalone (deliberate duplication of cms backend/plugin/bnp/reader.go; kept in lockstep by WO-WZ-010). ABI riders (additive; buf breaking clean): - ABI_ERROR_CODE_TX_EXPIRED enum value + bnwasm guest mapping to a new bnwasm.ErrTxExpired sentinel (retryable tx expiry, distinct from real faults); the cms dbexec side adopts the emit separately. - PluginManifest.data_dir bool + a first-class `data_dir` key on the plugin.mod parser (so writeMod's struct round-trip can't drop it); `plugin build` stamps it from plugin.mod into the manifest. Docs: wasm-abi.md gains a Building & packing section, the error-code table row, the manifest data_dir mapping, and the plugin.mod reference. Tests: CLI e2e builds the WZ-002 fixture → verify + manifest block keys; a capfixture proves the actionable describe-time error; verify rejects each malformed class; bnwasm TX_EXPIRED classification. 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/core/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)
|
|
}
|
|
}
|