服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
106 lines
3.0 KiB
Go
106 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
func TestSelectDatabaseModePreservesInstallationAndClient(t *testing.T) {
|
|
root := t.TempDir()
|
|
paths := newProjectPaths(root)
|
|
if err := paths.ensureDirectories(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg := validTestInstance()
|
|
cfg.Database = databaseConfig{
|
|
Mode: databaseModeSQLite,
|
|
Path: "data/dnf90.db",
|
|
Name: "dnf_local",
|
|
}
|
|
cfg.Client.Directory = `D:\Games\DNF`
|
|
data, err := marshalInstance(cfg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := writeFile(paths.instance, data, 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var output bytes.Buffer
|
|
controller := newController(paths, &output, &output)
|
|
if err := controller.selectDatabaseMode(context.Background(), "mysql", false); err != nil {
|
|
t.Fatalf("select MySQL: %v", err)
|
|
}
|
|
selected, err := decodeInstance(paths.instance)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.Database.Mode != databaseModePortable ||
|
|
selected.Database.Host != "127.0.0.1" ||
|
|
selected.Database.Port != 13306 ||
|
|
selected.Database.User != "root" ||
|
|
selected.Database.Password == "" {
|
|
t.Fatalf("portable database profile = %+v", selected.Database)
|
|
}
|
|
if selected.InstallationID != cfg.InstallationID ||
|
|
selected.Server.AccountID != cfg.Server.AccountID ||
|
|
selected.Client.Directory != cfg.Client.Directory {
|
|
t.Fatalf("mode switch changed installation identity: %+v", selected)
|
|
}
|
|
|
|
password := selected.Database.Password
|
|
if err := controller.selectDatabaseMode(context.Background(), "sqlite", false); err != nil {
|
|
t.Fatalf("select SQLite: %v", err)
|
|
}
|
|
selected, err = decodeInstance(paths.instance)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.Database.Mode != databaseModeSQLite || selected.Database.Path != "data/dnf90.db" {
|
|
t.Fatalf("SQLite database profile = %+v", selected.Database)
|
|
}
|
|
if selected.Database.Password != password {
|
|
t.Fatal("portable MySQL credential was not retained for switching back")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeDatabaseModeAcceptsPublishedNames(t *testing.T) {
|
|
for input, want := range map[string]string{
|
|
"SQLite": databaseModeSQLite,
|
|
"SQLiet": databaseModeSQLite,
|
|
"MySQL": databaseModePortable,
|
|
} {
|
|
got, err := normalizeDatabaseMode(input)
|
|
if err != nil || got != want {
|
|
t.Fatalf("normalizeDatabaseMode(%q) = %q, %v", input, got, err)
|
|
}
|
|
}
|
|
if _, err := normalizeDatabaseMode("unknown"); err == nil {
|
|
t.Fatal("unknown database mode was accepted")
|
|
}
|
|
}
|
|
|
|
func TestSelectDatabaseModeRejectsUnknownModeWithoutWriting(t *testing.T) {
|
|
root := t.TempDir()
|
|
paths := newProjectPaths(root)
|
|
if err := paths.ensureDirectories(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(paths.instance, []byte("sentinel"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
controller := newController(paths, &bytes.Buffer{}, &bytes.Buffer{})
|
|
if err := controller.selectDatabaseMode(context.Background(), "unknown", false); err == nil {
|
|
t.Fatal("unknown database mode was accepted")
|
|
}
|
|
data, err := os.ReadFile(paths.instance)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(data) != "sentinel" {
|
|
t.Fatalf("invalid selection changed instance.json: %q", data)
|
|
}
|
|
}
|