battle-viewer e2e: mock user.games.battle ConnectRPC command
Phase 28 moved the battle fetch off the REST passthrough onto the signed envelope, so the Playwright spec's `page.route(...)` against the old REST path no longer intercepts anything and the viewer times out waiting for data. Update the spec to: - Build a FlatBuffers `BattleReport` payload in `fixtures/battle-fbs.ts` (mirrors `report-fbs.ts`'s pattern). - Add a `user.games.battle` case to the ExecuteCommand mock that decodes the FBS `GameBattleRequest`, returns the encoded report when the battle_id matches the seeded one, and surfaces a canonical `not_found` resultCode otherwise. - Drop the obsolete REST route stubs. - Drive the negative-path test with a real UUID that does not match the seeded one, so the gateway-side switch is the source of the 404 (the old `missing-uuid` literal was no longer a valid wire shape for the UUID decoder). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// Phase 28 — FBS BattleReport payload builder used by Battle Viewer
|
||||
// Playwright specs. Mirrors the `user.games.battle` ConnectRPC
|
||||
// response that the gateway re-encodes from the engine's JSON.
|
||||
|
||||
import { Builder } from "flatbuffers";
|
||||
|
||||
import {
|
||||
BattleActionReport,
|
||||
BattleReport,
|
||||
BattleReportGroup,
|
||||
RaceEntry,
|
||||
ShipEntry,
|
||||
TechEntry,
|
||||
UUID,
|
||||
} from "../../../src/proto/galaxy/fbs/battle";
|
||||
|
||||
export interface BattleGroupFixture {
|
||||
key: number;
|
||||
race: string;
|
||||
className: string;
|
||||
tech?: Record<string, number>;
|
||||
num: number;
|
||||
numLeft: number;
|
||||
loadType?: string;
|
||||
loadQuantity?: number;
|
||||
inBattle?: boolean;
|
||||
}
|
||||
|
||||
export interface BattleActionFixture {
|
||||
a: number;
|
||||
sa: number;
|
||||
d: number;
|
||||
sd: number;
|
||||
x: boolean;
|
||||
}
|
||||
|
||||
export interface BattleFixture {
|
||||
id: string;
|
||||
planet: number;
|
||||
planetName: string;
|
||||
races: Record<string, string>;
|
||||
ships: BattleGroupFixture[];
|
||||
protocol: BattleActionFixture[];
|
||||
}
|
||||
|
||||
export function buildBattlePayload(fixture: BattleFixture): Uint8Array {
|
||||
const builder = new Builder(512);
|
||||
|
||||
const planetNameOff = builder.createString(fixture.planetName);
|
||||
|
||||
const raceOffsets: number[] = [];
|
||||
for (const [keyStr, value] of Object.entries(fixture.races)) {
|
||||
const key = Number(keyStr);
|
||||
const [hi, lo] = uuidToHiLo(value);
|
||||
RaceEntry.startRaceEntry(builder);
|
||||
RaceEntry.addKey(builder, BigInt(key));
|
||||
RaceEntry.addValue(builder, UUID.createUUID(builder, hi, lo));
|
||||
raceOffsets.push(RaceEntry.endRaceEntry(builder));
|
||||
}
|
||||
const racesVec = BattleReport.createRacesVector(builder, raceOffsets);
|
||||
|
||||
const shipOffsets: number[] = [];
|
||||
for (const ship of fixture.ships) {
|
||||
const techOffsets: number[] = [];
|
||||
for (const [tk, tv] of Object.entries(ship.tech ?? {})) {
|
||||
const tkOff = builder.createString(tk);
|
||||
TechEntry.startTechEntry(builder);
|
||||
TechEntry.addKey(builder, tkOff);
|
||||
TechEntry.addValue(builder, tv);
|
||||
techOffsets.push(TechEntry.endTechEntry(builder));
|
||||
}
|
||||
const techVec = BattleReportGroup.createTechVector(builder, techOffsets);
|
||||
const raceStrOff = builder.createString(ship.race);
|
||||
const classNameOff = builder.createString(ship.className);
|
||||
const loadTypeOff = builder.createString(ship.loadType ?? "");
|
||||
BattleReportGroup.startBattleReportGroup(builder);
|
||||
BattleReportGroup.addInBattle(builder, ship.inBattle ?? true);
|
||||
BattleReportGroup.addNumber(builder, BigInt(ship.num));
|
||||
BattleReportGroup.addNumberLeft(builder, BigInt(ship.numLeft));
|
||||
BattleReportGroup.addLoadQuantity(builder, ship.loadQuantity ?? 0);
|
||||
BattleReportGroup.addTech(builder, techVec);
|
||||
BattleReportGroup.addRace(builder, raceStrOff);
|
||||
BattleReportGroup.addClassName(builder, classNameOff);
|
||||
BattleReportGroup.addLoadType(builder, loadTypeOff);
|
||||
const groupOff = BattleReportGroup.endBattleReportGroup(builder);
|
||||
ShipEntry.startShipEntry(builder);
|
||||
ShipEntry.addKey(builder, BigInt(ship.key));
|
||||
ShipEntry.addValue(builder, groupOff);
|
||||
shipOffsets.push(ShipEntry.endShipEntry(builder));
|
||||
}
|
||||
const shipsVec = BattleReport.createShipsVector(builder, shipOffsets);
|
||||
|
||||
const protocolOffsets: number[] = [];
|
||||
for (const action of fixture.protocol) {
|
||||
BattleActionReport.startBattleActionReport(builder);
|
||||
BattleActionReport.addAttacker(builder, BigInt(action.a));
|
||||
BattleActionReport.addAttackerShipClass(builder, BigInt(action.sa));
|
||||
BattleActionReport.addDefender(builder, BigInt(action.d));
|
||||
BattleActionReport.addDefenderShipClass(builder, BigInt(action.sd));
|
||||
BattleActionReport.addDestroyed(builder, action.x);
|
||||
protocolOffsets.push(BattleActionReport.endBattleActionReport(builder));
|
||||
}
|
||||
const protocolVec = BattleReport.createProtocolVector(
|
||||
builder,
|
||||
protocolOffsets,
|
||||
);
|
||||
|
||||
const [idHi, idLo] = uuidToHiLo(fixture.id);
|
||||
BattleReport.startBattleReport(builder);
|
||||
BattleReport.addId(builder, UUID.createUUID(builder, idHi, idLo));
|
||||
BattleReport.addPlanet(builder, BigInt(fixture.planet));
|
||||
BattleReport.addPlanetName(builder, planetNameOff);
|
||||
BattleReport.addRaces(builder, racesVec);
|
||||
BattleReport.addShips(builder, shipsVec);
|
||||
BattleReport.addProtocol(builder, protocolVec);
|
||||
builder.finish(BattleReport.endBattleReport(builder));
|
||||
return builder.asUint8Array();
|
||||
}
|
||||
|
||||
function uuidToHiLo(value: string): [bigint, bigint] {
|
||||
const hex = value.replace(/-/g, "").toLowerCase();
|
||||
if (hex.length !== 32 || /[^0-9a-f]/.test(hex)) {
|
||||
throw new Error(`buildBattlePayload: invalid uuid ${value}`);
|
||||
}
|
||||
const hi = BigInt(`0x${hex.slice(0, 16)}`);
|
||||
const lo = BigInt(`0x${hex.slice(16, 32)}`);
|
||||
return [hi, lo];
|
||||
}
|
||||
Reference in New Issue
Block a user