Files
90CNServer/server/internal/modules/dnf/expertjob/owner_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

141 lines
6.1 KiB
Go

package expertjob
import (
"context"
"errors"
"testing"
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
dnfrepomemory "longheng.io/server/internal/modules/dnf/repository/memory"
)
func TestOwnerCompoundCommitsCharacterAndInventoryAtomically(t *testing.T) {
ctx := context.Background()
repositories := dnfrepomemory.NewMemoryGroup()
if err := repositories.Character.Save(ctx, dnfrepo.CharacterRecord{CharacterID: "19", AccountID: "dnf:1", Stats: map[string]int64{"expert_job_exp": 0}}); err != nil {
t.Fatal(err)
}
if err := repositories.Inventory.Save(ctx, dnfrepo.InventoryRecord{CharacterID: "19", Slots: map[string]dnfrepo.ItemStack{"0:9": {ItemID: 3001, Count: 2}}}); err != nil {
t.Fatal(err)
}
owner, err := NewOwner(repositories)
if err != nil {
t.Fatal(err)
}
err = owner.Compound(ctx, Command{AccountID: "dnf:1", CharacterID: "19", Project: func(assets *Assets) (Changes, error) {
assets.Character.Stats["expert_job_exp"] = 3
delete(assets.Inventory.Slots, "0:9")
return Changes{Character: true, Inventory: true}, nil
}})
if err != nil {
t.Fatal(err)
}
character, _, _ := repositories.Character.Load(ctx, "19")
inventory, _, _ := repositories.Inventory.Load(ctx, "19")
if character.Stats["expert_job_exp"] != 3 || len(inventory.Slots) != 0 {
t.Fatalf("character=%+v inventory=%+v", character, inventory)
}
}
func TestOwnerLearnRecipeRollsBackBothAggregates(t *testing.T) {
ctx := context.Background()
repositories := dnfrepomemory.NewMemoryGroup()
if err := repositories.Character.Save(ctx, dnfrepo.CharacterRecord{CharacterID: "19", AccountID: "dnf:1", Stats: map[string]int64{}}); err != nil {
t.Fatal(err)
}
if err := repositories.Inventory.Save(ctx, dnfrepo.InventoryRecord{CharacterID: "19", Slots: map[string]dnfrepo.ItemStack{"0:9": {ItemID: 1002, Count: 1}}}); err != nil {
t.Fatal(err)
}
owner, _ := NewOwner(repositories)
want := errors.New("late failure")
err := owner.LearnRecipe(ctx, Command{AccountID: "dnf:1", CharacterID: "19", Project: func(assets *Assets) (Changes, error) {
if assets.Character.Stats == nil {
assets.Character.Stats = make(map[string]int64)
}
assets.Character.Stats["expert_job_recipe_2_1002"] = 1
delete(assets.Inventory.Slots, "0:9")
return Changes{Character: true, Inventory: true}, want
}})
if !errors.Is(err, want) {
t.Fatalf("error = %v", err)
}
character, _, _ := repositories.Character.Load(ctx, "19")
inventory, _, _ := repositories.Inventory.Load(ctx, "19")
if character.Stats["expert_job_recipe_2_1002"] != 0 || inventory.Slots["0:9"].Count != 1 {
t.Fatalf("rollback character=%+v inventory=%+v", character, inventory)
}
}
func TestOwnerGiveUpCommitsCharacterWalletAndQuestAtomically(t *testing.T) {
ctx := context.Background()
repositories := dnfrepomemory.NewMemoryGroup()
if err := repositories.Character.Save(ctx, dnfrepo.CharacterRecord{CharacterID: "19", AccountID: "dnf:1", Stats: map[string]int64{"expert_job_type": 3, "expert_job_exp": 20}}); err != nil {
t.Fatal(err)
}
if err := repositories.Inventory.Save(ctx, dnfrepo.InventoryRecord{CharacterID: "19", Slots: map[string]dnfrepo.ItemStack{"0:0": {ItemID: 0, Count: 60_000}}}); err != nil {
t.Fatal(err)
}
if err := repositories.Quest.Save(ctx, dnfrepo.QuestRecord{CharacterID: "19", States: map[int64]dnfrepo.QuestState{2702: {Status: "completed"}, 5000: {Status: "active"}}, Progress: map[int64]dnfrepo.QuestState{2708: {Status: "active"}}}); err != nil {
t.Fatal(err)
}
owner, err := NewOwner(repositories)
if err != nil {
t.Fatal(err)
}
err = owner.GiveUp(ctx, Command{AccountID: "dnf:1", CharacterID: "19", Project: func(assets *Assets) (Changes, error) {
assets.Character.Stats["expert_job_type"] = 0
assets.Character.Stats["expert_job_exp"] = 0
wallet := assets.Inventory.Slots["0:0"]
wallet.Count = 59_000
assets.Inventory.Slots["0:0"] = wallet
delete(assets.Quest.States, 2702)
delete(assets.Quest.Progress, 2708)
return Changes{Character: true, Inventory: true, Quest: true}, nil
}})
if err != nil {
t.Fatal(err)
}
character, _, _ := repositories.Character.Load(ctx, "19")
inventory, _, _ := repositories.Inventory.Load(ctx, "19")
quest, _, _ := repositories.Quest.Load(ctx, "19")
if character.Stats["expert_job_type"] != 0 || inventory.Slots["0:0"].Count != 59_000 {
t.Fatalf("character=%+v inventory=%+v", character, inventory)
}
if _, exists := quest.States[2702]; exists {
t.Fatalf("transition state was not removed: %+v", quest)
}
if _, exists := quest.Progress[2708]; exists {
t.Fatalf("transition progress was not removed: %+v", quest)
}
if quest.States[5000].Status != "active" {
t.Fatalf("unrelated quest was changed: %+v", quest)
}
}
func TestOwnerGiveUpRollsBackQuestWithWallet(t *testing.T) {
ctx := context.Background()
repositories := dnfrepomemory.NewMemoryGroup()
_ = repositories.Character.Save(ctx, dnfrepo.CharacterRecord{CharacterID: "19", AccountID: "dnf:1", Stats: map[string]int64{"expert_job_type": 2}})
_ = repositories.Inventory.Save(ctx, dnfrepo.InventoryRecord{CharacterID: "19", Slots: map[string]dnfrepo.ItemStack{"0:0": {ItemID: 0, Count: 60_000}}})
_ = repositories.Quest.Save(ctx, dnfrepo.QuestRecord{CharacterID: "19", States: map[int64]dnfrepo.QuestState{2702: {Status: "completed"}}})
owner, _ := NewOwner(repositories)
want := errors.New("late give-up failure")
err := owner.GiveUp(ctx, Command{AccountID: "dnf:1", CharacterID: "19", Project: func(assets *Assets) (Changes, error) {
assets.Character.Stats["expert_job_type"] = 0
wallet := assets.Inventory.Slots["0:0"]
wallet.Count = 59_000
assets.Inventory.Slots["0:0"] = wallet
delete(assets.Quest.States, 2702)
return Changes{Character: true, Inventory: true, Quest: true}, want
}})
if !errors.Is(err, want) {
t.Fatalf("error=%v", err)
}
character, _, _ := repositories.Character.Load(ctx, "19")
inventory, _, _ := repositories.Inventory.Load(ctx, "19")
quest, _, _ := repositories.Quest.Load(ctx, "19")
if character.Stats["expert_job_type"] != 2 || inventory.Slots["0:0"].Count != 60_000 || quest.States[2702].Status != "completed" {
t.Fatalf("rollback character=%+v inventory=%+v quest=%+v", character, inventory, quest)
}
}