Files
Nexus/packages/engine/src/__tests__/rules-engine.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

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");
});
});