// Pure-function coverage for `map/cargo-routes.ts.buildCargoRouteLines`. // The renderer turns each `ReportRouteEntry` into one shaft plus two // arrowhead wings; the tests assert geometry on a flat fixture, on a // torus seam-crossing fixture, and the per-load-type style/priority // mapping. Pixi-free — the helper is a pure projection of the report. import { describe, expect, test } from "vitest"; import type { GameReport, ReportPlanet, ReportRouteEntry, } from "../src/api/game-state"; import { ROUTE_LINE_ID_PREFIX, STYLE_ROUTE_CAP, STYLE_ROUTE_COL, STYLE_ROUTE_EMP, STYLE_ROUTE_MAT, buildCargoRouteLines, } from "../src/map/cargo-routes"; function makePlanet(overrides: Partial): ReportPlanet { return { number: 0, name: "", x: 0, y: 0, kind: "local", owner: null, size: null, resources: null, industryStockpile: null, materialsStockpile: null, industry: null, population: null, colonists: null, production: null, freeIndustry: null, ...overrides, }; } function makeReport( planets: ReportPlanet[], source: number, entries: ReportRouteEntry[], mapWidth = 1000, mapHeight = 1000, ): GameReport { return { turn: 1, mapWidth, mapHeight, planetCount: planets.length, planets, race: "Earthlings", localShipClass: [], routes: [{ sourcePlanetNumber: source, entries }], localPlayerDrive: 1, }; } describe("buildCargoRouteLines", () => { test("emits one shaft + two wings per route entry", () => { const report = makeReport( [ makePlanet({ number: 1, x: 100, y: 100 }), makePlanet({ number: 2, x: 300, y: 100 }), ], 1, [{ loadType: "COL", destinationPlanetNumber: 2 }], ); const lines = buildCargoRouteLines(report); expect(lines.length).toBe(3); expect(lines.every((l) => l.kind === "line")).toBe(true); }); test("shaft endpoints follow the no-wrap straight line", () => { const report = makeReport( [ makePlanet({ number: 1, x: 100, y: 100 }), makePlanet({ number: 2, x: 300, y: 100 }), ], 1, [{ loadType: "COL", destinationPlanetNumber: 2 }], ); const [shaft] = buildCargoRouteLines(report); expect(shaft).toBeDefined(); if (shaft === undefined) return; expect(shaft.x1).toBe(100); expect(shaft.y1).toBe(100); expect(shaft.x2).toBe(300); expect(shaft.y2).toBe(100); }); test("shaft uses the torus-shortest delta on the seam", () => { // Source at x=950, dest at x=50 in a world 1000 wide. The // shorter wrap is +100 (right past x=1000 to x=1050), not // −900 (left to x=50). const report = makeReport( [ makePlanet({ number: 1, x: 950, y: 500 }), makePlanet({ number: 2, x: 50, y: 500 }), ], 1, [{ loadType: "MAT", destinationPlanetNumber: 2 }], 1000, 1000, ); const [shaft] = buildCargoRouteLines(report); expect(shaft).toBeDefined(); if (shaft === undefined) return; expect(shaft.x1).toBe(950); expect(shaft.x2).toBe(1050); // 950 + 100 expect(shaft.y2).toBe(500); }); test("each load type maps to the documented style and priority", () => { const report = makeReport( [ makePlanet({ number: 1, x: 100, y: 100 }), makePlanet({ number: 2, x: 200, y: 100 }), makePlanet({ number: 3, x: 300, y: 100 }), makePlanet({ number: 4, x: 400, y: 100 }), makePlanet({ number: 5, x: 500, y: 100 }), ], 1, [ { loadType: "COL", destinationPlanetNumber: 2 }, { loadType: "CAP", destinationPlanetNumber: 3 }, { loadType: "MAT", destinationPlanetNumber: 4 }, { loadType: "EMP", destinationPlanetNumber: 5 }, ], ); const lines = buildCargoRouteLines(report); expect(lines.length).toBe(12); const styleByPriority = new Map(); for (const line of lines) { const existing = styleByPriority.get(line.priority); if (existing === undefined) styleByPriority.set(line.priority, line.style); else expect(existing).toBe(line.style); } expect(styleByPriority.get(8)).toBe(STYLE_ROUTE_COL); expect(styleByPriority.get(7)).toBe(STYLE_ROUTE_CAP); expect(styleByPriority.get(6)).toBe(STYLE_ROUTE_MAT); expect(styleByPriority.get(5)).toBe(STYLE_ROUTE_EMP); }); test("line ids carry the ROUTE_LINE_ID_PREFIX high bit", () => { const report = makeReport( [ makePlanet({ number: 1, x: 100, y: 100 }), makePlanet({ number: 2, x: 200, y: 100 }), ], 1, [{ loadType: "COL", destinationPlanetNumber: 2 }], ); const lines = buildCargoRouteLines(report); for (const line of lines) { expect((line.id & ROUTE_LINE_ID_PREFIX) !== 0).toBe(true); } // Three distinct ids — one per segment. const ids = new Set(lines.map((l) => l.id)); expect(ids.size).toBe(3); }); test("skips routes whose source or destination is missing", () => { const report = makeReport( [makePlanet({ number: 1, x: 100, y: 100 })], 1, [ { loadType: "COL", destinationPlanetNumber: 999 }, // unknown dest ], ); expect(buildCargoRouteLines(report).length).toBe(0); }); test("skips zero-length routes (source == destination coords)", () => { const report = makeReport( [ makePlanet({ number: 1, x: 100, y: 100 }), makePlanet({ number: 2, x: 100, y: 100 }), ], 1, [{ loadType: "COL", destinationPlanetNumber: 2 }], ); expect(buildCargoRouteLines(report).length).toBe(0); }); test("returns an empty array when no routes are configured", () => { const report: GameReport = { turn: 1, mapWidth: 1000, mapHeight: 1000, planetCount: 1, planets: [makePlanet({ number: 1, x: 100, y: 100 })], race: "Earthlings", localShipClass: [], routes: [], localPlayerDrive: 1, }; expect(buildCargoRouteLines(report)).toEqual([]); }); test("arrowhead wings symmetric around the shaft direction", () => { const report = makeReport( [ makePlanet({ number: 1, x: 0, y: 0 }), makePlanet({ number: 2, x: 100, y: 0 }), ], 1, [{ loadType: "COL", destinationPlanetNumber: 2 }], ); const [shaft, leftWing, rightWing] = buildCargoRouteLines(report); expect(shaft).toBeDefined(); expect(leftWing).toBeDefined(); expect(rightWing).toBeDefined(); if ( shaft === undefined || leftWing === undefined || rightWing === undefined ) return; // Both wings start at the head. expect(leftWing.x1).toBe(shaft.x2); expect(leftWing.y1).toBe(shaft.y2); expect(rightWing.x1).toBe(shaft.x2); expect(rightWing.y1).toBe(shaft.y2); // And land symmetrically around the y axis (shaft along +x). expect(leftWing.y2 + rightWing.y2).toBeCloseTo(0); expect(leftWing.x2).toBeCloseTo(rightWing.x2); }); });