334 lines
11 KiB
Go
334 lines
11 KiB
Go
package transcoder
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
model "galaxy/model/order"
|
|
fbs "galaxy/schema/fbs/order"
|
|
|
|
flatbuffers "github.com/google/flatbuffers/go"
|
|
)
|
|
|
|
func TestOrderToPayloadAndPayloadToOrderRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
appliedTrue := true
|
|
appliedFalse := false
|
|
errZero := 0
|
|
errThree := 3
|
|
errSeven := 7
|
|
|
|
source := &model.Order{
|
|
UpdatedAt: 42,
|
|
Commands: []model.DecodableCommand{
|
|
&model.CommandRaceQuit{CommandMeta: commandMeta("cmd-01", model.CommandTypeRaceQuit, &appliedTrue, &errZero)},
|
|
&model.CommandRaceVote{CommandMeta: commandMeta("cmd-02", model.CommandTypeRaceVote, nil, nil), Acceptor: "race-a"},
|
|
&model.CommandRaceRelation{CommandMeta: commandMeta("cmd-03", model.CommandTypeRaceRelation, &appliedFalse, nil), Acceptor: "race-b", Relation: "WAR"},
|
|
&model.CommandShipClassCreate{CommandMeta: commandMeta("cmd-04", model.CommandTypeShipClassCreate, nil, &errThree), Name: "frigate", Drive: 1.5, Armament: 5, Weapons: 2.5, Shields: 3.5, Cargo: 4.5},
|
|
&model.CommandShipClassMerge{CommandMeta: commandMeta("cmd-05", model.CommandTypeShipClassMerge, nil, nil), Name: "alpha", Target: "beta"},
|
|
&model.CommandShipClassRemove{CommandMeta: commandMeta("cmd-06", model.CommandTypeShipClassRemove, nil, nil), Name: "obsolete"},
|
|
&model.CommandShipGroupBreak{CommandMeta: commandMeta("cmd-07", model.CommandTypeShipGroupBreak, nil, nil), ID: "group-1", NewID: "group-2", Quantity: 12},
|
|
&model.CommandShipGroupLoad{CommandMeta: commandMeta("cmd-08", model.CommandTypeShipGroupLoad, nil, nil), ID: "group-3", Cargo: "MAT", Quantity: 7.25},
|
|
&model.CommandShipGroupUnload{CommandMeta: commandMeta("cmd-09", model.CommandTypeShipGroupUnload, nil, nil), ID: "group-4", Quantity: 1.75},
|
|
&model.CommandShipGroupSend{CommandMeta: commandMeta("cmd-10", model.CommandTypeShipGroupSend, nil, nil), ID: "group-5", Destination: 19},
|
|
&model.CommandShipGroupUpgrade{CommandMeta: commandMeta("cmd-11", model.CommandTypeShipGroupUpgrade, nil, nil), ID: "group-6", Tech: "SHIELDS", Level: 2.0},
|
|
&model.CommandShipGroupMerge{CommandMeta: commandMeta("cmd-12", model.CommandTypeShipGroupMerge, nil, nil)},
|
|
&model.CommandShipGroupDismantle{CommandMeta: commandMeta("cmd-13", model.CommandTypeShipGroupDismantle, nil, nil), ID: "group-7"},
|
|
&model.CommandShipGroupTransfer{CommandMeta: commandMeta("cmd-14", model.CommandTypeShipGroupTransfer, nil, &errSeven), ID: "group-8", Acceptor: "race-c"},
|
|
&model.CommandShipGroupJoinFleet{CommandMeta: commandMeta("cmd-15", model.CommandTypeShipGroupJoinFleet, nil, nil), ID: "group-9", Name: "fleet-a"},
|
|
&model.CommandFleetMerge{CommandMeta: commandMeta("cmd-16", model.CommandTypeFleetMerge, nil, nil), Name: "fleet-b", Target: "fleet-c"},
|
|
&model.CommandFleetSend{CommandMeta: commandMeta("cmd-17", model.CommandTypeFleetSend, nil, nil), Name: "fleet-d", Destination: 31},
|
|
&model.CommandScienceCreate{CommandMeta: commandMeta("cmd-18", model.CommandTypeScienceCreate, nil, nil), Name: "science-a", Drive: 0.1, Weapons: 0.2, Shields: 0.3, Cargo: 0.4},
|
|
&model.CommandScienceRemove{CommandMeta: commandMeta("cmd-19", model.CommandTypeScienceRemove, nil, nil), Name: "science-b"},
|
|
&model.CommandPlanetRename{CommandMeta: commandMeta("cmd-20", model.CommandTypePlanetRename, nil, nil), Number: 7, Name: "new-name"},
|
|
&model.CommandPlanetProduce{CommandMeta: commandMeta("cmd-21", model.CommandTypePlanetProduce, nil, nil), Number: 8, Production: "SHIP", Subject: "frigate"},
|
|
&model.CommandPlanetRouteSet{CommandMeta: commandMeta("cmd-22", model.CommandTypePlanetRouteSet, nil, nil), Origin: 9, Destination: 10, LoadType: "EMP"},
|
|
&model.CommandPlanetRouteRemove{CommandMeta: commandMeta("cmd-23", model.CommandTypePlanetRouteRemove, nil, nil), Origin: 11, LoadType: "COL"},
|
|
},
|
|
}
|
|
|
|
payload, err := OrderToPayload(source)
|
|
if err != nil {
|
|
t.Fatalf("encode order payload: %v", err)
|
|
}
|
|
|
|
decoded, err := PayloadToOrder(payload)
|
|
if err != nil {
|
|
t.Fatalf("decode order payload: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(source, decoded) {
|
|
t.Fatalf("round-trip mismatch\nsource: %#v\ndecoded:%#v", source, decoded)
|
|
}
|
|
}
|
|
|
|
func TestOrderToPayloadNilOrder(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := OrderToPayload(nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for nil order")
|
|
}
|
|
}
|
|
|
|
func TestOrderToPayloadUnsupportedCommandType(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
source := &model.Order{
|
|
Commands: []model.DecodableCommand{unsupportedCommand{}},
|
|
}
|
|
|
|
_, err := OrderToPayload(source)
|
|
if err == nil {
|
|
t.Fatal("expected error for unsupported command type")
|
|
}
|
|
if !strings.Contains(err.Error(), "unsupported command type") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOrderToPayloadTypedNilCommand(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var typedNil *model.CommandRaceQuit
|
|
source := &model.Order{
|
|
Commands: []model.DecodableCommand{typedNil},
|
|
}
|
|
|
|
_, err := OrderToPayload(source)
|
|
if err == nil {
|
|
t.Fatal("expected error for typed nil command")
|
|
}
|
|
if !strings.Contains(err.Error(), "command is nil") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOrderToPayloadInvalidEnum(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
source := &model.Order{
|
|
Commands: []model.DecodableCommand{
|
|
&model.CommandRaceRelation{
|
|
CommandMeta: commandMeta("cmd-1", model.CommandTypeRaceRelation, nil, nil),
|
|
Acceptor: "race-a",
|
|
Relation: "ALLY",
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := OrderToPayload(source)
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid enum value")
|
|
}
|
|
if !strings.Contains(err.Error(), "unsupported relation value") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderEmptyData(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := PayloadToOrder(nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty payload")
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderGarbageDataDoesNotPanic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := PayloadToOrder([]byte{0x01, 0x02, 0x03})
|
|
if err == nil {
|
|
t.Fatal("expected error for malformed payload")
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderUnknownPayloadType(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
payload := buildSingleCommandOrderPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
|
fbs.CommandRaceQuitStart(builder)
|
|
commandPayload := fbs.CommandRaceQuitEnd(builder)
|
|
cmdID := builder.CreateString("cmd-1")
|
|
|
|
fbs.CommandItemStart(builder)
|
|
fbs.CommandItemAddCmdId(builder, cmdID)
|
|
fbs.CommandItemAddPayloadType(builder, fbs.CommandPayload(127))
|
|
fbs.CommandItemAddPayload(builder, commandPayload)
|
|
return fbs.CommandItemEnd(builder)
|
|
})
|
|
|
|
_, err := PayloadToOrder(payload)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown payload type")
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown command payload type") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderMissingPayload(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
payload := buildSingleCommandOrderPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
|
cmdID := builder.CreateString("cmd-1")
|
|
|
|
fbs.CommandItemStart(builder)
|
|
fbs.CommandItemAddCmdId(builder, cmdID)
|
|
fbs.CommandItemAddPayloadType(builder, fbs.CommandPayloadCommandRaceQuit)
|
|
return fbs.CommandItemEnd(builder)
|
|
})
|
|
|
|
_, err := PayloadToOrder(payload)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing payload")
|
|
}
|
|
if !strings.Contains(err.Error(), "payload is missing") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderPayloadTypeNone(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
payload := buildSingleCommandOrderPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
|
fbs.CommandRaceQuitStart(builder)
|
|
commandPayload := fbs.CommandRaceQuitEnd(builder)
|
|
|
|
cmdID := builder.CreateString("cmd-1")
|
|
fbs.CommandItemStart(builder)
|
|
fbs.CommandItemAddCmdId(builder, cmdID)
|
|
fbs.CommandItemAddPayload(builder, commandPayload)
|
|
return fbs.CommandItemEnd(builder)
|
|
})
|
|
|
|
_, err := PayloadToOrder(payload)
|
|
if err == nil {
|
|
t.Fatal("expected error for NONE payload type")
|
|
}
|
|
if !strings.Contains(err.Error(), "payload type is NONE") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderUnknownEnum(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
payload := buildSingleCommandOrderPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
|
acceptor := builder.CreateString("race-a")
|
|
fbs.CommandRaceRelationStart(builder)
|
|
fbs.CommandRaceRelationAddAcceptor(builder, acceptor)
|
|
fbs.CommandRaceRelationAddRelation(builder, fbs.RelationUNKNOWN)
|
|
commandPayload := fbs.CommandRaceRelationEnd(builder)
|
|
|
|
cmdID := builder.CreateString("cmd-1")
|
|
fbs.CommandItemStart(builder)
|
|
fbs.CommandItemAddCmdId(builder, cmdID)
|
|
fbs.CommandItemAddPayloadType(builder, fbs.CommandPayloadCommandRaceRelation)
|
|
fbs.CommandItemAddPayload(builder, commandPayload)
|
|
return fbs.CommandItemEnd(builder)
|
|
})
|
|
|
|
_, err := PayloadToOrder(payload)
|
|
if err == nil {
|
|
t.Fatal("expected error for UNKNOWN enum")
|
|
}
|
|
if !strings.Contains(err.Error(), "UNKNOWN") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPayloadToOrderOverflow(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 := buildSingleCommandOrderPayload(func(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
|
name := builder.CreateString("planet-a")
|
|
fbs.CommandPlanetRenameStart(builder)
|
|
fbs.CommandPlanetRenameAddNumber(builder, overflowValue)
|
|
fbs.CommandPlanetRenameAddName(builder, name)
|
|
commandPayload := fbs.CommandPlanetRenameEnd(builder)
|
|
|
|
cmdID := builder.CreateString("cmd-1")
|
|
fbs.CommandItemStart(builder)
|
|
fbs.CommandItemAddCmdId(builder, cmdID)
|
|
fbs.CommandItemAddPayloadType(builder, fbs.CommandPayloadCommandPlanetRename)
|
|
fbs.CommandItemAddPayload(builder, commandPayload)
|
|
return fbs.CommandItemEnd(builder)
|
|
})
|
|
|
|
_, err := PayloadToOrder(payload)
|
|
if err == nil {
|
|
t.Fatal("expected overflow error")
|
|
}
|
|
if !strings.Contains(err.Error(), "overflows int") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestInt64ToInt(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
value, err := int64ToInt(123, "field")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if value != 123 {
|
|
t.Fatalf("unexpected int value: %d", value)
|
|
}
|
|
|
|
if strconv.IntSize == 32 {
|
|
maxInt := int(^uint(0) >> 1)
|
|
_, err = int64ToInt(int64(maxInt)+1, "field")
|
|
if err == nil {
|
|
t.Fatal("expected overflow error")
|
|
}
|
|
}
|
|
}
|
|
|
|
type unsupportedCommand struct{}
|
|
|
|
func (unsupportedCommand) CommandID() string {
|
|
return "unsupported"
|
|
}
|
|
|
|
func (unsupportedCommand) CommandType() model.CommandType {
|
|
return model.CommandType("unsupported")
|
|
}
|
|
|
|
func commandMeta(id string, cmdType model.CommandType, applied *bool, errCode *int) model.CommandMeta {
|
|
return model.CommandMeta{
|
|
CmdType: cmdType,
|
|
CmdID: id,
|
|
CmdApplied: applied,
|
|
CmdErrCode: errCode,
|
|
}
|
|
}
|
|
|
|
func buildSingleCommandOrderPayload(itemBuilder func(*flatbuffers.Builder) flatbuffers.UOffsetT) []byte {
|
|
builder := flatbuffers.NewBuilder(256)
|
|
|
|
itemOffset := itemBuilder(builder)
|
|
|
|
fbs.OrderStartCommandsVector(builder, 1)
|
|
builder.PrependUOffsetT(itemOffset)
|
|
commands := builder.EndVector(1)
|
|
|
|
fbs.OrderStart(builder)
|
|
fbs.OrderAddUpdatedAt(builder, 1)
|
|
fbs.OrderAddCommands(builder, commands)
|
|
orderOffset := fbs.OrderEnd(builder)
|
|
fbs.FinishOrderBuffer(builder, orderOffset)
|
|
|
|
return builder.FinishedBytes()
|
|
}
|