// Vitest unit coverage for `sync/order-load.ts`. Builds FBS // `UserGamesOrderGetResponse` payloads by hand and verifies the // decoder produces the expected `OrderCommand[]`. import { Builder } from "flatbuffers"; import { describe, expect, test, vi } from "vitest"; import type { GalaxyClient } from "../src/api/galaxy-client"; import { uuidToHiLo } from "../src/api/game-state"; import { UUID } from "../src/proto/galaxy/fbs/common"; import { CommandItem, CommandPayload, CommandPlanetRename, UserGamesOrder, UserGamesOrderGet, UserGamesOrderGetResponse, } from "../src/proto/galaxy/fbs/order"; import { fetchOrder, OrderLoadError } from "../src/sync/order-load"; const GAME_ID = "11111111-2222-3333-4444-555555555555"; function mockClient( executeCommand: ( messageType: string, payload: Uint8Array, ) => Promise<{ resultCode: string; payloadBytes: Uint8Array }>, ): GalaxyClient { return { executeCommand } as unknown as GalaxyClient; } function buildResponse( commands: { id: string; planetNumber: number; name: string }[], updatedAt: number, found = true, ): Uint8Array { const builder = new Builder(256); let orderOffset = 0; if (found) { const itemOffsets = commands.map((c) => { const cmdIdOffset = builder.createString(c.id); const nameOffset = builder.createString(c.name); const inner = CommandPlanetRename.createCommandPlanetRename( builder, BigInt(c.planetNumber), nameOffset, ); CommandItem.startCommandItem(builder); CommandItem.addCmdId(builder, cmdIdOffset); CommandItem.addPayloadType(builder, CommandPayload.CommandPlanetRename); CommandItem.addPayload(builder, inner); return CommandItem.endCommandItem(builder); }); const commandsVec = UserGamesOrder.createCommandsVector(builder, itemOffsets); const [hi, lo] = uuidToHiLo(GAME_ID); 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(); } describe("fetchOrder", () => { test("decodes a found response into typed commands", async () => { const responsePayload = buildResponse( [{ id: "cmd-1", planetNumber: 7, name: "Earth" }], 42, ); const exec = vi.fn(async (messageType: string) => { expect(messageType).toBe("user.games.order.get"); return { resultCode: "ok", payloadBytes: responsePayload }; }); const result = await fetchOrder(mockClient(exec), GAME_ID, 5); expect(result.commands).toHaveLength(1); const cmd = result.commands[0]!; expect(cmd.kind).toBe("planetRename"); if (cmd.kind !== "planetRename") return; expect(cmd.id).toBe("cmd-1"); expect(cmd.planetNumber).toBe(7); expect(cmd.name).toBe("Earth"); expect(result.updatedAt).toBe(42); }); test("found=false surfaces as an empty draft", async () => { const responsePayload = buildResponse([], 0, false); const exec = vi.fn(async () => ({ resultCode: "ok", payloadBytes: responsePayload, })); const result = await fetchOrder(mockClient(exec), GAME_ID, 5); expect(result.commands).toEqual([]); expect(result.updatedAt).toBe(0); }); test("rejects negative turn before issuing a request", async () => { const exec = vi.fn(async () => ({ resultCode: "ok", payloadBytes: new Uint8Array(), })); await expect(fetchOrder(mockClient(exec), GAME_ID, -1)).rejects.toBeInstanceOf( OrderLoadError, ); expect(exec).not.toHaveBeenCalled(); }); test("throws OrderLoadError on non-ok resultCode", async () => { const exec = vi.fn(async () => ({ resultCode: "internal_error", payloadBytes: new TextEncoder().encode( JSON.stringify({ code: "boom", message: "down" }), ), })); await expect(fetchOrder(mockClient(exec), GAME_ID, 5)).rejects.toMatchObject({ name: "OrderLoadError", resultCode: "internal_error", code: "boom", }); }); test("posts a well-formed UserGamesOrderGet payload", async () => { let captured: Uint8Array | null = null; const exec = vi.fn(async (_messageType, payload: Uint8Array) => { captured = payload; return { resultCode: "ok", payloadBytes: buildResponse([], 0, false), }; }); await fetchOrder(mockClient(exec), GAME_ID, 9); expect(captured).not.toBeNull(); const decoded = UserGamesOrderGet.getRootAsUserGamesOrderGet( new (await import("flatbuffers")).ByteBuffer(captured!), ); expect(Number(decoded.turn())).toBe(9); const id = decoded.gameId(); expect(id).not.toBeNull(); }); });