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>
149 lines
5.4 KiB
TypeScript
149 lines
5.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { findMatchingRule, applyCostEffect } from "../rules/engine.js";
|
|
import { DEFAULT_CALCULATION_RULES } from "../rules/default-rules.js";
|
|
import type { CalculationRule } from "@capakraken/shared";
|
|
|
|
const now = new Date();
|
|
|
|
function makeRule(overrides: Partial<CalculationRule>): CalculationRule {
|
|
return {
|
|
id: "rule_1",
|
|
name: "Test Rule",
|
|
description: null,
|
|
triggerType: "SICK",
|
|
projectId: null,
|
|
orderType: null,
|
|
costEffect: "ZERO",
|
|
costReductionPercent: null,
|
|
chargeabilityEffect: "COUNT",
|
|
priority: 0,
|
|
isActive: true,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("findMatchingRule", () => {
|
|
it("matches by triggerType", () => {
|
|
const rules = [makeRule({ triggerType: "SICK" })];
|
|
const match = findMatchingRule(rules, "SICK");
|
|
expect(match).not.toBeNull();
|
|
expect(match!.costEffect).toBe("ZERO");
|
|
});
|
|
|
|
it("returns null when no rules match", () => {
|
|
const rules = [makeRule({ triggerType: "SICK" })];
|
|
const match = findMatchingRule(rules, "VACATION");
|
|
expect(match).toBeNull();
|
|
});
|
|
|
|
it("skips inactive rules", () => {
|
|
const rules = [makeRule({ triggerType: "SICK", isActive: false })];
|
|
const match = findMatchingRule(rules, "SICK");
|
|
expect(match).toBeNull();
|
|
});
|
|
|
|
it("prefers more specific rules (projectId match)", () => {
|
|
const global = makeRule({ id: "global", triggerType: "SICK", projectId: null });
|
|
const specific = makeRule({ id: "specific", triggerType: "SICK", projectId: "proj_1", costEffect: "CHARGE" });
|
|
const match = findMatchingRule([global, specific], "SICK", "proj_1");
|
|
expect(match!.rule.id).toBe("specific");
|
|
expect(match!.costEffect).toBe("CHARGE");
|
|
});
|
|
|
|
it("does not match project-specific rule to wrong project", () => {
|
|
const specific = makeRule({ triggerType: "SICK", projectId: "proj_1" });
|
|
const match = findMatchingRule([specific], "SICK", "proj_2");
|
|
expect(match).toBeNull();
|
|
});
|
|
|
|
it("prefers higher specificity over higher priority", () => {
|
|
const highPriority = makeRule({ id: "hp", triggerType: "SICK", priority: 100 });
|
|
const specific = makeRule({ id: "sp", triggerType: "SICK", projectId: "proj_1", priority: 0 });
|
|
const match = findMatchingRule([highPriority, specific], "SICK", "proj_1");
|
|
expect(match!.rule.id).toBe("sp");
|
|
});
|
|
|
|
it("breaks specificity ties with priority", () => {
|
|
const lowP = makeRule({ id: "low", triggerType: "SICK", priority: 5 });
|
|
const highP = makeRule({ id: "high", triggerType: "SICK", priority: 10 });
|
|
const match = findMatchingRule([lowP, highP], "SICK");
|
|
expect(match!.rule.id).toBe("high");
|
|
});
|
|
|
|
it("matches orderType filter", () => {
|
|
const rule = makeRule({ triggerType: "VACATION", orderType: "CHARGEABLE" as never, costEffect: "REDUCE", costReductionPercent: 50 });
|
|
const match = findMatchingRule([rule], "VACATION", null, "CHARGEABLE");
|
|
expect(match).not.toBeNull();
|
|
expect(match!.costEffect).toBe("REDUCE");
|
|
});
|
|
|
|
it("does not match wrong orderType", () => {
|
|
const rule = makeRule({ triggerType: "VACATION", orderType: "CHARGEABLE" as never });
|
|
const match = findMatchingRule([rule], "VACATION", null, "INTERNAL");
|
|
expect(match).toBeNull();
|
|
});
|
|
|
|
it("specificity: projectId + orderType > projectId only", () => {
|
|
const projOnly = makeRule({ id: "proj", triggerType: "SICK", projectId: "p1" });
|
|
const both = makeRule({ id: "both", triggerType: "SICK", projectId: "p1", orderType: "CHARGEABLE" as never, costEffect: "REDUCE" });
|
|
const match = findMatchingRule([projOnly, both], "SICK", "p1", "CHARGEABLE");
|
|
expect(match!.rule.id).toBe("both");
|
|
});
|
|
});
|
|
|
|
describe("applyCostEffect", () => {
|
|
it("CHARGE returns full cost", () => {
|
|
expect(applyCostEffect(1000, "CHARGE", null)).toBe(1000);
|
|
});
|
|
|
|
it("ZERO returns 0", () => {
|
|
expect(applyCostEffect(1000, "ZERO", null)).toBe(0);
|
|
});
|
|
|
|
it("REDUCE applies percentage", () => {
|
|
expect(applyCostEffect(1000, "REDUCE", 30)).toBe(700);
|
|
});
|
|
|
|
it("REDUCE with 100% returns 0", () => {
|
|
expect(applyCostEffect(1000, "REDUCE", 100)).toBe(0);
|
|
});
|
|
|
|
it("REDUCE with 0% returns full cost", () => {
|
|
expect(applyCostEffect(1000, "REDUCE", 0)).toBe(1000);
|
|
});
|
|
|
|
it("REDUCE with null percent returns full cost", () => {
|
|
expect(applyCostEffect(1000, "REDUCE", null)).toBe(1000);
|
|
});
|
|
});
|
|
|
|
describe("DEFAULT_CALCULATION_RULES", () => {
|
|
it("provides vacation, sick, and public holiday rules", () => {
|
|
expect(DEFAULT_CALCULATION_RULES).toHaveLength(3);
|
|
const triggers = DEFAULT_CALCULATION_RULES.map((r) => r.triggerType);
|
|
expect(triggers).toContain("VACATION");
|
|
expect(triggers).toContain("SICK");
|
|
expect(triggers).toContain("PUBLIC_HOLIDAY");
|
|
});
|
|
|
|
it("vacation: zero cost, count chargeability", () => {
|
|
const rule = DEFAULT_CALCULATION_RULES.find((r) => r.triggerType === "VACATION")!;
|
|
expect(rule.costEffect).toBe("ZERO");
|
|
expect(rule.chargeabilityEffect).toBe("COUNT");
|
|
});
|
|
|
|
it("sick: zero cost, count chargeability", () => {
|
|
const rule = DEFAULT_CALCULATION_RULES.find((r) => r.triggerType === "SICK")!;
|
|
expect(rule.costEffect).toBe("ZERO");
|
|
expect(rule.chargeabilityEffect).toBe("COUNT");
|
|
});
|
|
|
|
it("public holiday: zero cost, skip chargeability", () => {
|
|
const rule = DEFAULT_CALCULATION_RULES.find((r) => r.triggerType === "PUBLIC_HOLIDAY")!;
|
|
expect(rule.costEffect).toBe("ZERO");
|
|
expect(rule.chargeabilityEffect).toBe("SKIP");
|
|
});
|
|
});
|