cd78f72f33
Complete rename of all technical identifiers across the codebase: Package names (11 packages): - @planarchy/* → @capakraken/* in all package.json, tsconfig, imports Import statements: 277 files, 548 occurrences replaced Database & Docker: - PostgreSQL user/db: planarchy → capakraken - Docker volumes: planarchy_pgdata → capakraken_pgdata - Connection strings updated in docker-compose, .env, CI CI/CD: - GitHub Actions workflow: all filter commands updated - Test database credentials updated Infrastructure: - Redis channel: planarchy:sse → capakraken:sse - Logger service name: planarchy-api → capakraken-api - Anonymization seed updated - Start/stop/restart scripts updated Test data: - Seed emails: @planarchy.dev → @capakraken.dev - E2E test credentials: all 11 spec files updated - Email defaults: @planarchy.app → @capakraken.app - localStorage keys: planarchy_* → capakraken_* Documentation: 30+ .md files updated Verification: - pnpm install: workspace resolution works - TypeScript: only pre-existing TS2589 (no new errors) - Engine: 310/310 tests pass - Staffing: 37/37 tests pass Co-Authored-By: claude-flow <ruv@ruv.net>
75 lines
3.3 KiB
TypeScript
75 lines
3.3 KiB
TypeScript
import { AllocationStatus } from "@capakraken/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);
|
||
});
|
||
});
|