163 lines
3.1 KiB
Go
163 lines
3.1 KiB
Go
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()
|
|
}
|