package gameserver import ( "bytes" "crypto/sha256" "fmt" "os" "path/filepath" "strings" "testing" goproto "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/proto" ) func TestBattleResultCaptureRetainsObfuscatedAndUnknownStatistics(t *testing.T) { t.Chdir(t.TempDir()) req := &goproto.PVEBattleResultCsReq{Stt: &goproto.BattleStatistics{NPFDEJPPPLI: 7, IEOGEFBLNEL: 3}} unknown := protowire.AppendTag(nil, 100, protowire.VarintType) unknown = protowire.AppendVarint(unknown, 9) req.Stt.ProtoReflect().SetUnknown(unknown) if err := saveBattleResultCapture(req); err != nil { t.Fatal(err) } files, err := filepath.Glob("battle_diagnostics/*/battle_result.bin") if err != nil || len(files) != 1 { t.Fatalf("capture files=%v err=%v", files, err) } wire, err := os.ReadFile(files[0]) if err != nil { t.Fatal(err) } var got goproto.PVEBattleResultCsReq if err := proto.Unmarshal(wire, &got); err != nil { t.Fatal(err) } if !proto.Equal(req, &got) { t.Fatal("capture lost result statistics") } } func TestCaptureUsesExactResponseBattleBytes(t *testing.T) { t.Chdir(t.TempDir()) // Fixture values exercise zero-step affixes and map ordering on the wire. battle := &goproto.SceneBattleInfo{BattleAvatarList: []*goproto.BattleAvatar{{Id: 1204, RelicList: []*goproto.BattleRelic{{Id: 61151, SubAffixList: []*goproto.RelicAffix{{AffixId: 7, Cnt: 3, Step: 0}}}}}}, BuffList: []*goproto.BattleBuff{{DynamicValues: map[string]float32{"first": 1, "second": 2}}}} rsp := &goproto.StartQuickCocoonStageRsp{BattleInfo: battle} body, err := proto.Marshal(rsp) if err != nil { t.Fatal(err) } wire, meta, err := saveBattleCapture(rsp, 1371, body) if err != nil { t.Fatal(err) } if meta.ResponseSHA256 != fmt.Sprintf("%x", sha256.Sum256(body)) { t.Fatal("response checksum mismatch") } var decoded goproto.SceneBattleInfo if err := proto.Unmarshal(wire, &decoded); err != nil { t.Fatal(err) } if !proto.Equal(battle, &decoded) { t.Fatal("captured battle differs from outgoing battle") } written, err := os.ReadFile("battle_last_response.bin") if err != nil || !bytes.Equal(written, body) { t.Fatalf("response not preserved: %v", err) } written, err = os.ReadFile("battle_last.bin") if err != nil || !bytes.Equal(written, wire) { t.Fatalf("battle wire not preserved: %v", err) } probe := string(makeBattleProbe(wire, "FixtureBattleType", meta.BattleSHA256)) if strings.Contains(probe, "__TYPE_NAME__") || !strings.Contains(probe, meta.BattleSHA256) { t.Fatal("probe placeholders not populated") } } func TestProbeDoesNotRunWithoutBattle(t *testing.T) { t.Chdir(t.TempDir()) _, meta, err := saveBattleCapture(&goproto.GetCurBattleInfoScRsp{}, 171, nil) if err != nil || meta != nil { t.Fatalf("empty login response captured: %v", err) } if _, err := os.Stat("battle_last.bin"); !os.IsNotExist(err) { t.Fatal("empty response wrote a capture") } s := &Session{} s.battleProbe = []byte("fixture") if string(s.takeBattleProbe()) != "fixture" || len(s.takeBattleProbe()) != 0 { t.Fatal("probe was not consumed exactly once") } } func TestBattleWireRejectsTruncatedResponse(t *testing.T) { if _, err := extractBattleWire(&goproto.StartQuickCocoonStageRsp{}, []byte{0x2a, 0x7f}); err == nil { t.Fatal("truncated payload accepted") } }