chore(repo): initialize planarchy workspace

This commit is contained in:
2026-03-14 14:31:09 +01:00
commit dd55d0e78b
769 changed files with 166461 additions and 0 deletions
@@ -0,0 +1,74 @@
import { AllocationStatus } from "@planarchy/shared";
import { describe, expect, it } from "vitest";
import { computeBudgetStatus } from "../budget/monitor.js";
const now = new Date("2025-01-06");
const endDate = new Date("2025-03-07");
const mkAlloc = (
hoursPerDay: number,
dailyCostCents: number,
status: AllocationStatus = AllocationStatus.CONFIRMED,
) => ({
status,
dailyCostCents,
hoursPerDay,
startDate: now,
endDate,
});
describe("computeBudgetStatus", () => {
it("returns 0% utilization for no allocations", () => {
const result = computeBudgetStatus(1000000, 100, [], now, endDate);
expect(result.allocatedCents).toBe(0);
expect(result.utilizationPercent).toBe(0);
expect(result.remainingCents).toBe(1000000);
expect(result.warnings).toHaveLength(0);
});
it("calculates correct utilization", () => {
// 1 alloc, 10 working days, 8000 cents/day = 80000 cents
const alloc = { ...mkAlloc(8, 8000), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-17") };
const result = computeBudgetStatus(400000, 100, [alloc], now, endDate);
expect(result.confirmedCents).toBe(80000);
expect(result.utilizationPercent).toBeCloseTo(20, 0);
});
it("emits WARNING at 85% utilization", () => {
// Budget 100000, alloc cost 87000 = 87%
const alloc = { ...mkAlloc(8, 87000 / 10), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-17") };
const result = computeBudgetStatus(100000, 100, [alloc], now, endDate);
const hasWarning = result.warnings.some((w) => w.code === "BUDGET_WARNING");
expect(hasWarning).toBe(true);
});
it("emits CRITICAL at 95% utilization", () => {
const alloc = { ...mkAlloc(8, 96000 / 10), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-17") };
const result = computeBudgetStatus(100000, 100, [alloc], now, endDate);
const hasCritical = result.warnings.some((w) => w.code === "BUDGET_CRITICAL");
expect(hasCritical).toBe(true);
});
it("emits EXCEEDED when allocations exceed budget", () => {
const alloc = { ...mkAlloc(8, 120000 / 10), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-17") };
const result = computeBudgetStatus(100000, 100, [alloc], now, endDate);
const hasExceeded = result.warnings.some((w) => w.code === "BUDGET_EXCEEDED");
expect(hasExceeded).toBe(true);
});
it("applies win probability weighting", () => {
const alloc = { ...mkAlloc(8, 10000), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-17") };
const result = computeBudgetStatus(1000000, 50, [alloc], now, endDate);
// allocated = 10 days × 10000 = 100000, weighted = 50% = 50000
expect(result.winProbabilityWeightedCents).toBe(Math.round(result.allocatedCents * 0.5));
});
it("separates proposed from confirmed costs", () => {
const confirmed = { ...mkAlloc(8, 5000, AllocationStatus.CONFIRMED), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-10") };
const proposed = { ...mkAlloc(8, 5000, AllocationStatus.PROPOSED), startDate: new Date("2025-01-06"), endDate: new Date("2025-01-10") };
const result = computeBudgetStatus(1000000, 100, [confirmed, proposed], now, endDate);
expect(result.confirmedCents).toBeGreaterThan(0);
expect(result.proposedCents).toBeGreaterThan(0);
expect(result.confirmedCents).toBe(result.proposedCents);
});
});