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

109 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package quest
import (
"testing"
dnfcerashop "longheng.io/server/internal/modules/dnf/cerashop"
)
// 任务奖励里的代币券 / 欢乐代币券处理(纯函数部分)。
//
// 券在道具 PVF 里是普通 `[material]`、**没有 `[action type]`**,客户端根本没有任何
// 途径把它们换成钱包余额;当普通道具发下去,玩家只会拿到一件永远用不掉的"材料"。
// 所以发放前先把它们从奖励规则里摘出来,改成入账号钱包。
func finishVoucherTestRule(itemID, count int64) RewardItemRule {
return RewardItemRule{ItemID: itemID, Count: count}
}
// 拆分必须只摘出券,并且**保持非券规则的原有顺序**——顺序变了会改变落格结果,
// 那是"修了 A 坏了 B"的典型来源。
func TestSplitFinishVoucherRulesKeepsOrdinaryRulesInOrder(t *testing.T) {
rules := []RewardItemRule{
finishVoucherTestRule(100050, 1),
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 100),
finishVoucherTestRule(100056, 3),
finishVoucherTestRule(dnfcerashop.HappyTokenCeraVoucherItemID, 7),
finishVoucherTestRule(100057, 2),
}
vouchers, items := splitFinishVoucherRules(rules)
if len(vouchers) != 2 || len(items) != 3 {
t.Fatalf("vouchers=%+v items=%+v", vouchers, items)
}
for index, want := range []int64{100050, 100056, 100057} {
if items[index].ItemID != want {
t.Fatalf("非券规则顺序被改变: items[%d]=%d want=%d", index, items[index].ItemID, want)
}
}
if vouchers[0].ItemID != dnfcerashop.TokenCeraVoucherItemID ||
vouchers[1].ItemID != dnfcerashop.HappyTokenCeraVoucherItemID {
t.Fatalf("券的顺序被改变: %+v", vouchers)
}
}
// 没有券时必须原样返回(不得产生任何副作用)。
func TestSplitFinishVoucherRulesLeavesVoucherFreeRewardsUntouched(t *testing.T) {
if vouchers, items := splitFinishVoucherRules([]RewardItemRule{finishVoucherTestRule(1, 1)}); len(vouchers) != 0 || len(items) != 1 {
t.Fatalf("无券时不该有变化: vouchers=%+v items=%+v", vouchers, items)
}
if vouchers, items := splitFinishVoucherRules(nil); len(vouchers) != 0 || len(items) != 0 {
t.Fatalf("空输入: vouchers=%+v items=%+v", vouchers, items)
}
}
func TestFinishVoucherCountsSumsPerKind(t *testing.T) {
counts, err := finishVoucherCounts([]RewardItemRule{
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 100),
finishVoucherTestRule(dnfcerashop.HappyTokenCeraVoucherItemID, 7),
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 5),
})
if err != nil {
t.Fatal(err)
}
if counts.TokenCera != 105 || counts.HappyTokenCera != 7 {
t.Fatalf("counts=%+v want token=105 happy=7", counts)
}
}
// 坏值必须被挡住:券既然不走落格路径,就没有别的东西替我们校验这些字段了
// (allocateFinishRewardItems 的那套校验对券不再执行)。
func TestFinishVoucherCountsRejectsMalformedRules(t *testing.T) {
cases := map[string]RewardItemRule{
"zero_count": finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 0),
"negative": finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, -1),
"non_voucher": finishVoucherTestRule(100050, 1),
"zero_item": finishVoucherTestRule(0, 1),
"oversized": finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 1<<33),
}
for name, rule := range cases {
t.Run(name, func(t *testing.T) {
if _, err := finishVoucherCounts([]RewardItemRule{rule}); err == nil {
t.Fatalf("坏值必须报错: %+v", rule)
}
})
}
}
// 汇总后超过客户端可读范围(i32)时必须报错,而不是让它截断成负数。
func TestFinishVoucherCountsRejectsPerKindOverflow(t *testing.T) {
if _, err := finishVoucherCounts([]RewardItemRule{
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 1<<31-1),
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 1),
}); err == nil {
t.Fatal("代币券汇总溢出必须报错")
}
if _, err := finishVoucherCounts([]RewardItemRule{
finishVoucherTestRule(dnfcerashop.HappyTokenCeraVoucherItemID, 1<<31-1),
finishVoucherTestRule(dnfcerashop.HappyTokenCeraVoucherItemID, 1),
}); err == nil {
t.Fatal("欢乐代币券汇总溢出必须报错")
}
// 刚好到上限是允许的。
counts, err := finishVoucherCounts([]RewardItemRule{
finishVoucherTestRule(dnfcerashop.TokenCeraVoucherItemID, 1<<31-1),
})
if err != nil || counts.TokenCera != 1<<31-1 {
t.Fatalf("刚好到上限应当允许: err=%v counts=%+v", err, counts)
}
}