import { describe, expect, it } from "vitest"; import type { AssetEntry } from "@nengine/core"; import { AssetCoordinator, MAX_ASSET_REQUEST_IDS } from "../src/sync/asset-coordinator.js"; const index = (ids: string[]): Record => { const out: Record = {}; for (const id of ids) out[id] = { kind: "image", mime: "image/png", size: 1 }; return out; }; const entry = (id: string): Record => ({ [id]: { kind: "image", mime: "image/png", data: "x" } }); describe("AssetCoordinator (D37 on-demand asset requests)", () => { it("requests only wanted ids known to the server index", () => { const c = new AssetCoordinator(); c.setIndex(index(["grass-1", "water-1"])); c.want(["grass-1", "water-1", "missing-asset"]); expect(c.requestBatch()).toEqual(["grass-1", "water-1"]); expect(c.requestBatch()).toEqual([]); }); it("dedupes: never re-requests ids that are pending or loaded", () => { const c = new AssetCoordinator(); c.setIndex(index(["a", "b", "c"])); c.want(["a", "b", "c"]); const batch1 = c.requestBatch(); expect(batch1).toEqual(["a", "b", "c"]); // all pending — nothing new expect(c.requestBatch()).toEqual([]); // a response for a and b arrives; c's request is still in flight c.applyAssets({ ...entry("a"), ...entry("b") }); expect(c.has("a")).toBe(true); expect(c.has("b")).toBe(true); expect(c.has("c")).toBe(false); expect(c.requestBatch()).toEqual([]); // c still pending // c's response arrives — nothing left to request c.applyAssets(entry("c")); expect(c.has("c")).toBe(true); expect(c.requestBatch()).toEqual([]); }); it("caps a batch at MAX_ASSET_REQUEST_IDS", () => { const ids = Array.from({ length: MAX_ASSET_REQUEST_IDS + 10 }, (_, i) => `a${i}`); const c = new AssetCoordinator(); c.setIndex(index(ids)); c.want(ids); const batch = c.requestBatch(); expect(batch).toHaveLength(MAX_ASSET_REQUEST_IDS); const rest = c.requestBatch(); expect(rest).toHaveLength(10); }); it("new wanted ids from a later snapshot are requested after a prior batch resolved", () => { const c = new AssetCoordinator(); c.setIndex(index(["a"])); c.want(["a"]); expect(c.requestBatch()).toEqual(["a"]); c.applyAssets(entry("a")); // a fresh snapshot adds a new referenced asset c.setIndex(index(["a", "b"])); c.want(["b"]); expect(c.requestBatch()).toEqual(["b"]); }); it("applyAssets with an empty frame changes nothing", () => { const c = new AssetCoordinator(); c.setIndex(index(["a"])); c.want(["a"]); expect(c.requestBatch()).toEqual(["a"]); c.applyAssets({}); expect(c.has("a")).toBe(false); expect(c.requestBatch()).toEqual([]); // still pending until a response arrives }); });