Files
ngzz-mc/internal/mods/mods_test.go

139 lines
3.9 KiB
Go
Raw Normal View History

package mods
import (
"os"
"path/filepath"
"testing"
"time"
)
// TestLuaRegister 脚本注册方块/物品/配方与事件(Mod开发指南.md §4)。
func TestLuaRegister(t *testing.T) {
reg := NewRegistry()
m := NewLuaMod("test_mod", reg)
script := `
api.register_block{ id = "ruby_ore", hardness = 3.0, light = 0 }
api.register_item{ id = "ruby", stack = 64 }
api.register_recipe{ type = "crafting_shaped", pattern = {"RRR","R R","RRR"}, result = {item = "ruby_block"} }
`
if err := m.Load(script); err != nil {
t.Fatalf("加载失败: %v", err)
}
entries := reg.Entries()
if len(entries) != 3 {
t.Fatalf("注册条目期望 3,实际 %d", len(entries))
}
if reg.Gen("test_mod") != 1 {
t.Fatalf("代数期望 1,实际 %d", reg.Gen("test_mod"))
}
// 方块数据完整性
found := false
for _, e := range entries {
if e.Key == "block:ruby_ore" {
found = true
if d, ok := e.Data.(map[string]any); !ok || d["hardness"] != 3.0 {
t.Fatalf("方块数据异常: %+v", e.Data)
}
}
}
if !found {
t.Fatal("缺少 block:ruby_ore 注册")
}
}
// TestHotReload 热重载:新脚本替换旧注册(generation 机制,热更新.md §4.1)。
func TestHotReload(t *testing.T) {
reg := NewRegistry()
m := NewLuaMod("hot_mod", reg)
if err := m.Load(`api.register_block{ id = "old_block" }`); err != nil {
t.Fatalf("首次加载失败: %v", err)
}
// 热重载:注册新方块
if err := m.Load(`api.register_block{ id = "new_block" }`); err != nil {
t.Fatalf("重载失败: %v", err)
}
if reg.Gen("hot_mod") != 2 {
t.Fatalf("重载后代数期望 2,实际 %d", reg.Gen("hot_mod"))
}
for _, e := range reg.Entries() {
if e.Key == "block:old_block" {
t.Fatal("旧代注册未回收")
}
}
}
// TestReloadRollback 坏脚本回滚:保留旧版本(热更新.md §4.2)。
func TestReloadRollback(t *testing.T) {
reg := NewRegistry()
m := NewLuaMod("rollback_mod", reg)
if err := m.Load(`api.register_block{ id = "good_block" }`); err != nil {
t.Fatalf("首次加载失败: %v", err)
}
// 语法错误脚本:重载失败,旧版保留
if err := m.Load(`api.register_block{ id = `); err == nil {
t.Fatal("坏脚本应报错")
}
if reg.Gen("rollback_mod") != 1 {
t.Fatalf("回滚后代数应保持 1,实际 %d", reg.Gen("rollback_mod"))
}
found := false
for _, e := range reg.Entries() {
if e.Key == "block:good_block" {
found = true
}
}
if !found {
t.Fatal("回滚后旧注册丢失")
}
}
// TestSandbox 沙箱:os/io 库不可用(Mod开发指南.md §6)。
func TestSandbox(t *testing.T) {
reg := NewRegistry()
m := NewLuaMod("sandbox_mod", reg)
if err := m.Load(`os.exit(0)`); err == nil {
t.Fatal("沙箱应禁止 os 库")
}
}
// TestWatcher 文件监听热重载(热更新.md §4.5 防抖)。
func TestWatcher(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "init.lua")
if err := os.WriteFile(path, []byte(`api.register_block{ id = "v1" }`), 0o644); err != nil {
t.Fatalf("写脚本失败: %v", err)
}
reg := NewRegistry()
m := NewLuaMod("watched_mod", reg)
reload := func() error {
b, err := os.ReadFile(path)
if err != nil {
return err
}
return m.Load(string(b))
}
w, err := NewWatcher()
if err != nil {
t.Fatalf("创建监听器失败: %v", err)
}
defer w.Close()
if err := w.WatchFile(path, reload); err != nil {
t.Fatalf("监听失败: %v", err)
}
go w.Run()
if err := reload(); err != nil {
t.Fatalf("初始加载失败: %v", err)
}
// 修改文件 → 防抖后自动重载
if err := os.WriteFile(path, []byte(`api.register_block{ id = "v2" }`), 0o644); err != nil {
t.Fatalf("写脚本失败: %v", err)
}
deadline := time.Now().Add(5 * time.Second)
for reg.Gen("watched_mod") != 2 && time.Now().Before(deadline) {
time.Sleep(50 * time.Millisecond)
}
if reg.Gen("watched_mod") != 2 {
t.Fatalf("文件监听未触发重载(代数 %d)", reg.Gen("watched_mod"))
}
}