Files
galaxy-game/ui/frontend/tests/e2e/fixtures/order-fbs.ts
T
Ilia Denisov 7bea22b0b5 ui/phase-21: sciences CRUD list, designer, and production-picker integration
Lights up the player-defined sciences feature: a table view with sort
and filter, a designer with four percent inputs and a strict
sum-equals-100 gate, and a Research-sub-row integration so the
planet production picker lists the user's sciences alongside the
four tech buttons. Phase 21 decisions are baked back into ui/PLAN.md
(no UpdateScience on the wire — write-once via createScience +
removeScience; percentages instead of fractions; sciences live under
the existing Research segment).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 21:32:37 +02:00

307 lines
8.2 KiB
TypeScript

// FlatBuffers payload builders for the Phase 14 / Phase 15 Playwright
// suites. Mirrors what `pkg/transcoder/order.go` produces in production
// for the `user.games.order` POST response and the
// `user.games.order.get` GET response. Phase 15 extends the fixture
// with a `setProductionType` variant so a single mocked gateway can
// echo either rename or production-switch commands back to the client.
import { Builder } from "flatbuffers";
import { uuidToHiLo } from "../../../src/api/game-state";
import { UUID } from "../../../src/proto/galaxy/fbs/common";
import {
CommandItem,
CommandPayload,
CommandPlanetProduce,
CommandPlanetRename,
CommandPlanetRouteRemove,
CommandPlanetRouteSet,
CommandScienceCreate,
CommandScienceRemove,
CommandShipClassCreate,
CommandShipClassRemove,
PlanetProduction,
PlanetRouteLoadType,
UserGamesOrder,
UserGamesOrderGetResponse,
UserGamesOrderResponse,
} from "../../../src/proto/galaxy/fbs/order";
interface CommandResultFixtureBase {
cmdId: string;
applied: boolean | null;
errorCode: number | null;
}
export interface PlanetRenameResultFixture extends CommandResultFixtureBase {
kind: "planetRename";
planetNumber: number;
name: string;
}
export interface SetProductionTypeResultFixture
extends CommandResultFixtureBase {
kind: "setProductionType";
planetNumber: number;
productionType:
| "MAT"
| "CAP"
| "DRIVE"
| "WEAPONS"
| "SHIELDS"
| "CARGO"
| "SCIENCE"
| "SHIP";
subject: string;
}
export interface SetCargoRouteResultFixture extends CommandResultFixtureBase {
kind: "setCargoRoute";
sourcePlanetNumber: number;
destinationPlanetNumber: number;
loadType: "COL" | "CAP" | "MAT" | "EMP";
}
export interface RemoveCargoRouteResultFixture
extends CommandResultFixtureBase {
kind: "removeCargoRoute";
sourcePlanetNumber: number;
loadType: "COL" | "CAP" | "MAT" | "EMP";
}
export interface CreateShipClassResultFixture extends CommandResultFixtureBase {
kind: "createShipClass";
name: string;
drive: number;
armament: number;
weapons: number;
shields: number;
cargo: number;
}
export interface RemoveShipClassResultFixture extends CommandResultFixtureBase {
kind: "removeShipClass";
name: string;
}
export interface CreateScienceResultFixture extends CommandResultFixtureBase {
kind: "createScience";
name: string;
drive: number;
weapons: number;
shields: number;
cargo: number;
}
export interface RemoveScienceResultFixture extends CommandResultFixtureBase {
kind: "removeScience";
name: string;
}
export type CommandResultFixture =
| PlanetRenameResultFixture
| SetProductionTypeResultFixture
| SetCargoRouteResultFixture
| RemoveCargoRouteResultFixture
| CreateShipClassResultFixture
| RemoveShipClassResultFixture
| CreateScienceResultFixture
| RemoveScienceResultFixture;
export function buildOrderResponsePayload(
gameId: string,
commands: CommandResultFixture[],
updatedAt: number,
): Uint8Array {
const builder = new Builder(256);
const itemOffsets = commands.map((c) => encodeItem(builder, c));
const commandsVec = UserGamesOrderResponse.createCommandsVector(
builder,
itemOffsets,
);
const [hi, lo] = uuidToHiLo(gameId);
const gameIdOffset = UUID.createUUID(builder, hi, lo);
UserGamesOrderResponse.startUserGamesOrderResponse(builder);
UserGamesOrderResponse.addGameId(builder, gameIdOffset);
UserGamesOrderResponse.addUpdatedAt(builder, BigInt(updatedAt));
UserGamesOrderResponse.addCommands(builder, commandsVec);
const offset = UserGamesOrderResponse.endUserGamesOrderResponse(builder);
builder.finish(offset);
return builder.asUint8Array();
}
export function buildOrderGetResponsePayload(
gameId: string,
commands: CommandResultFixture[],
updatedAt: number,
found = true,
): Uint8Array {
const builder = new Builder(256);
let orderOffset = 0;
if (found) {
const itemOffsets = commands.map((c) => encodeItem(builder, c));
const commandsVec = UserGamesOrder.createCommandsVector(
builder,
itemOffsets,
);
const [hi, lo] = uuidToHiLo(gameId);
const gameIdOffset = UUID.createUUID(builder, hi, lo);
UserGamesOrder.startUserGamesOrder(builder);
UserGamesOrder.addGameId(builder, gameIdOffset);
UserGamesOrder.addUpdatedAt(builder, BigInt(updatedAt));
UserGamesOrder.addCommands(builder, commandsVec);
orderOffset = UserGamesOrder.endUserGamesOrder(builder);
}
UserGamesOrderGetResponse.startUserGamesOrderGetResponse(builder);
UserGamesOrderGetResponse.addFound(builder, found);
if (orderOffset !== 0) {
UserGamesOrderGetResponse.addOrder(builder, orderOffset);
}
const offset =
UserGamesOrderGetResponse.endUserGamesOrderGetResponse(builder);
builder.finish(offset);
return builder.asUint8Array();
}
function encodeItem(builder: Builder, c: CommandResultFixture): number {
const cmdIdOffset = builder.createString(c.cmdId);
let payloadType: CommandPayload;
let inner: number;
switch (c.kind) {
case "planetRename": {
const nameOffset = builder.createString(c.name);
inner = CommandPlanetRename.createCommandPlanetRename(
builder,
BigInt(c.planetNumber),
nameOffset,
);
payloadType = CommandPayload.CommandPlanetRename;
break;
}
case "setProductionType": {
const subjectOffset = builder.createString(c.subject);
inner = CommandPlanetProduce.createCommandPlanetProduce(
builder,
BigInt(c.planetNumber),
productionTypeToFBS(c.productionType),
subjectOffset,
);
payloadType = CommandPayload.CommandPlanetProduce;
break;
}
case "setCargoRoute": {
inner = CommandPlanetRouteSet.createCommandPlanetRouteSet(
builder,
BigInt(c.sourcePlanetNumber),
BigInt(c.destinationPlanetNumber),
cargoLoadTypeToFBS(c.loadType),
);
payloadType = CommandPayload.CommandPlanetRouteSet;
break;
}
case "removeCargoRoute": {
inner = CommandPlanetRouteRemove.createCommandPlanetRouteRemove(
builder,
BigInt(c.sourcePlanetNumber),
cargoLoadTypeToFBS(c.loadType),
);
payloadType = CommandPayload.CommandPlanetRouteRemove;
break;
}
case "createShipClass": {
const nameOffset = builder.createString(c.name);
inner = CommandShipClassCreate.createCommandShipClassCreate(
builder,
nameOffset,
c.drive,
BigInt(c.armament),
c.weapons,
c.shields,
c.cargo,
);
payloadType = CommandPayload.CommandShipClassCreate;
break;
}
case "removeShipClass": {
const nameOffset = builder.createString(c.name);
inner = CommandShipClassRemove.createCommandShipClassRemove(
builder,
nameOffset,
);
payloadType = CommandPayload.CommandShipClassRemove;
break;
}
case "createScience": {
const nameOffset = builder.createString(c.name);
inner = CommandScienceCreate.createCommandScienceCreate(
builder,
nameOffset,
c.drive,
c.weapons,
c.shields,
c.cargo,
);
payloadType = CommandPayload.CommandScienceCreate;
break;
}
case "removeScience": {
const nameOffset = builder.createString(c.name);
inner = CommandScienceRemove.createCommandScienceRemove(
builder,
nameOffset,
);
payloadType = CommandPayload.CommandScienceRemove;
break;
}
}
CommandItem.startCommandItem(builder);
CommandItem.addCmdId(builder, cmdIdOffset);
if (c.applied !== null) CommandItem.addCmdApplied(builder, c.applied);
if (c.errorCode !== null) {
CommandItem.addCmdErrorCode(builder, BigInt(c.errorCode));
}
CommandItem.addPayloadType(builder, payloadType);
CommandItem.addPayload(builder, inner);
return CommandItem.endCommandItem(builder);
}
function productionTypeToFBS(
value: SetProductionTypeResultFixture["productionType"],
): PlanetProduction {
switch (value) {
case "MAT":
return PlanetProduction.MAT;
case "CAP":
return PlanetProduction.CAP;
case "DRIVE":
return PlanetProduction.DRIVE;
case "WEAPONS":
return PlanetProduction.WEAPONS;
case "SHIELDS":
return PlanetProduction.SHIELDS;
case "CARGO":
return PlanetProduction.CARGO;
case "SCIENCE":
return PlanetProduction.SCIENCE;
case "SHIP":
return PlanetProduction.SHIP;
}
}
function cargoLoadTypeToFBS(
value: SetCargoRouteResultFixture["loadType"],
): PlanetRouteLoadType {
switch (value) {
case "COL":
return PlanetRouteLoadType.COL;
case "CAP":
return PlanetRouteLoadType.CAP;
case "MAT":
return PlanetRouteLoadType.MAT;
case "EMP":
return PlanetRouteLoadType.EMP;
}
}