276 lines
7.4 KiB
Go
276 lines
7.4 KiB
Go
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()
|
|
}
|