100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { requireDungeonDefinition } from "./dungeonRegistry";
|
|
import {
|
|
MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES,
|
|
partyObjectivesForNavigation,
|
|
shouldLoadPartyNavigation,
|
|
} from "./partyNavigationPolicy";
|
|
import type { DungeonDefinition } from "./dungeonTypes";
|
|
import type { ManastormStageAssetPackage } from "./manastormStageAssets";
|
|
|
|
function manastormPackageWithNavigationTriangles(
|
|
triangleCount: number | undefined,
|
|
): Pick<ManastormStageAssetPackage, "navigation"> {
|
|
return {
|
|
navigation: {
|
|
id: "navigation",
|
|
url: "/navigation.glb",
|
|
checksum: "0".repeat(64),
|
|
compression: "none",
|
|
triangleCount,
|
|
},
|
|
};
|
|
}
|
|
|
|
function dungeonWithNavigation(
|
|
status: DungeonDefinition["validation"]["status"],
|
|
triangleCount: number,
|
|
navigation?: DungeonDefinition["validation"]["navigation"],
|
|
): Pick<DungeonDefinition, "assets" | "validation"> {
|
|
return {
|
|
assets: {
|
|
visual: [],
|
|
collision: [],
|
|
navigation: { id: "navigation", fileName: "navigation.glb", triangleCount },
|
|
},
|
|
validation: { status, navigation, blockers: [], warnings: [] },
|
|
};
|
|
}
|
|
|
|
describe("party navigation loading policy", () => {
|
|
it("keeps reviewed, budgeted dungeon navigation enabled", () => {
|
|
const wailingCaverns = requireDungeonDefinition("wailing-caverns");
|
|
|
|
expect(shouldLoadPartyNavigation("dungeon", wailingCaverns, null)).toBe(true);
|
|
});
|
|
|
|
it("does not schedule Shadowfang Keep's unvalidated oversized navigation graph", () => {
|
|
const shadowfangKeep = requireDungeonDefinition("shadowfang-keep");
|
|
|
|
expect(shadowfangKeep.validation.status).toBe("unvalidated");
|
|
expect(shadowfangKeep.assets.navigation?.triangleCount).toBeGreaterThan(
|
|
MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES,
|
|
);
|
|
expect(shouldLoadPartyNavigation("dungeon", shadowfangKeep, null)).toBe(false);
|
|
});
|
|
|
|
it("keeps unvalidated regular dungeons on breadcrumb following even under budget", () => {
|
|
expect(shouldLoadPartyNavigation(
|
|
"dungeon",
|
|
dungeonWithNavigation("unvalidated", 1_000),
|
|
null,
|
|
)).toBe(false);
|
|
});
|
|
|
|
it("loads an independently reviewed dungeon navigation graph", () => {
|
|
expect(shouldLoadPartyNavigation(
|
|
"dungeon",
|
|
dungeonWithNavigation("unvalidated", 1_000, "green"),
|
|
null,
|
|
)).toBe(true);
|
|
});
|
|
|
|
it("protects Manastorm from oversized or unmeasured synchronous graphs", () => {
|
|
const fallbackDungeon = requireDungeonDefinition("wailing-caverns");
|
|
|
|
expect(shouldLoadPartyNavigation(
|
|
"manastorm",
|
|
fallbackDungeon,
|
|
manastormPackageWithNavigationTriangles(MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES),
|
|
)).toBe(true);
|
|
expect(shouldLoadPartyNavigation(
|
|
"manastorm",
|
|
fallbackDungeon,
|
|
manastormPackageWithNavigationTriangles(MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES + 1),
|
|
)).toBe(false);
|
|
expect(shouldLoadPartyNavigation(
|
|
"manastorm",
|
|
fallbackDungeon,
|
|
manastormPackageWithNavigationTriangles(undefined),
|
|
)).toBe(false);
|
|
});
|
|
|
|
it("exposes objectives only after a usable navigation graph is ready", () => {
|
|
const objectives = [{ id: "boss", name: "Boss", position: [1, 2, 3] as const }];
|
|
|
|
expect(partyObjectivesForNavigation(false, objectives)).toEqual([]);
|
|
expect(partyObjectivesForNavigation(true, objectives)).toBe(objectives);
|
|
});
|
|
});
|