Something went wrong. Try again.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Something went wrong. Try again.
4.1 kB · 129 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130import { test, expect, describe } from "bun:test";
import { createRegistry } from "../../src/level-blocks/registry.ts";
import { makeDef, makeDefs, ToyCapBlock } from "./pipelineFixtures.ts";
describe("createRegistry — register/get/list", () => { test("registers defs passed to the constructor", () => { const registry = createRegistry(makeDefs()); expect(registry.get("ToyCap")?.code).toBe("ToyCap"); expect(registry.get("ToyGreedy")?.family).toBe("search"); });
test("get returns undefined for unknown codes", () => { const registry = createRegistry(); expect(registry.get("Nope")).toBeUndefined(); });
test("register adds a def after construction", () => { const registry = createRegistry(); registry.register( makeDef("ToyCap", "constraint", ToyCapBlock as never, { resource: "crane", max: 2, }), ); expect(registry.get("ToyCap")).toBeDefined(); });
test("list returns all defs without a filter", () => { const registry = createRegistry(makeDefs()); expect(registry.list()).toHaveLength(4); });
test("list filters by family", () => { const registry = createRegistry(makeDefs()); const constraints = registry.list({ family: "constraint" }); expect(constraints.map((d) => d.code)).toEqual(["ToyCap"]); });
test("list filters by tier", () => { const registry = createRegistry(makeDefs()); const sales = registry.list({ tier: "sales" }); expect(sales.map((d) => d.code)).toEqual(["ToyCap"]); });
test("list filters by family and tier together", () => { const registry = createRegistry(makeDefs()); expect( registry.list({ family: "constraint", tier: "engineer" }), ).toHaveLength(0); expect(registry.list({ family: "search", tier: "engineer" })).toHaveLength( 1, ); });});
describe("createRegistry — registration validation", () => { test("throws on duplicate code", () => { const registry = createRegistry(makeDefs()); expect(() => registry.register( makeDef("ToyCap", "constraint", ToyCapBlock as never, { resource: "x", max: 1, }), ), ).toThrow(/duplicate/i); });
test("throws when defaultShadow fails the block input schema", () => { expect(() => createRegistry([ makeDef("BadShadow", "constraint", ToyCapBlock as never, { resource: "crane", max: 0, }), ]), ).toThrow(/defaultShadow/); });
test("a failed register leaves the registry unchanged", () => { const registry = createRegistry(); expect(() => registry.register( makeDef("Bad", "constraint", ToyCapBlock as never, { max: 2 }), ), ).toThrow(); expect(registry.get("Bad")).toBeUndefined(); expect(registry.list()).toHaveLength(0); });
test("accepts panelBindings and projectAnchoredPaths that resolve in defaultShadow", () => { const registry = createRegistry(); const def = makeDef("ToyCap", "constraint", ToyCapBlock as never, { resource: "crane", max: 2, }); def.panelBindings = [ { paramPath: "max", label: "Max crews", control: "number" }, ]; def.projectAnchoredPaths = ["resource"]; expect(() => registry.register(def)).not.toThrow(); });
test("throws when a panelBindings paramPath does not resolve in defaultShadow", () => { const registry = createRegistry(); const def = makeDef("ToyCap", "constraint", ToyCapBlock as never, { resource: "crane", max: 2, }); def.panelBindings = [ { paramPath: "maximum", label: "Max crews", control: "number" }, ]; expect(() => registry.register(def)).toThrow(/"maximum" does not resolve/); });
test("throws when a projectAnchoredPath does not resolve in defaultShadow", () => { const registry = createRegistry(); const def = makeDef("ToyCap", "constraint", ToyCapBlock as never, { resource: "crane", max: 2, }); def.projectAnchoredPaths = ["window.start"]; expect(() => registry.register(def)).toThrow( /"window.start" does not resolve/, ); });});