Files
CapaKraken/packages/engine/src/__tests__/budget.test.ts
T
Hartmut cd78f72f33 chore: full technical rename planarchy → capakraken
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>
2026-03-27 13:18:09 +01:00

75 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
});
});