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

96 lines
3.7 KiB
Go

package statbuff
import (
"testing"
"time"
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
)
// 强化/增幅成功率窗口:与爆率窗口同一套 fail-closed 语义,但**按种类分开存**
// (强化与增幅是两件不同的道具,各自生效、互不覆盖)。
func TestRateWindowsAreKindScopedAndFailClosed(t *testing.T) {
now := time.Date(2026, 10, 3, 14, 0, 0, 0, time.UTC)
character := dnfrepo.CharacterRecord{CharacterID: "11"}
if _, ok := ActiveRate(character, KindUpgradeSuccess, now); ok {
t.Fatal("an empty character must not report an active upgrade window")
}
if _, ok := ActiveRate(character, Kind("unknown"), now); ok {
t.Fatal("an unknown kind must not report an active window")
}
upgrade, err := ActivateRate(&character, KindUpgradeSuccess, 10008220, 1099, 30, 10*time.Minute, now)
if err != nil {
t.Fatal(err)
}
amplify, err := ActivateRate(&character, KindAmplifySuccess, 490001534, 0, 10, 30*time.Minute, now)
if err != nil {
t.Fatal(err)
}
if upgrade.Percent != 30 || upgrade.BuffID != 1099 || !upgrade.ExpireAt.Equal(now.Add(10*time.Minute)) {
t.Fatalf("upgrade window=%+v", upgrade)
}
if amplify.Percent != 10 || amplify.BuffID != 0 || !amplify.ExpireAt.Equal(now.Add(30*time.Minute)) {
t.Fatalf("amplify window=%+v", amplify)
}
// 两族互不覆盖:读强化拿到的必须是 30/1099,读增幅拿到的是 10/0。
gotUpgrade, ok := ActiveRate(character, KindUpgradeSuccess, now.Add(time.Minute))
if !ok || gotUpgrade.Percent != 30 || gotUpgrade.BuffID != 1099 || gotUpgrade.ItemID != 10008220 {
t.Fatalf("upgrade read=%+v ok=%t", gotUpgrade, ok)
}
gotAmplify, ok := ActiveRate(character, KindAmplifySuccess, now.Add(time.Minute))
if !ok || gotAmplify.Percent != 10 || gotAmplify.ItemID != 490001534 {
t.Fatalf("amplify read=%+v ok=%t", gotAmplify, ok)
}
// 过期即视为没有(fail-closed),另一种类不受影响。
if _, ok := ActiveRate(character, KindUpgradeSuccess, now.Add(11*time.Minute)); ok {
t.Fatal("an expired upgrade window must not stay active")
}
if _, ok := ActiveRate(character, KindAmplifySuccess, now.Add(11*time.Minute)); !ok {
t.Fatal("the amplify window must survive the upgrade window expiring")
}
// 字段缺失/非法同样按"没有增益"处理,绝不 fail-open。
broken := dnfrepo.CharacterRecord{CharacterID: "11", Stats: map[string]int64{
"upgrade_success_rate_buff_percent": 10, // 没有到期时间
}}
if _, ok := ActiveRate(broken, KindUpgradeSuccess, now); ok {
t.Fatal("a window without an expiry must not be active")
}
negative := dnfrepo.CharacterRecord{CharacterID: "11", Stats: map[string]int64{
"amplify_success_rate_buff_percent": -5,
"amplify_success_rate_buff_expire_at": now.Add(time.Hour).Unix(),
}}
if _, ok := ActiveRate(negative, KindAmplifySuccess, now); ok {
t.Fatal("a non-positive bonus must not be active")
}
if _, err := ActivateRate(&character, KindUpgradeSuccess, 0, 0, 10, time.Minute, now); err == nil {
t.Fatal("activating with a zero item id must fail")
}
if _, err := ActivateRate(&character, KindUpgradeSuccess, 1, 0, 10, 0, now); err == nil {
t.Fatal("activating with a zero duration must fail")
}
}
func TestKindForUpgradeMode(t *testing.T) {
for _, tc := range []struct {
mode string
want Kind
ok bool
}{
{mode: "reinforce", want: KindUpgradeSuccess, ok: true},
{mode: "amplify", want: KindAmplifySuccess, ok: true},
{mode: " Reinforce ", want: KindUpgradeSuccess, ok: true},
{mode: "", ok: false},
{mode: "normal", ok: false},
} {
got, ok := KindForUpgradeMode(tc.mode)
if ok != tc.ok || got != tc.want {
t.Fatalf("KindForUpgradeMode(%q)=%q,%t want %q,%t", tc.mode, got, ok, tc.want, tc.ok)
}
}
}