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.5 kB · 152 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153import { test, expect, describe } from "bun:test";
import { createRegistry } from "../../src/level-blocks/registry.ts";import type { PipelineGraph } from "../../src/level-blocks/graph.ts";import { compileGraph, GraphValidationError, type CompiledFamilies, type CompileTarget,} from "../../src/level-blocks/compile.ts";
import { makeDefs } from "./pipelineFixtures.ts";
const registry = createRegistry(makeDefs());
function makeGraph(): PipelineGraph { return { formatVersion: 1, blocks: [ { instanceId: "c1", code: "ToyCap", params: { resource: "crane", max: 2 }, provenance: "curated", shadow: false, }, { instanceId: "c2", code: "ToyCap", params: { resource: "forklift", max: 4 }, provenance: "curated", shadow: false, }, { instanceId: "s1", code: "ToyWeight", params: { weight: 0.5 }, provenance: "curated", shadow: false, }, { instanceId: "g1", code: "ToyGreedy", params: { seed: 7 }, provenance: "curated", shadow: false, }, { instanceId: "p1", code: "ToyProbe", params: { label: "trace" }, provenance: "curated", shadow: false, }, ], };}
/** Toy target proving the interface: echoes the grouped families + graph. */const echoTarget: CompileTarget<{ families: CompiledFamilies; graph: PipelineGraph;}> = { fromFamilies: (families, graph) => ({ families, graph }),};
describe("compileGraph — family grouping", () => { test("groups outputs by family and routes them to the target", () => { const { families, graph } = compileGraph(registry, makeGraph(), echoTarget);
expect(families.constraints.map((c) => c.instance.instanceId)).toEqual([ "c1", "c2", ]); expect(families.scorers.map((s) => s.instance.instanceId)).toEqual(["s1"]); expect(families.search.instance.instanceId).toBe("g1"); expect(families.collectors.map((c) => c.instance.instanceId)).toEqual([ "p1", ]); expect(graph.blocks).toHaveLength(5); });
test("each compiled entry carries def, instance, and apply() output", () => { const { families } = compileGraph(registry, makeGraph(), echoTarget);
expect(families.constraints[0]!.def.code).toBe("ToyCap"); expect(families.constraints[0]!.output).toEqual({ tag: "cap", resource: "crane", max: 2, }); expect(families.constraints[1]!.output).toEqual({ tag: "cap", resource: "forklift", max: 4, }); expect(families.scorers[0]!.output).toEqual({ tag: "weight", weight: 0.5 }); expect(families.search.output).toEqual({ tag: "greedy", seed: 7 }); expect(families.collectors[0]!.output).toEqual({ tag: "probe", label: "trace", }); });
test("grouping is independent of block order in the graph", () => { const graph = makeGraph(); graph.blocks.reverse(); const { families } = compileGraph(registry, graph, echoTarget);
expect(families.constraints.map((c) => c.instance.instanceId)).toEqual([ "c2", "c1", ]); expect(families.search.instance.instanceId).toBe("g1"); expect(families.scorers).toHaveLength(1); expect(families.collectors).toHaveLength(1); });
test("returns the target's value", () => { const countTarget: CompileTarget<number> = { fromFamilies: (families) => families.constraints.length + families.scorers.length + families.collectors.length + 1, }; expect(compileGraph(registry, makeGraph(), countTarget)).toBe(5); });});
describe("compileGraph — validation", () => { test("throws GraphValidationError on an invalid graph", () => { const graph = makeGraph(); graph.blocks[0]!.code = "Mystery"; expect(() => compileGraph(registry, graph, echoTarget)).toThrow( GraphValidationError, ); });
test("the thrown error carries the issues", () => { const graph = makeGraph(); graph.blocks[0]!.code = "Mystery"; try { compileGraph(registry, graph, echoTarget); throw new Error("expected compileGraph to throw"); } catch (err) { expect(err).toBeInstanceOf(GraphValidationError); const issues = (err as GraphValidationError).issues; expect(issues.some((i) => i.code === "Mystery")).toBe(true); } });});