flatbuffers & transcoders

This commit is contained in:
Ilia Denisov
2026-03-31 19:16:34 +02:00
committed by GitHub
parent 6e01d73a5e
commit f616e3f5ca
75 changed files with 10540 additions and 0 deletions
+275
View File
@@ -0,0 +1,275 @@
package transcoder
import (
"bytes"
"reflect"
"strconv"
"strings"
"testing"
model "galaxy/model/report"
fbs "galaxy/schema/fbs/battle"
flatbuffers "github.com/google/flatbuffers/go"
"github.com/google/uuid"
)
func TestBattleReportToPayloadAndPayloadToBattleReportRoundTrip(t *testing.T) {
t.Parallel()
source := sampleBattleReport()
payload, err := BattleReportToPayload(source)
if err != nil {
t.Fatalf("encode battle report payload: %v", err)
}
decoded, err := PayloadToBattleReport(payload)
if err != nil {
t.Fatalf("decode battle report payload: %v", err)
}
expected := battleReportWireClone(t, source)
if !reflect.DeepEqual(expected, decoded) {
t.Fatalf("round-trip mismatch\nexpected: %#v\ndecoded: %#v", expected, decoded)
}
}
func TestBattleReportToPayloadNilReport(t *testing.T) {
t.Parallel()
_, err := BattleReportToPayload(nil)
if err == nil {
t.Fatal("expected error for nil battle report")
}
}
func TestPayloadToBattleReportEmptyData(t *testing.T) {
t.Parallel()
_, err := PayloadToBattleReport(nil)
if err == nil {
t.Fatal("expected error for empty payload")
}
}
func TestPayloadToBattleReportGarbageDataDoesNotPanic(t *testing.T) {
t.Parallel()
_, err := PayloadToBattleReport([]byte{0x01, 0x02, 0x03})
if err == nil {
t.Fatal("expected error for malformed payload")
}
}
func TestPayloadToBattleReportMissingID(t *testing.T) {
t.Parallel()
payload := buildBattleReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.BattleReportStart(builder)
fbs.BattleReportAddPlanet(builder, 7)
return fbs.BattleReportEnd(builder)
})
_, err := PayloadToBattleReport(payload)
if err == nil {
t.Fatal("expected error for missing battle report id")
}
if !strings.Contains(err.Error(), "id is missing") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestPayloadToBattleReportMissingRaceValue(t *testing.T) {
t.Parallel()
payload := buildBattleReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.RaceEntryStart(builder)
fbs.RaceEntryAddKey(builder, 1)
race := fbs.RaceEntryEnd(builder)
fbs.BattleReportStartRacesVector(builder, 1)
builder.PrependUOffsetT(race)
races := builder.EndVector(1)
fbs.BattleReportStart(builder)
fbs.BattleReportAddId(builder, fbs.CreateUUID(builder, 1, 2))
fbs.BattleReportAddRaces(builder, races)
return fbs.BattleReportEnd(builder)
})
_, err := PayloadToBattleReport(payload)
if err == nil {
t.Fatal("expected error for missing race value")
}
if !strings.Contains(err.Error(), "race value is missing") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestPayloadToBattleReportMissingShipValue(t *testing.T) {
t.Parallel()
payload := buildBattleReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.ShipEntryStart(builder)
fbs.ShipEntryAddKey(builder, 1)
ship := fbs.ShipEntryEnd(builder)
fbs.BattleReportStartShipsVector(builder, 1)
builder.PrependUOffsetT(ship)
ships := builder.EndVector(1)
fbs.BattleReportStart(builder)
fbs.BattleReportAddId(builder, fbs.CreateUUID(builder, 1, 2))
fbs.BattleReportAddShips(builder, ships)
return fbs.BattleReportEnd(builder)
})
_, err := PayloadToBattleReport(payload)
if err == nil {
t.Fatal("expected error for missing ship value")
}
if !strings.Contains(err.Error(), "ship value is missing") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestPayloadToBattleReportOverflowInt(t *testing.T) {
t.Parallel()
if strconv.IntSize == 64 {
t.Skip("int overflow from int64 is not possible on 64-bit runtime")
}
maxInt := int(^uint(0) >> 1)
overflowValue := int64(maxInt) + 1
payload := buildBattleReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.BattleActionReportStart(builder)
fbs.BattleActionReportAddAttacker(builder, overflowValue)
protocol := fbs.BattleActionReportEnd(builder)
fbs.BattleReportStartProtocolVector(builder, 1)
builder.PrependUOffsetT(protocol)
protocolVector := builder.EndVector(1)
fbs.BattleReportStart(builder)
fbs.BattleReportAddId(builder, fbs.CreateUUID(builder, 1, 2))
fbs.BattleReportAddProtocol(builder, protocolVector)
return fbs.BattleReportEnd(builder)
})
_, err := PayloadToBattleReport(payload)
if err == nil {
t.Fatal("expected overflow error")
}
if !strings.Contains(err.Error(), "overflows int") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestPayloadToBattleReportOverflowUint(t *testing.T) {
t.Parallel()
if strconv.IntSize == 64 {
t.Skip("uint overflow from uint64 is not possible on 64-bit runtime")
}
maxUint := uint64(^uint(0))
overflowValue := maxUint + 1
payload := buildBattleReportPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
fbs.BattleReportStart(builder)
fbs.BattleReportAddId(builder, fbs.CreateUUID(builder, 1, 2))
fbs.BattleReportAddPlanet(builder, overflowValue)
return fbs.BattleReportEnd(builder)
})
_, err := PayloadToBattleReport(payload)
if err == nil {
t.Fatal("expected overflow error")
}
if !strings.Contains(err.Error(), "overflows uint") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestBattleReportToPayloadDeterministicMapEncoding(t *testing.T) {
t.Parallel()
report := sampleBattleReport()
firstPayload, err := BattleReportToPayload(report)
if err != nil {
t.Fatalf("encode battle report payload: %v", err)
}
for i := 0; i < 20; i++ {
nextPayload, nextErr := BattleReportToPayload(report)
if nextErr != nil {
t.Fatalf("encode battle report payload #%d: %v", i+2, nextErr)
}
if !bytes.Equal(firstPayload, nextPayload) {
t.Fatalf("payload differs between runs at iteration %d", i+2)
}
}
}
func sampleBattleReport() *model.BattleReport {
return &model.BattleReport{
ID: uuid.MustParse("44444444-4444-4444-4444-444444444444"),
Planet: 12,
PlanetName: "Nexus",
Races: map[int]uuid.UUID{
2: uuid.MustParse("55555555-5555-5555-5555-555555555555"),
1: uuid.MustParse("66666666-6666-6666-6666-666666666666"),
},
Ships: map[int]model.BattleReportGroup{
3: {
InBattle: true,
Number: 20,
NumberLeft: 7,
LoadQuantity: model.Float(3.5),
Tech: map[string]model.Float{"WEAPONS": model.Float(2.0), "DRIVE": model.Float(1.5)},
Race: "Terrans",
ClassName: "Frigate",
LoadType: "MAT",
},
1: {
InBattle: false,
Number: 15,
NumberLeft: 15,
LoadQuantity: model.Float(0.0),
Tech: map[string]model.Float{"CARGO": model.Float(1.25), "SHIELDS": model.Float(1.75)},
Race: "Martians",
ClassName: "Destroyer",
LoadType: "CAP",
},
},
Protocol: []model.BattleActionReport{
{Attacker: 1, AttackerShipClass: 3, Defender: 2, DefenderShipClass: 1, Destroyed: true},
{Attacker: 2, AttackerShipClass: 1, Defender: 1, DefenderShipClass: 3, Destroyed: false},
},
}
}
func battleReportWireClone(t *testing.T, source *model.BattleReport) *model.BattleReport {
t.Helper()
data, err := source.MarshalBinary()
if err != nil {
t.Fatalf("marshal source battle report: %v", err)
}
result := new(model.BattleReport)
if err := result.UnmarshalBinary(data); err != nil {
t.Fatalf("unmarshal source battle report clone: %v", err)
}
return result
}
func buildBattleReportPayload(build func(*flatbuffers.Builder) flatbuffers.UOffsetT) []byte {
builder := flatbuffers.NewBuilder(256)
offset := build(builder)
fbs.FinishBattleReportBuffer(builder, offset)
return builder.FinishedBytes()
}