b41c1d2501
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { AllocationStatus, type AllocationWithDetails } from "@nexus/shared";
|
|
import { buildAllocationReadModel } from "../index.js";
|
|
|
|
function makeAllocation(overrides: Partial<AllocationWithDetails>): AllocationWithDetails {
|
|
return {
|
|
id: "alloc_1",
|
|
resourceId: "res_1",
|
|
projectId: "proj_1",
|
|
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-05T00:00:00.000Z"),
|
|
hoursPerDay: 8,
|
|
percentage: 100,
|
|
role: "Compositor",
|
|
roleId: "role_1",
|
|
isPlaceholder: false,
|
|
headcount: 1,
|
|
dailyCostCents: 12_000,
|
|
status: AllocationStatus.PROPOSED,
|
|
metadata: {},
|
|
createdAt: new Date("2026-03-01T00:00:00.000Z"),
|
|
updatedAt: new Date("2026-03-01T00:00:00.000Z"),
|
|
resource: {
|
|
id: "res_1",
|
|
displayName: "Alice",
|
|
eid: "E-001",
|
|
lcrCents: 15_000,
|
|
},
|
|
project: {
|
|
id: "proj_1",
|
|
name: "Project One",
|
|
shortCode: "P1",
|
|
status: "ACTIVE",
|
|
endDate: new Date("2026-03-31T00:00:00.000Z"),
|
|
},
|
|
roleEntity: {
|
|
id: "role_1",
|
|
name: "Compositor",
|
|
color: "#111111",
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("allocation read model", () => {
|
|
it("splits assignments from placeholder demand rows", () => {
|
|
const input: AllocationWithDetails[] = [
|
|
makeAllocation({ id: "assign_1" }),
|
|
makeAllocation({
|
|
id: "demand_1",
|
|
resourceId: null,
|
|
isPlaceholder: true,
|
|
headcount: 3,
|
|
dailyCostCents: 0,
|
|
resource: null,
|
|
}),
|
|
];
|
|
|
|
const result = buildAllocationReadModel(input);
|
|
|
|
expect(result.allocations).toHaveLength(2);
|
|
expect(result.assignments).toHaveLength(1);
|
|
expect(result.demands).toHaveLength(1);
|
|
expect(result.assignments[0]).toMatchObject({
|
|
id: "assign_1",
|
|
kind: "assignment",
|
|
sourceAllocationId: "assign_1",
|
|
resourceId: "res_1",
|
|
isPlaceholder: false,
|
|
});
|
|
expect(result.demands[0]).toMatchObject({
|
|
id: "demand_1",
|
|
kind: "demand",
|
|
sourceAllocationId: "demand_1",
|
|
resourceId: null,
|
|
isPlaceholder: true,
|
|
requestedHeadcount: 3,
|
|
unfilledHeadcount: 3,
|
|
});
|
|
});
|
|
|
|
it("treats null-resource rows as demand for legacy safety", () => {
|
|
const input: AllocationWithDetails[] = [
|
|
makeAllocation({
|
|
id: "legacy_1",
|
|
resourceId: null,
|
|
isPlaceholder: false,
|
|
headcount: 2,
|
|
dailyCostCents: 0,
|
|
resource: null,
|
|
}),
|
|
];
|
|
|
|
const result = buildAllocationReadModel(input);
|
|
|
|
expect(result.assignments).toEqual([]);
|
|
expect(result.demands).toHaveLength(1);
|
|
expect(result.demands[0]?.sourceAllocationId).toBe("legacy_1");
|
|
});
|
|
});
|