Files
jscriptx/govalue_test.go
T
what 7f484d7a71 test: 引擎核心的测试与基准
除了常规用例,有几组是专门钉住「引擎的既定事实」的,改动时会先红:

  - vmstate_test  goja 的硬限制:非原始值不是 goroutine 安全的、对象跨不了
                  Runtime。其中一条是哨兵——goja 哪天允许对象跨 Runtime 了,
                  它会失败,提醒我们可以简化设计。
  - govalue_test  Go 的切片/map 进到脚本里长什么样。这不是本库的行为而是
                  goja 的,但脚本作者照着它写代码,变了会静默走错分支。
  - safety_test   并发下坏 VM 不会被别的 goroutine 捡到。
  - bench_gobind  脚本碰 Go 对象的单次开销,README 性能一节的数据来源。

missing_global_test 里有一条断言 typeof setTimeout === "undefined":
定时器必须保持未定义,库里的特性探测才能正常降级,防止以后有人把桩加回来。
2026-09-05 22:13:57 +08:00

97 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package jscriptx_test
import (
"context"
"testing"
"git.fsdpf.net/go/jscriptx"
)
// Go 的切片和 map 进到脚本里长什么样。
//
// 这不是 jscriptx 的行为,是 goja 的——但脚本作者是照着这个写代码的,一旦变了,
// 业务脚本会**静默**走错分支(比如查出来的记录突然 Array.isArray 为 false
// 整段列表被当成空的),所以在这里钉住。
//
// 之所以走 jscriptx 而不是直接用 goja:注入前还经过一层 freeze,得连那层一起验。
func TestGo值到脚本里的形状(t *testing.T) {
e, err := jscriptx.New(jscriptx.WithLogger(nil))
if err != nil {
t.Fatal(err)
}
defer e.Close()
// 照着资源查询的返回长的:[]map[string]any,里面混着切片、字符串和 nil
records := func() []map[string]any {
return []map[string]any{{
"role": "user",
"content": "你好",
"attempts": []any{map[string]any{"name": "a.pdf"}, map[string]any{"url": "http://x"}},
"tool_calls": `[{"id":"1"}]`, // 列里存的是 JSON 字符串
"reasoning": nil,
}}
}
s, err := e.Compile("shape.ts", `
export default class C {
Run() {
const rows = getRecords()
const r = rows[0]
return {
// 切片是真数组:length / for-of / map 都能用
rowsIsArray: Array.isArray(rows),
rowsLen: rows.length,
attemptsIsArray: Array.isArray(r.attempts),
attemptsLen: r.attempts.length,
mapped: r.attempts.map(a => a.name || a.url).join(","),
// map[string]any 的值按 Go 的类型各自转换,不会统一成字符串
contentType: typeof r.content,
toolCallsType: typeof r.tool_calls,
// Go 的 nil 是 null,不是 undefined——判空要用 == null 或 === null
reasoningIsNull: r.reasoning === null,
reasoningIsUndef: r.reasoning === undefined,
// 没有的列是 undefined,不会报错
missingIsUndef: r.nope === undefined,
}
}
}`)
if err != nil {
t.Fatal(err)
}
ctx := jscriptx.WithScope(context.Background(),
jscriptx.ScopeGlobals(map[string]any{"getRecords": records}))
obj, err := s.New(ctx)
if err != nil {
t.Fatal(err)
}
defer obj.Close()
var got map[string]any
if err := obj.CallInto(ctx, "Run", &got); err != nil {
t.Fatal(err)
}
want := map[string]any{
"rowsIsArray": true,
"rowsLen": int64(1),
"attemptsIsArray": true,
"attemptsLen": int64(2),
"mapped": "a.pdf,http://x",
"contentType": "string",
"toolCallsType": "string",
"reasoningIsNull": true,
"reasoningIsUndef": false,
"missingIsUndef": true,
}
for k, w := range want {
if got[k] != w {
t.Errorf("%s = %#v,该是 %#v", k, got[k], w)
}
}
}