Files
90CNServer/pvf/services/vending_machine_test.go
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

120 lines
4.1 KiB
Go

package services
import (
"strings"
"testing"
"unicode/utf16"
"pvfine/internal/pvf"
)
const vendingTestText = "# 😀 [/item group]\r\n[name]\r\n`抽奖机 [/output]`\r\n[item group]\r\n[group num]\r\n1\r\n[material]\r\n2 1\r\n[output]\r\n1 3000 2 0\r\n1 20 4 1\r\n[/output]\r\n[unknown]\r\n`保留`\r\n[/item group]\r\n"
func TestVendingMachineGroupsAndSource(t *testing.T) {
text := vendingTestText + "[item group][group num]2[material]3 2[output][/output][/item group]"
doc, err := parseVendingMachine(text)
if err != nil {
t.Fatal(err)
}
if len(doc.Groups) != 2 || len(doc.Groups[0].Rewards) != 2 || len(doc.Groups[1].Rewards) != 0 {
t.Fatalf("groups: %+v", doc.Groups)
}
units := utf16.Encode([]rune(strings.ReplaceAll(text, "\r\n", "\n")))
g := doc.Groups[0]
if string(utf16.Decode(units[g.Start:g.End])) != "[item group]\n[group num]\n1\n[material]\n2 1\n[output]\n1 3000 2 0\n1 20 4 1\n[/output]\n[unknown]\n`保留`\n[/item group]" {
t.Fatal("incorrect group span")
}
if string(utf16.Decode(units[g.OutputClose:g.OutputClose+9])) != "[/output]" {
t.Fatal("incorrect insertion offset")
}
for _, row := range g.Rewards {
for _, f := range row.Fields {
if string(utf16.Decode(units[f.Start:f.End])) == "" {
t.Fatal("empty numeric span")
}
}
}
if g.Rewards[1].Fields[2].Value != 4 || g.Rewards[1].Fields[3].Value != 1 {
t.Fatal("reward columns")
}
}
func TestVendingMachineRejectsInvalidStructure(t *testing.T) {
good := "[item group][group num]1[material]2 1[output]1 10 1 0[/output][/item group]"
cases := []string{
strings.ReplaceAll(good, "[/item group]", ""),
strings.ReplaceAll(good, "[/output]", ""),
strings.ReplaceAll(good, "1 10 1 0", "1 10 1"),
strings.ReplaceAll(good, "1 10 1 0", "1 10 1 2"),
strings.ReplaceAll(good, "1 10 1 0", "1 -1 1 0"),
strings.ReplaceAll(good, "1 10 1 0", "1 2147483648 1 0"),
strings.ReplaceAll(good, "1 10 1 0", "1 1.0 1 0"),
strings.ReplaceAll(good, "[material]2 1", "[material]2 1[material]2 1"),
strings.ReplaceAll(good, "[material]2 1", "[material]2"),
strings.ReplaceAll(good, "[group num]1", ""),
good + good,
"[material]2 1",
"[item group]" + good + "[/item group]",
}
for _, text := range cases {
if _, err := parseVendingMachine(text); err == nil {
t.Errorf("accepted invalid: %s", text)
}
}
if doc, err := parseVendingMachine("# empty\n[name]`新抽奖机`"); err != nil || len(doc.Groups) != 0 {
t.Fatalf("empty: %+v %v", doc, err)
}
}
func TestVendingMachineListAndRead(t *testing.T) {
c, _ := shopFixture(t)
a := c.archive
i, err := a.AddFileText("etc/vending/a.vm", vendingTestText, pvf.TypeScript)
if err != nil {
t.Fatal(err)
}
if _, err = a.AddFileText("etc/vendingmachine.lst", "10 `vending/a.vm` 11 `etc/vending/a.vm` 12 `missing.vm` 13 `../stackable/one.stk`", pvf.TypeScript); err != nil {
t.Fatal(err)
}
service := NewFileGUIService(c)
before := a.ModifiedCount()
entries, err := service.ListVendingMachines()
if err != nil {
t.Fatal(err)
}
if len(entries) != 2 || entries[0].ID != "10" || entries[1].ID != "11" || entries[0].FileIndex != i {
t.Fatalf("entries: %+v", entries)
}
doc, err := service.ReadVendingMachine(i, vendingTestText)
if err != nil {
t.Fatal(err)
}
if doc.Groups[0].MaterialName != "材料甲" || doc.Groups[0].Rewards[0].Name != "长名称药剂" {
t.Fatalf("names: %+v", doc.Groups[0])
}
if a.ModifiedCount() != before {
t.Fatal("read mutated archive")
}
other, _ := a.Find("stackable/one.stk")
if _, err := service.ReadVendingMachine(other, vendingTestText); err == nil {
t.Fatal("accepted non vm")
}
if _, err := service.ReadVendingMachine(-1, vendingTestText); err == nil {
t.Fatal("accepted bad index")
}
}
func TestVendingMachineNoArchiveOrList(t *testing.T) {
service := NewFileGUIService(NewCore())
if _, err := service.ListVendingMachines(); err == nil {
t.Fatal("accepted no archive")
}
if _, err := service.ReadVendingMachine(0, ""); err == nil {
t.Fatal("accepted no archive")
}
service.c.archive = pvf.New()
if _, err := service.ListVendingMachines(); err == nil {
t.Fatal("accepted missing list")
}
}