blob: 2225e2f44da049d3c2d73b7369044ab317eb4d71 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package instance
import (
"context"
"fmt"
"reflect"
"sync"
"testing"
)
import (
"github.com/dop251/goja"
_ "github.com/dop251/goja_nodejs/console"
"github.com/dop251/goja_nodejs/require"
"github.com/stretchr/testify/assert"
)
import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/base"
"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
)
var url1 = func() *common.URL {
i, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
"side=provider&timeout=3000&timestamp=1556509797245")
return i
}
var url2 = func() *common.URL {
u, _ := common.NewURL("dubbo://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&" +
"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1448&revision=0.0.1&" +
"side=provider&timeout=3000&timestamp=1556509797246")
return u
}
var url3 = func() *common.URL {
i, _ := common.NewURL("dubbo://127.0.0.1:20002/com.ikurento.user.UserProvider?anyhost=true&" +
"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1449&revision=0.0.1&" +
"side=provider&timeout=3000&timestamp=1556509797247")
return i
}
func getRouteArgs() ([]base.Invoker, base.Invocation, context.Context) {
return []base.Invoker{
base.NewBaseInvoker(url1()), base.NewBaseInvoker(url2()), base.NewBaseInvoker(url3()),
}, invocation.NewRPCInvocation("GetUser", nil, map[string]any{
"attachmentKey": []string{"attachmentValue"},
}),
context.TODO()
}
var localIp = common.GetLocalIp()
var Func_Script string = `
(function route(invokers,invocation,context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL().Port !== "20000"){
invokers[i].GetURL().Ip = "` + localIp + `"
result.push(invokers[i]);
}
}
}
return result;
}(invokers,invocation,context));`
func rt_link_external_libraries(runtime *goja.Runtime) {
require.NewRegistry().Enable(runtime)
}
func rt_init_args(runtime *goja.Runtime) {
invokers, invocation, context := getRouteArgs()
// 在 Goja 中注册 Go 的 struct
err := runtime.Set("invokers", invokers)
if err != nil {
panic(err)
}
err = runtime.Set("invocation", invocation)
if err != nil {
panic(err)
}
err = runtime.Set("context", context)
if err != nil {
panic(err)
}
}
func re_init_res_recv(runtime *goja.Runtime) {
err := runtime.Set(jsScriptResultName, nil)
if err != nil {
panic(err)
}
}
func TestStructureFuncImpl(t *testing.T) {
runtime := goja.New()
test_invokers := []base.Invoker{base.NewBaseInvoker(url1())}
test_invocation := invocation.NewRPCInvocation("GetUser", nil, map[string]any{"attachmentKey": []string{"attachmentValue"}})
test_context := context.TODO() // set invoker test field
for _, invoker := range test_invokers {
invoker.GetURL().Methods = []string{"testMethods"}
invoker.GetURL().Username = "testUsername"
invoker.GetURL().Password = "testPassword"
invoker.GetURL().SetParam(constant.VersionKey, "testVersion")
invoker.GetURL().SetParam(constant.GroupKey, "testGroup")
invoker.GetURL().SetParam(constant.InterfaceKey, "testInterface")
invoker.GetURL().SubURL = url3()
}
rt_link_external_libraries(runtime)
err := runtime.Set("invokers", test_invokers)
if err != nil {
panic(err)
}
err = runtime.Set("invocation", test_invocation)
if err != nil {
panic(err)
}
err = runtime.Set("context", test_context)
if err != nil {
panic(err)
}
re_init_res_recv(runtime)
// run go func in javaScript
_ = runtime.Set(`check`, func(str string, args ...any) {
/*
here set fmt.print to check func support
*/
//fmt.Printf("support %s\n", str)
//fmt.Print(args...)
//fmt.Print("\n")
})
/*
//may not require
invokers[0].GetURL().URLEqual(url *URL) bool
invokers[0].GetURL().ReplaceParams(param url.Values)
invokers[0].GetURL().RangeParams(f func(key string, value string) bool)
invokers[0].GetURL().GetParams() url.Values
invokers[0].GetURL().SetParams(m url.Values)
invokers[0].GetURL().MergeURL(anotherUrl *URL) *URL
invokers[0].GetURL().Clone() *URL
invokers[0].GetURL().CloneExceptParams(excludeParams *gxset.HashSet) *URL
invokers[0].GetURL().Compare(comp common.Comparator) int
invocation.SetInvoker(invoker protocol.Invoker)
invocation.CallBack()
invocation.SetCallBack(c any)
*/
// 在 JavaScript 中调用 Go 的 struct 的方法
_, err = runtime.RunString(`var console = require('console')
function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
// get all field
check(
'invokers[i].GetURL().Path',
invokers[i].GetURL().Path
)
check(
'invokers[i].GetURL().Address()',
invokers[i].GetURL().Address()
)
check(
'invokers[i].GetURL().Protocol',
invokers[i].GetURL().Protocol
)
check(
'invokers[i].GetURL().Location',
invokers[i].GetURL().Location
)
check(
'invokers[i].GetURL().Ip',
invokers[i].GetURL().Ip
)
check(
'invokers[i].GetURL().Port',
invokers[i].GetURL().Port
)
check(
'invokers[i].GetURL().PrimitiveURL',
invokers[i].GetURL().PrimitiveURL
)
check(
'invokers[i].GetURL().Username',
invokers[i].GetURL().Username
)
check(
'invokers[i].GetURL().Password',
invokers[i].GetURL().Password
)
check(
'invokers[i].GetURL().Methods',
invokers[i].GetURL().Methods
)
check(
'invokers[i].GetURL().SubURL',
invokers[i].GetURL().SubURL
)
// do all call
check(
'invokers[i].GetURL()',
invokers[i].GetURL()
)
check(
'invokers[i].GetURL().JavaClassName()',
invokers[i].GetURL().JavaClassName()
)
check(
'invokers[i].GetURL().Group()',
invokers[i].GetURL().Group()
)
check(
'invokers[i].GetURL().Interface()',
invokers[i].GetURL().Interface()
)
check(
'invokers[i].GetURL().Version()',
invokers[i].GetURL().Version()
)
check(
'invokers[i].GetURL().Address()',
invokers[i].GetURL().Address()
)
check(
'invokers[i].GetURL().String()',
invokers[i].GetURL().String()
)
check(
'invokers[i].GetURL().Key()',
invokers[i].GetURL().Key()
)
check(
'invokers[i].GetURL().GetCacheInvokerMapKey()',
invokers[i].GetURL().GetCacheInvokerMapKey()
)
check(
'invokers[i].GetURL().ServiceKey()',
invokers[i].GetURL().ServiceKey()
)
check(
'invokers[i].GetURL().ColonSeparatedKey()',
invokers[i].GetURL().ColonSeparatedKey()
)
check(
'invokers[i].GetURL().EncodedServiceKey()',
invokers[i].GetURL().EncodedServiceKey()
)
check(
'invokers[i].GetURL().Service()',
invokers[i].GetURL().Service()
)
check(
'invokers[i].GetURL().AddParam("key-string", "value-string")',
invokers[i].GetURL().AddParam("key-string", "value-string")
)
check(
'invokers[i].GetURL().AddParamAvoidNil("key-string", "value-string")',
invokers[i].GetURL().AddParamAvoidNil("key-string", "value-string")
)
check(
'invokers[i].GetURL().SetParam("key-string", "value-string")',
invokers[i].GetURL().SetParam("key-string", "value-string")
)
check(
'invokers[i].GetURL().SetAttribute("key-string", "value-any")',
invokers[i].GetURL().SetAttribute("key-string", "value-any")
)
check(
'invokers[i].GetURL().GetAttribute("key-string")',
invokers[i].GetURL().GetAttribute("key-string")
)
check(
'invokers[i].GetURL().DelParam("key-string")',
invokers[i].GetURL().DelParam("key-string")
)
check(
'invokers[i].GetURL().GetParam("key-string", "default-value")',
invokers[i].GetURL().GetParam("key-string", "default-value")
)
check(
'invokers[i].GetURL().GetNonDefaultParam("key-string")',
invokers[i].GetURL().GetNonDefaultParam("key-string")
)
check(
'invokers[i].GetURL().GetParamAndDecoded("key-string")',
invokers[i].GetURL().GetParamAndDecoded("key-string")
)
check(
'invokers[i].GetURL().GetRawParam("key-string")',
invokers[i].GetURL().GetRawParam("key-string")
)
check(
'invokers[i].GetURL().GetParamBool("key-string", true)',
invokers[i].GetURL().GetParamBool("key-string", true)
)
check(
'invokers[i].GetURL().GetParamInt("key-string", 64)',
invokers[i].GetURL().GetParamInt("key-string", 64)
)
check(
'invokers[i].GetURL().GetParamInt32("key-string", 64)',
invokers[i].GetURL().GetParamInt32("key-string", 64)
)
check(
'invokers[i].GetURL().GetParamByIntValue("key-string",64 )',
invokers[i].GetURL().GetParamByIntValue("key-string", 64)
)
check(
'invokers[i].GetURL().GetMethodParamInt("GetUser", "key-string", 64)',
invokers[i].GetURL().GetMethodParamInt("GetUser", "key-string", 64)
)
check(
'invokers[i].GetURL().GetMethodParamIntValue("GetUser", "key-string", 65)',
invokers[i].GetURL().GetMethodParamIntValue("GetUser", "key-string", 65)
)
check(
'invokers[i].GetURL().GetMethodParamInt64("GetUser", " key-string", 64)',
invokers[i].GetURL().GetMethodParamInt64("GetUser", " key-string", 64)
)
check(
'invokers[i].GetURL().GetMethodParam("GetUser", "key-string", "d string")',
invokers[i].GetURL().GetMethodParam("GetUser", "key-string", "d string")
)
check(
'invokers[i].GetURL().GetMethodParamBool("GetUser", "key-string", false)',
invokers[i].GetURL().GetMethodParamBool("GetUser", "key-string", false)
)
check(
'invokers[i].GetURL().ToMap()',
invokers[i].GetURL().ToMap()
)
check(
'invokers[i].GetURL().CloneWithParams(["a", "b", "c", "d"])',
invokers[i].GetURL().CloneWithParams(["a", "b", "c", "d"])
)
check(
'invokers[i].GetURL().GetParamDuration("s string", "d string")',
invokers[i].GetURL().GetParamDuration("s string", "d string")
)
// test invocation
check(
'invocation.MethodName()',
invocation.MethodName()
)
check(
'invocation.ActualMethodName()',
invocation.ActualMethodName()
)
check(
'invocation.IsGenericInvocation()',
invocation.IsGenericInvocation()
)
check(
'invocation.ParameterTypes()',
invocation.ParameterTypes()
)
check(
'invocation.ParameterTypeNames()',
invocation.ParameterTypeNames()
)
check(
'invocation.ParameterValues()',
invocation.ParameterValues()
)
check(
'invocation.ParameterRawValues()',
invocation.ParameterRawValues()
)
check(
'invocation.Arguments()',
invocation.Arguments()
)
check(
'invocation.Reply()',
invocation.Reply()
)
check(
'invocation.SetReply("interface key")',
invocation.SetReply("interface key")
)
check(
'invocation.Attachments()',
invocation.Attachments()
)
check(
'invocation.GetAttachmentInterface("key-string")',
invocation.GetAttachmentInterface("key-string")
)
check(
'invocation.Attributes()',
invocation.Attributes()
)
check(
'invocation.Invoker()',
invocation.Invoker()
)
check(
'invocation.ServiceKey()',
invocation.ServiceKey()
)
check(
'invocation.SetAttachment("key-string", "value-any")',
invocation.SetAttachment("key-string", "value-any")
)
check(
'invocation.GetAttachment("key-string")',
invocation.GetAttachment("key-string")
)
check(
'invocation.GetAttachmentWithDefaultValue("key-string", "defaultValue-string")',
invocation.GetAttachmentWithDefaultValue("key-string", "defaultValue-string")
)
check(
'invocation.SetAttribute("key-string", "value-any")',
invocation.SetAttribute("key-string", "value-any")
)
check(
'invocation.GetAttribute("key-string")',
invocation.GetAttribute("key-string")
)
check(
'invocation.GetAttributeWithDefaultValue("key-string", "defaultValue-any")',
invocation.GetAttributeWithDefaultValue("key-string", "defaultValue-any")
)
check(
'invocation.GetAttachmentAsContext()',
invocation.GetAttachmentAsContext()
)
}
return result;
}
__go_program_get_result = route(invokers,
invocation, context)
`)
assert.Nil(t, err)
}
func TestFuncWithCompile(t *testing.T) {
pg, err := goja.Compile("routeJs", jsScriptPrefix+Func_Script, true)
if err != nil {
panic(err)
}
rt := goja.New()
rt_link_external_libraries(rt)
rt_init_args(rt)
re_init_res_recv(rt)
res, err := rt.RunProgram(pg)
if err != nil {
panic(err)
}
assert.Equal(t, 2, len(res.Export().([]any)))
assert.Equal(t, localIp, (*(res.Export().([]any)[0]).(*base.BaseInvoker)).GetURL().Ip)
}
func TestFuncWithCompileConcurrent(t *testing.T) {
pg, err := goja.Compile("routeJs", jsScriptPrefix+Func_Script, true)
if err != nil {
panic(err)
}
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
rt := goja.New()
rt_link_external_libraries(rt)
rt_init_args(rt)
re_init_res_recv(rt)
res, err := rt.RunProgram(pg)
if err != nil {
panic(err)
}
assert.Equal(t, 2, len(res.Export().([]any)))
assert.Equal(t, localIp, (*(res.Export().([]any)[0]).(*base.BaseInvoker)).GetURL().Ip)
}()
}
wg.Wait()
}
func TestFuncWithCompileAndRunRepeatedly(t *testing.T) {
pg, err := goja.Compile("routeJs", jsScriptPrefix+`(
function route(invokers,invocation,context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL().Port !== "20000"){
invokers[i].GetURL().Ip = "`+localIp+`"
invokers[i].GetURL().Port = "20004"
result.push(invokers[i]);
}
}
}
return result;
})(invokers,invocation,context)`, true)
if err != nil {
panic(err)
}
rt := goja.New()
_ = rt.Set(`println`, func(args ...any) {
//fmt.Println(args...)
})
for i := 0; i < 100; i++ {
rt_link_external_libraries(rt)
rt_init_args(rt)
re_init_res_recv(rt)
res, err := rt.RunProgram(pg)
if err != nil {
panic(err)
}
assert.Equal(t, 2, len(res.Export().([]any)))
assert.Equal(t, localIp, (*(res.Export().([]any)[0]).(*base.BaseInvoker)).GetURL().Ip)
assert.Equal(t, "20004", (*(res.Export().([]any)[0]).(*base.BaseInvoker)).GetURL().Port)
}
}
func setRunScriptEnv() *goja.Runtime {
runtime := goja.New()
rt_link_external_libraries(runtime)
srcInvoker1, srcInvoker2, srcInvoker3 := base.NewBaseInvoker(url1()), base.NewBaseInvoker(url2()), base.NewBaseInvoker(url3())
getArgs := func() ([]base.Invoker, *invocation.RPCInvocation, context.Context) {
return []base.Invoker{
newScriptInvokerImpl(srcInvoker1), newScriptInvokerImpl(srcInvoker2), newScriptInvokerImpl(srcInvoker3),
}, invocation.NewRPCInvocation("GetUser", nil, map[string]any{
"attachmentKey": []string{"attachmentValue"},
}), context.TODO()
}
invokers, invocation, context := getArgs()
err := runtime.Set("invokers", invokers)
if err != nil {
panic(err)
}
err = runtime.Set("invocation", invocation)
if err != nil {
panic(err)
}
err = runtime.Set("context", context)
if err != nil {
panic(err)
}
re_init_res_recv(runtime)
return runtime
}
func TestRunScriptInPanic(t *testing.T) {
willPanic := func(errScript string) {
rt := setRunScriptEnv()
pg, err := goja.Compile(``, errScript, true)
assert.Nil(t, err)
res, err := func() (res any, err error) {
defer func(err *error) {
panicReason := recover()
if panicReason != nil {
switch panicReason := panicReason.(type) {
case string:
*err = fmt.Errorf("panic: %s", panicReason)
case error:
*err = panicReason
default:
*err = fmt.Errorf("panic occurred: failed to retrieve panic information. Expected string or error, got %T", panicReason)
}
}
}(&err)
res, err = rt.RunProgram(pg)
testData := res.(*goja.Object).Export()
assert.Equal(t, reflect.ValueOf([]any{}), reflect.ValueOf(testData))
return
}()
assert.NotNil(t, err)
assert.Nil(t, res)
}
wontPanic := func(errScript string) {
rt := setRunScriptEnv()
pg, err := goja.Compile(``, errScript, true)
assert.Nil(t, err)
res, err := rt.RunProgram(pg)
assert.Nil(t, err)
assert.NotNil(t, res)
data := res.Export()
assert.NotNil(t, data)
}
const scriptInvokePanic = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL().Port !== "20000") {
invokers[i].GetURL().Ip = "anotherIP"
// Invoke() call should go panic
invokers[i].Invoke(context, invocation)
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
willPanic(scriptInvokePanic)
const scriptNodeDestroyPanic = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL().Port !== "20000") {
invokers[i].GetURL().Ip = "anotherIP"
// Destroy() call should go panic
invokers[i].Destroy()
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
willPanic(scriptNodeDestroyPanic)
// Wrong Call will go panic , recover , and return an error
const scriptCallWrongFunc = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
// err here
if (invokers[i].GetURLS(" Err Here ").Port !== "20000") {
invokers[i].GetURLS().Ip = "anotherIP"
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
willPanic(scriptCallWrongFunc)
// these case means
// error input is program acceptable, but it will make an undefined error
const scriptCallWrongArgs1 = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL(" Err Here ").Port !== "20000") {
//---------------------------------^ here wont go err , it will trans to ()
invokers[i].GetURL().Ip = "anotherIP"
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
wontPanic(scriptCallWrongArgs1)
const scriptCallWrongArgs2 = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL(" Err Here ").Port !== "20000") {
invokers[i].GetURL().AddParam( null , "key string", "value string")
//---------------------------------------------^ here wont go err , it will trans to ("" , "key string" , "value string")
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
wontPanic(scriptCallWrongArgs2)
const scriptCallWrongArgs3 = `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if ("127.0.0.1" === invokers[i].GetURL().Ip) {
if (invokers[i].GetURL(" Err Here ").Port !== "20000") {
invokers[i].GetURL().AddParam( invocation , "key string", "value string")
//---------------------------------------------^ here wont go err , it will trans to ("[object Object]" , "key string" , "value string")
result.push(invokers[i]);
}
}
}
return result;
}(invokers, invocation, context));
`
wontPanic(scriptCallWrongArgs3)
}