Adding upstream version 0.0~git20250409.f7acab6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
b9b5d88025
commit
21b930d007
51 changed files with 11229 additions and 0 deletions
587
require/module_test.go
Normal file
587
require/module_test.go
Normal file
|
@ -0,0 +1,587 @@
|
|||
package require
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
js "github.com/dop251/goja"
|
||||
)
|
||||
|
||||
func mapFileSystemSourceLoader(files map[string]string) SourceLoader {
|
||||
return func(p string) ([]byte, error) {
|
||||
s, ok := files[filepath.ToSlash(p)]
|
||||
if !ok {
|
||||
return nil, ModuleFileDoesNotExistError
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireNativeModule(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var m = require("test/m");
|
||||
m.test();
|
||||
`
|
||||
|
||||
vm := js.New()
|
||||
|
||||
registry := new(Registry)
|
||||
registry.Enable(vm)
|
||||
|
||||
RegisterNativeModule("test/m", func(runtime *js.Runtime, module *js.Object) {
|
||||
o := module.Get("exports").(*js.Object)
|
||||
o.Set("test", func(call js.FunctionCall) js.Value {
|
||||
return runtime.ToValue("passed")
|
||||
})
|
||||
})
|
||||
|
||||
v, err := vm.RunString(SCRIPT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !v.StrictEquals(vm.ToValue("passed")) {
|
||||
t.Fatalf("Unexpected result: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterCoreModule(t *testing.T) {
|
||||
vm := js.New()
|
||||
|
||||
registry := new(Registry)
|
||||
registry.Enable(vm)
|
||||
|
||||
RegisterCoreModule("coremod", func(runtime *js.Runtime, module *js.Object) {
|
||||
o := module.Get("exports").(*js.Object)
|
||||
o.Set("test", func(call js.FunctionCall) js.Value {
|
||||
return runtime.ToValue("passed")
|
||||
})
|
||||
})
|
||||
|
||||
RegisterCoreModule("coremod1", func(runtime *js.Runtime, module *js.Object) {
|
||||
o := module.Get("exports").(*js.Object)
|
||||
o.Set("test", func(call js.FunctionCall) js.Value {
|
||||
return runtime.ToValue("passed1")
|
||||
})
|
||||
})
|
||||
|
||||
RegisterCoreModule("node:test1", func(runtime *js.Runtime, module *js.Object) {
|
||||
o := module.Get("exports").(*js.Object)
|
||||
o.Set("test", func(call js.FunctionCall) js.Value {
|
||||
return runtime.ToValue("test1 passed")
|
||||
})
|
||||
})
|
||||
|
||||
registry.RegisterNativeModule("bob", func(runtime *js.Runtime, module *js.Object) {
|
||||
|
||||
})
|
||||
|
||||
_, err := vm.RunString(`
|
||||
const m1 = require("coremod");
|
||||
const m2 = require("node:coremod");
|
||||
if (m1 !== m2) {
|
||||
throw new Error("Modules are not equal");
|
||||
}
|
||||
if (m1.test() !== "passed") {
|
||||
throw new Error("m1.test() has failed");
|
||||
}
|
||||
|
||||
const m3 = require("node:coremod1");
|
||||
const m4 = require("coremod1");
|
||||
if (m3 !== m4) {
|
||||
throw new Error("Modules are not equal (1)");
|
||||
}
|
||||
if (m3.test() !== "passed1") {
|
||||
throw new Error("m3.test() has failed");
|
||||
}
|
||||
|
||||
try {
|
||||
require("node:bob");
|
||||
} catch (e) {
|
||||
if (!e.message.includes("No such built-in module")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
require("bob");
|
||||
|
||||
try {
|
||||
require("test1");
|
||||
throw new Error("Expected exception");
|
||||
} catch (e) {
|
||||
if (!e.message.includes("Invalid module")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (require("node:test1").test() !== "test1 passed") {
|
||||
throw new Error("test1.test() has failed");
|
||||
}
|
||||
`)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireRegistryNativeModule(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var log = require("test/log");
|
||||
log.print('passed');
|
||||
`
|
||||
|
||||
logWithOutput := func(w io.Writer, prefix string) ModuleLoader {
|
||||
return func(vm *js.Runtime, module *js.Object) {
|
||||
o := module.Get("exports").(*js.Object)
|
||||
o.Set("print", func(call js.FunctionCall) js.Value {
|
||||
fmt.Fprint(w, prefix, call.Argument(0).String())
|
||||
return js.Undefined()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
vm1 := js.New()
|
||||
buf1 := &bytes.Buffer{}
|
||||
|
||||
registry1 := new(Registry)
|
||||
registry1.Enable(vm1)
|
||||
|
||||
registry1.RegisterNativeModule("test/log", logWithOutput(buf1, "vm1 "))
|
||||
|
||||
vm2 := js.New()
|
||||
buf2 := &bytes.Buffer{}
|
||||
|
||||
registry2 := new(Registry)
|
||||
registry2.Enable(vm2)
|
||||
|
||||
registry2.RegisterNativeModule("test/log", logWithOutput(buf2, "vm2 "))
|
||||
|
||||
_, err := vm1.RunString(SCRIPT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := buf1.String()
|
||||
if s != "vm1 passed" {
|
||||
t.Fatalf("vm1: Unexpected result: %q", s)
|
||||
}
|
||||
|
||||
_, err = vm2.RunString(SCRIPT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s = buf2.String()
|
||||
if s != "vm2 passed" {
|
||||
t.Fatalf("vm2: Unexpected result: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequire(t *testing.T) {
|
||||
absPath, err := filepath.Abs("./testdata/m.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
isWindows := runtime.GOOS == "windows"
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
"./testdata/m.js",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"../require/testdata/m.js",
|
||||
true,
|
||||
},
|
||||
{
|
||||
absPath,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`.\testdata\m.js`,
|
||||
isWindows,
|
||||
},
|
||||
{
|
||||
`..\require\testdata\m.js`,
|
||||
isWindows,
|
||||
},
|
||||
}
|
||||
|
||||
const SCRIPT = `
|
||||
var m = require(testPath);
|
||||
m.test();
|
||||
`
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
vm := js.New()
|
||||
vm.Set("testPath", test.path)
|
||||
|
||||
registry := new(Registry)
|
||||
registry.Enable(vm)
|
||||
|
||||
v, err := vm.RunString(SCRIPT)
|
||||
|
||||
ok := err == nil
|
||||
|
||||
if ok != test.ok {
|
||||
t.Fatalf("Expected ok to be %v, got %v (%v)", test.ok, ok, err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !v.StrictEquals(vm.ToValue("passed")) {
|
||||
t.Fatalf("Unexpected result: %v", v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceLoader(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var m = require("m.js");
|
||||
m.test();
|
||||
`
|
||||
|
||||
const MODULE = `
|
||||
function test() {
|
||||
return "passed1";
|
||||
}
|
||||
|
||||
exports.test = test;
|
||||
`
|
||||
|
||||
vm := js.New()
|
||||
|
||||
registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) {
|
||||
if name == "m.js" {
|
||||
return []byte(MODULE), nil
|
||||
}
|
||||
return nil, errors.New("Module does not exist")
|
||||
}))
|
||||
registry.Enable(vm)
|
||||
|
||||
v, err := vm.RunString(SCRIPT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !v.StrictEquals(vm.ToValue("passed1")) {
|
||||
t.Fatalf("Unexpected result: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrictModule(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var m = require("m.js");
|
||||
m.test();
|
||||
`
|
||||
|
||||
const MODULE = `
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
var a = "passed1";
|
||||
eval("var a = 'not passed'");
|
||||
return a;
|
||||
}
|
||||
|
||||
exports.test = test;
|
||||
`
|
||||
|
||||
vm := js.New()
|
||||
|
||||
registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) {
|
||||
if name == "m.js" {
|
||||
return []byte(MODULE), nil
|
||||
}
|
||||
return nil, errors.New("Module does not exist")
|
||||
}))
|
||||
registry.Enable(vm)
|
||||
|
||||
v, err := vm.RunString(SCRIPT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !v.StrictEquals(vm.ToValue("passed1")) {
|
||||
t.Fatalf("Unexpected result: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
testRequire := func(src, fpath string, globalFolders []string, fs map[string]string) (*js.Runtime, js.Value, error) {
|
||||
vm := js.New()
|
||||
r := NewRegistry(WithGlobalFolders(globalFolders...), WithLoader(mapFileSystemSourceLoader(fs)))
|
||||
r.Enable(vm)
|
||||
t.Logf("Require(%s)", fpath)
|
||||
ret, err := vm.RunScript(path.Join(src, "test.js"), fmt.Sprintf("require('%s')", fpath))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return vm, ret, nil
|
||||
}
|
||||
|
||||
globalFolders := []string{
|
||||
"/usr/lib/node_modules",
|
||||
"/home/src/.node_modules",
|
||||
}
|
||||
|
||||
fs := map[string]string{
|
||||
"/home/src/app/app.js": `exports.name = "app"`,
|
||||
"/home/src/app2/app2.json": `{"name": "app2"}`,
|
||||
"/home/src/app3/index.js": `exports.name = "app3"`,
|
||||
"/home/src/app4/index.json": `{"name": "app4"}`,
|
||||
"/home/src/app5/package.json": `{"main": "app5.js"}`,
|
||||
"/home/src/app5/app5.js": `exports.name = "app5"`,
|
||||
"/home/src/app6/package.json": `{"main": "."}`,
|
||||
"/home/src/app6/index.js": `exports.name = "app6"`,
|
||||
"/home/src/app7/package.json": `{"main": "./a/b/c/file.js"}`,
|
||||
"/home/src/app7/a/b/c/file.js": `exports.name = "app7"`,
|
||||
"/usr/lib/node_modules/app8": `exports.name = "app8"`,
|
||||
"/home/src/app9/app9.js": `exports.name = require('./a/file.js').name`,
|
||||
"/home/src/app9/a/file.js": `exports.name = require('./b/file.js').name`,
|
||||
"/home/src/app9/a/b/file.js": `exports.name = require('./c/file.js').name`,
|
||||
"/home/src/app9/a/b/c/file.js": `exports.name = "app9"`,
|
||||
"/home/src/.node_modules/app10": `exports.name = "app10"`,
|
||||
"/home/src/app11/app11.js": `exports.name = require('d/file.js').name`,
|
||||
"/home/src/app11/a/b/c/app11.js": `exports.name = require('d/file.js').name`,
|
||||
"/home/src/app11/node_modules/d/file.js": `exports.name = "app11"`,
|
||||
"/app12.js": `exports.name = require('a/file.js').name`,
|
||||
"/node_modules/a/file.js": `exports.name = "app12"`,
|
||||
"/app13/app13.js": `exports.name = require('b/file.js').name`,
|
||||
"/node_modules/b/file.js": `exports.name = "app13"`,
|
||||
"node_modules/app14/index.js": `exports.name = "app14"`,
|
||||
"../node_modules/app15/index.js": `exports.name = "app15"`,
|
||||
}
|
||||
|
||||
for i, tc := range []struct {
|
||||
src string
|
||||
path string
|
||||
ok bool
|
||||
field string
|
||||
value string
|
||||
}{
|
||||
{"/home/src", "./app/app", true, "name", "app"},
|
||||
{"/home/src", "./app/app.js", true, "name", "app"},
|
||||
{"/home/src", "./app/bad.js", false, "", ""},
|
||||
{"/home/src", "./app2/app2", true, "name", "app2"},
|
||||
{"/home/src", "./app2/app2.json", true, "name", "app2"},
|
||||
{"/home/src", "./app/bad.json", false, "", ""},
|
||||
{"/home/src", "./app3", true, "name", "app3"},
|
||||
{"/home/src", "./appbad", false, "", ""},
|
||||
{"/home/src", "./app4", true, "name", "app4"},
|
||||
{"/home/src", "./appbad", false, "", ""},
|
||||
{"/home/src", "./app5", true, "name", "app5"},
|
||||
{"/home/src", "./app6", true, "name", "app6"},
|
||||
{"/home/src", "./app7", true, "name", "app7"},
|
||||
{"/home/src", "app8", true, "name", "app8"},
|
||||
{"/home/src", "./app9/app9", true, "name", "app9"},
|
||||
{"/home/src", "app10", true, "name", "app10"},
|
||||
{"/home/src", "./app11/app11.js", true, "name", "app11"},
|
||||
{"/home/src", "./app11/a/b/c/app11.js", true, "name", "app11"},
|
||||
{"/", "./app12", true, "name", "app12"},
|
||||
{"/", "./app13/app13", true, "name", "app13"},
|
||||
{".", "app14", true, "name", "app14"},
|
||||
{"..", "nonexistent", false, "", ""},
|
||||
} {
|
||||
vm, mod, err := testRequire(tc.src, tc.path, globalFolders, fs)
|
||||
if err != nil {
|
||||
if tc.ok {
|
||||
t.Errorf("%d: require() failed: %v", i, err)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
if !tc.ok {
|
||||
t.Errorf("%d: expected to fail, but did not", i)
|
||||
continue
|
||||
}
|
||||
}
|
||||
f := mod.ToObject(vm).Get(tc.field)
|
||||
if f == nil {
|
||||
t.Errorf("%v: field %q not found", i, tc.field)
|
||||
continue
|
||||
}
|
||||
value := f.String()
|
||||
if value != tc.value {
|
||||
t.Errorf("%v: got %q expected %q", i, value, tc.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireCycle(t *testing.T) {
|
||||
vm := js.New()
|
||||
r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{
|
||||
"a.js": `var b = require('./b.js'); exports.done = true;`,
|
||||
"b.js": `var a = require('./a.js'); exports.done = true;`,
|
||||
})))
|
||||
r.Enable(vm)
|
||||
res, err := vm.RunString(`
|
||||
var a = require('./a.js');
|
||||
var b = require('./b.js');
|
||||
a.done && b.done;
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v := res.Export(); v != true {
|
||||
t.Fatalf("Unexpected result: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorPropagation(t *testing.T) {
|
||||
vm := js.New()
|
||||
r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{
|
||||
"m.js": `throw 'test passed';`,
|
||||
})))
|
||||
rr := r.Enable(vm)
|
||||
_, err := rr.Require("./m")
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if ex, ok := err.(*js.Exception); ok {
|
||||
if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
|
||||
t.Fatalf("Unexpected Exception: %v", ex)
|
||||
}
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceMapLoader(t *testing.T) {
|
||||
vm := js.New()
|
||||
r := NewRegistry(WithLoader(func(p string) ([]byte, error) {
|
||||
switch filepath.ToSlash(p) {
|
||||
case "dir/m.js":
|
||||
return []byte(`throw 'test passed';
|
||||
//# sourceMappingURL=m.js.map`), nil
|
||||
case "dir/m.js.map":
|
||||
return []byte(`{"version":3,"file":"m.js","sourceRoot":"","sources":["m.ts"],"names":[],"mappings":";AAAA"}
|
||||
`), nil
|
||||
}
|
||||
return nil, ModuleFileDoesNotExistError
|
||||
}))
|
||||
|
||||
rr := r.Enable(vm)
|
||||
_, err := rr.Require("./dir/m")
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if ex, ok := err.(*js.Exception); ok {
|
||||
if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
|
||||
t.Fatalf("Unexpected Exception: %v", ex)
|
||||
}
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testsetup() (string, func(), error) {
|
||||
name, err := os.MkdirTemp("", "goja-nodejs-require-test")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return name, func() {
|
||||
os.RemoveAll(name)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestDefaultModuleLoader(t *testing.T) {
|
||||
workdir, teardown, err := testsetup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
err = os.Chdir(workdir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir("module", 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile("module/index.js", []byte(`throw 'test passed';`), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm := js.New()
|
||||
r := NewRegistry()
|
||||
rr := r.Enable(vm)
|
||||
_, err = rr.Require("./module")
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if ex, ok := err.(*js.Exception); ok {
|
||||
if !ex.Value().StrictEquals(vm.ToValue("test passed")) {
|
||||
t.Fatalf("Unexpected Exception: %v", ex)
|
||||
}
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPathResolver(t *testing.T) {
|
||||
workdir, teardown, err := testsetup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
err = os.Chdir(workdir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir("node_modules", 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir("node_modules/a", 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir("node_modules/a/node_modules", 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir("node_modules/b", 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Symlink("../../b", "node_modules/a/node_modules/b")
|
||||
if err != nil {
|
||||
if runtime.GOOS == "windows" && errors.Is(err, syscall.Errno(1314)) { // ERROR_PRIVILEGE_NOT_HELD
|
||||
t.Skip("Creating symlinks on Windows requires admin privileges")
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile("node_modules/b/index.js", []byte(``), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile("node_modules/a/index.js", []byte(`require('b')`), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm := js.New()
|
||||
r := NewRegistry()
|
||||
rr := r.Enable(vm)
|
||||
_, err = rr.Require("a")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue