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")) } }