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 · 136 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137// Instance-level constraint metadata: `name` (attribution) and `enabled`// (present-but-inactive lever), plus `Search.supports`.
import { describe, expect, test } from "bun:test";
import { serialSGS } from "../../src/level-core/search/serialSGS.ts";import { constraintLabel } from "../../src/level-core/types.ts";import type { Constraint } from "../../src/level-core/types.ts";
import { MON_JAN_5, SAT_JAN_10, makeAssignment, makeProject, makeResource, makeTask, runExpectingFailure, runOnce,} from "./fixtures.ts";
function twoParallelTasksOnOneCrew() { const t1 = makeTask({ uniqueId: 1, start: MON_JAN_5, finish: SAT_JAN_10 }); const t2 = makeTask({ uniqueId: 2, start: MON_JAN_5, finish: SAT_JAN_10 }); return makeProject([t1, t2], { resources: [makeResource(100, "Crew")], assignments: [makeAssignment(1, 100), makeAssignment(2, 100)], });}
describe("constraintLabel", () => { test("falls back to kind when unnamed", () => { const c: Constraint = { kind: "MaxConcurrentResource", resourceUniqueId: 100, max: 1, }; expect(constraintLabel(c)).toBe("MaxConcurrentResource"); });
test("returns the instance name when set", () => { const c: Constraint = { kind: "MaxConcurrentResource", resourceUniqueId: 100, max: 1, name: "carpenters-cap", }; expect(constraintLabel(c)).toBe("carpenters-cap"); });});
describe("serialSGS — enabled: false", () => { test("disabled cap is not enforced and is recorded as an annotation", async () => { const schedule = await runOnce(twoParallelTasksOnOneCrew(), [ { kind: "MaxConcurrentResource", resourceUniqueId: 100, max: 1, name: "crew-cap", enabled: false, }, ]); // Not enforced: both tasks run in parallel from day 0. expect(schedule.tasks.map((t) => t.startDay)).toEqual([0, 0]); // Recorded: "disabled" is distinguishable from "absent". expect(schedule.annotations.get("disabledConstraints")).toEqual([ { kind: "MaxConcurrentResource", name: "crew-cap" }, ]); });
test("the same cap enabled serializes the tasks", async () => { const schedule = await runOnce(twoParallelTasksOnOneCrew(), [ { kind: "MaxConcurrentResource", resourceUniqueId: 100, max: 1, name: "crew-cap", }, ]); const starts = schedule.tasks.map((t) => t.startDay).sort((a, b) => a - b); expect(starts[0]).toBe(0); expect(starts[1]).toBeGreaterThan(0); expect(schedule.annotations.has("disabledConstraints")).toBe(false); });});
describe("serialSGS — named-instance attribution", () => { test("deadline violation attributes to the named constraint instance", async () => { const t1 = makeTask({ uniqueId: 1, start: MON_JAN_5, finish: SAT_JAN_10 }); const failure = await runExpectingFailure(makeProject([t1]), [ { kind: "Deadline", taskUniqueId: 1, latestFinish: 2, name: "phase1-gate", }, ]); expect(failure.explanations).toHaveLength(1); const explanation = failure.explanations[0]!; expect(constraintLabel(explanation.violated)).toBe("phase1-gate"); expect(explanation.message).toContain('"phase1-gate"'); });
test("unsupported named constraint is annotated with its name", async () => { const t1 = makeTask({ uniqueId: 1, start: MON_JAN_5, finish: SAT_JAN_10 }); const schedule = await runOnce( makeProject([t1]), [ { kind: "UnitPrecedence", unitId: 1, afterUnitIds: [], name: "floors-in-order", }, ], [{ id: 1, taskUniqueIds: [1] }], ); const unsupported = schedule.annotations.get("unsupportedConstraints") as | { message: string }[] | undefined; expect(unsupported).toHaveLength(1); expect(unsupported![0]!.message).toMatch( /"floors-in-order" \(UnitPrecedence\).*not implemented/, ); });});
describe("serialSGS — supports()", () => { test("reports enforced vs annotated kinds", () => { expect(serialSGS.supports!("MaxConcurrentResource")).toBe(true); expect(serialSGS.supports!("Deadline")).toBe(true); expect(serialSGS.supports!("ModeSelection")).toBe(true); expect(serialSGS.supports!("UnitPrecedence")).toBe(false); expect(serialSGS.supports!("UnimodalProfile")).toBe(false); expect(serialSGS.supports!("CrewFlowContinuity")).toBe(false); });});