js/bridge_test.go

163 lines
3.1 KiB
Go
Raw Normal View History

package js
import (
"context"
"errors"
"testing"
"apigo.cc/go/jsmod"
"github.com/dop251/goja"
)
type User struct {
ID int
Name string
}
func (u *User) GetInfo() string {
return u.Name
}
func TestBridgeDataFidelity(t *testing.T) {
vm := goja.New()
// 1. Setup Go functions
originalUser := &User{ID: 1, Name: "Star"}
getUser := func() *User {
return originalUser
}
verifyUser := func(u *User) bool {
// Verify pointer address remains the same (Host Object fidelity)
return u == originalUser
}
// Register functions manually for testing bridge
vm.Set("getUser", wrapGoFunc(vm, getUser))
vm.Set("verifyUser", wrapGoFunc(vm, verifyUser))
// 2. JS Execution
script := `
let u = getUser();
if (u.Name !== "Star") throw "Name mismatch: " + u.Name;
if (u.ID !== 1) throw "ID mismatch: " + u.ID;
// Host Object method call (if exported)
// Note: goja requires methods to be exported and usually works better with struct pointers
let isSame = verifyUser(u);
if (!isSame) throw "Pointer mismatch in Go side";
"ok"
`
val, err := vm.RunString(script)
if err != nil {
t.Fatalf("JS execution failed: %v", err)
}
if val.Export() != "ok" {
t.Errorf("expected 'ok', got %v", val.Export())
}
}
func TestBridgeCasting(t *testing.T) {
vm := goja.New()
sum := func(a, b int) int {
return a + b
}
vm.Set("sum", wrapGoFunc(vm, sum))
// Test passing string as number (frictionless casting via go/cast)
script := `sum("10", 20)`
val, err := vm.RunString(script)
if err != nil {
t.Fatal(err)
}
if val.Export().(int64) != 30 {
t.Errorf("expected 30, got %v", val.Export())
}
}
func TestBridgeErrorHandling(t *testing.T) {
vm := goja.New()
failFunc := func() (string, error) {
return "", errors.New("go_error")
}
vm.Set("failFunc", wrapGoFunc(vm, failFunc))
script := `
try {
failFunc();
} catch (e) {
e.message;
}
`
val, err := vm.RunString(script)
if err != nil {
t.Fatal(err)
}
if val.Export() != "go_error" {
t.Errorf("expected 'go_error', got %v", val.Export())
}
}
func TestBridgeContextInjection(t *testing.T) {
vm := goja.New()
ctx := context.WithValue(context.Background(), "key", "value")
// Inject context into VM
vm.Set("__ctx__", vm.ToValue(ctx))
checkCtx := func(c context.Context) string {
return c.Value("key").(string)
}
vm.Set("checkCtx", wrapGoFunc(vm, checkCtx))
val, err := vm.RunString(`checkCtx()`)
if err != nil {
t.Fatal(err)
}
if val.Export() != "value" {
t.Errorf("expected 'value', got %v", val.Export())
}
}
func TestBridgeComplexStruct(t *testing.T) {
vm := goja.New()
type Complex struct {
Data map[string]any
Tags []string
}
process := func(c Complex) int {
return len(c.Tags) + len(c.Data)
}
vm.Set("process", wrapGoFunc(vm, process))
script := `
process({
Tags: ["a", "b"],
Data: { "x": 1, "y": 2, "z": 3 }
})
`
val, err := vm.RunString(script)
if err != nil {
t.Fatal(err)
}
if val.Export().(int64) != 5 {
t.Errorf("expected 5, got %v", val.Export())
}
}
// Ensure jsmod is used to avoid unused import if needed
func init() {
_ = jsmod.GetModules()
}