Files
90CNServer/server/internal/modules/dnf/companion/lifecycle_retirement_test.go
T
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

115 lines
3.6 KiB
Go

package companion
import "testing"
func retirementInstance(t *testing.T, withActor bool) (*Instance, RunKey) {
t.Helper()
key := runKey()
i, err := NewInstance(key, prepared(t), 1, 1)
if err != nil {
t.Fatal(err)
}
if err := i.Apply(Event{Run: key, Sequence: 1, Kind: "room_ready", RoomGeneration: 1}, nil); err != nil {
t.Fatal(err)
}
if withActor {
for n, kind := range []string{"native_created", "owner_bound", "activate"} {
if err := i.Apply(Event{Run: key, Sequence: uint64(n + 2), Kind: kind, RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}, map[uint16]bool{}); err != nil {
t.Fatal(err)
}
}
}
seq := i.Status().LastSequence + 1
if err := i.Apply(Event{Run: key, Sequence: seq, Kind: "retire", RoomGeneration: 1}, nil); err != nil {
t.Fatal(err)
}
return i, key
}
func TestCompanionDisposedRequiresExactActorAndRoom(t *testing.T) {
i, key := retirementInstance(t, true)
before := i.Status()
valid := Event{Run: key, Sequence: before.LastSequence + 1, Kind: "disposed", RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}
for _, field := range []string{"key", "generation", "room", "empty"} {
e := valid
switch field {
case "key":
e.ObjectKey++
case "generation":
e.ObjectGeneration++
case "room":
e.RoomGeneration++
case "empty":
e.ObjectKey = 0
e.ObjectGeneration = 0
}
if err := i.Apply(e, nil); err == nil {
t.Fatalf("accepted mismatched %s disposal", field)
}
if i.Status() != before || !i.Owns(key, 24, 7) {
t.Fatalf("invalid disposal revoked owner: %s", field)
}
}
if err := i.Apply(valid, nil); err != nil {
t.Fatal(err)
}
if i.Owns(key, 24, 7) || i.Status().Phase != Disposed {
t.Fatal("valid disposal did not close")
}
}
func TestCompanionLateCreationTracksCleanupWithoutAuthorization(t *testing.T) {
i, key := retirementInstance(t, false)
e := Event{Run: key, Sequence: 3, Kind: "native_created", RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}
if err := i.Apply(e, map[uint16]bool{}); err != nil {
t.Fatal("late creation lost:", err)
}
if i.Status().Phase != Retiring || i.Owns(key, 24, 7) {
t.Fatal("late creation resurrected cancelled run")
}
if err := i.Apply(e, map[uint16]bool{}); err != nil {
t.Fatal("exact duplicate should be idempotent", err)
}
if err := i.Apply(Event{Run: key, Sequence: 4, Kind: "owner_bound", RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}, nil); err == nil {
t.Fatal("late actor authorized")
}
if err := i.Apply(Event{Run: key, Sequence: 4, Kind: "disposed", RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}, nil); err != nil {
t.Fatal(err)
}
if i.Status().Phase != Disposed {
t.Fatal(i.Status())
}
}
func TestCompanionLateCreationCannotReplaceExistingActor(t *testing.T) {
i, key := retirementInstance(t, true)
before := i.Status()
e := Event{Run: key, Sequence: before.LastSequence + 1, Kind: "native_created", RoomGeneration: 1, ObjectKey: 25, ObjectGeneration: 8}
if err := i.Apply(e, map[uint16]bool{}); err == nil {
t.Fatal("replaced retiring actor")
}
if i.Status() != before {
t.Fatal("rejected late creation changed state")
}
}
func TestCompanionCancellationWithoutActorRequiresEmptyDisposal(t *testing.T) {
i, key := retirementInstance(t, false)
before := i.Status()
e := Event{Run: key, Sequence: 3, Kind: "disposed", RoomGeneration: 1, ObjectKey: 24, ObjectGeneration: 7}
if err := i.Apply(e, nil); err == nil {
t.Fatal("undisclosed actor disposal accepted")
}
if i.Status() != before {
t.Fatal("bad disposal changed state")
}
e.ObjectKey = 0
e.ObjectGeneration = 0
if err := i.Apply(e, nil); err != nil {
t.Fatal(err)
}
if i.Status().Phase != Disposed {
t.Fatal(i.Status())
}
}