package feature import ( "errors" "testing" "bd2server/internal/server/wire" ) func TestHandleAuditedEmptyResponses(t *testing.T) { for path, wantCode := range EmptyPacketCodes() { t.Run(path, func(t *testing.T) { gotCode, gotProto, ok, err := Handle(path, wire.AppendVarint(nil, 1, 42)) if err != nil || !ok || gotCode != wantCode || len(gotProto) != 0 { t.Fatalf("Handle(%q): code=%d proto=%x ok=%t err=%v; want code=%d, empty, handled", path, gotCode, gotProto, ok, err, wantCode) } }) } } func TestHandleRejectsUnknownAndInvalidRequests(t *testing.T) { for _, path := range []string{"/EventScheduleInfo", "/LoginEvent", "/ChargeCostInfo", "/EventMissionInfo", "/EventRewardHistory", "/PackEventStoryInfo", "/PackEventBattleInfo", "/Attendance", "/AttendanceInfo", "/FireWorksInfo", "/FriendshipSpecialEpisodeInfo", "/MiniEventHubInfo", "/PrestigeSkinInfo"} { if _, _, ok, err := Handle(path, wire.AppendVarint(nil, 1, 1)); ok || err != nil { t.Fatalf("stateful event route %s has stateless fallback", path) } } for _, path := range []string{"/HuntDispatchInfo", "/HuntingGroundInfo", "/HuntingGroundInfoList", "/HuntingGroundEnter", "/UserLevelReward", "/FieldObjectInfo", "/WaypointInfo", "/WaypointSave", "/WaypointUse", "/AchievementInfo", "/AchievementUpdate", "/RecipeInfo", "/PackInfo", "/DeckInfo", "/RootSortIdInfo"} { if _, _, handled, err := Handle(path, wire.AppendVarint(nil, 1, 1)); handled || err != nil { t.Fatalf("stateful route %s still has a stateless success fallback", path) } } if code, proto, ok, err := Handle("/not-a-real-endpoint", wire.AppendVarint(nil, 1, 1)); code != 0 || proto != nil || ok || err != nil { t.Fatalf("unknown route was not fail-closed: code=%d proto=%x ok=%t err=%v", code, proto, ok, err) } if _, _, ok, err := Handle("/CharAwakeInfo", wire.AppendVarint(nil, 1, 1)); ok || err != nil { t.Fatalf("stateful CharAwakeInfo must not be handled by feature defaults: ok=%v err=%v", ok, err) } for _, path := range []string{"/PackPreviewInfo", "/PackJamEvent", "/PresetInfo", "/DeckCostumeSettingInfo", "/FriendshipInfo", "/MasterTitleInfo", "/MasterTitleInfoUpdate"} { if _, _, ok, err := Handle(path, wire.AppendVarint(nil, 1, 1)); ok || err != nil { t.Fatalf("stateful %s must not be handled by feature defaults: ok=%v err=%v", path, ok, err) } } for name, request := range map[string][]byte{ "empty": nil, "no-sequence": wire.AppendVarint(nil, 2, 1), "zero-sequence": wire.AppendVarint(nil, 1, 0), "malformed": {0x08, 0x80}, } { t.Run(name, func(t *testing.T) { _, proto, ok, err := Handle("/QuestMaxClearInfo", request) if !ok || proto != nil || !errors.Is(err, ErrInvalidRequest) { t.Fatalf("invalid request: proto=%x ok=%t err=%v", proto, ok, err) } }) } } func TestEmptyPacketCodesReturnsCopy(t *testing.T) { codes := EmptyPacketCodes() codes["/QuestMaxClearInfo"] = -1 if code, _, ok, err := Handle("/QuestMaxClearInfo", wire.AppendVarint(nil, 1, 1)); err != nil || !ok || code != 137 { t.Fatalf("registry escaped its copy: code=%d ok=%t err=%v", code, ok, err) } } func TestStandaloneNativeDefaults(t *testing.T) { for path, want := range map[string]int{ "/EquipInfo": 34, "/TodayQuestInfo": 64, "/UpdateAgeGate": 0, "/ActiveMap": 0, "/PvpBattleDeckInfo": 87, "/PvpBattleHistory": 97, "/PvpBattleOnceRewardInfo": 277, } { code, proto, ok, err := Handle(path, wire.AppendVarint(nil, 1, 55)) if err != nil || !ok || code != want || len(proto) != 0 { t.Fatalf("%s: code=%d proto=%x ok=%v err=%v", path, code, proto, ok, err) } } }