a83edb2f9d
Redesigned timeline project and resource panels with expanded detail views, added quick filter toolbar, improved drag handling, and enhanced vacation/entitlement router logic. Includes e2e test updates and minor API fixes. Co-Authored-By: claude-flow <ruv@ruv.net>
218 lines
5.5 KiB
TypeScript
218 lines
5.5 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { listAssignmentBookings } from "../index.js";
|
|
|
|
describe("listAssignmentBookings", () => {
|
|
it("returns assignments with cost metadata", async () => {
|
|
const db = {
|
|
assignment: {
|
|
findMany: vi.fn().mockResolvedValue([
|
|
{
|
|
id: "asn_1",
|
|
projectId: "proj_1",
|
|
resourceId: "res_1",
|
|
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-02T00:00:00.000Z"),
|
|
hoursPerDay: 6,
|
|
dailyCostCents: 900,
|
|
status: "CONFIRMED",
|
|
project: {
|
|
id: "proj_1",
|
|
name: "Alpha",
|
|
shortCode: "ALPHA",
|
|
status: "ACTIVE",
|
|
orderType: "CHARGEABLE",
|
|
dynamicFields: null,
|
|
},
|
|
resource: {
|
|
id: "res_1",
|
|
displayName: "Alice",
|
|
chapter: "CGI",
|
|
},
|
|
},
|
|
{
|
|
id: "asn_2",
|
|
projectId: "proj_2",
|
|
resourceId: "res_2",
|
|
startDate: new Date("2026-03-03T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-03T00:00:00.000Z"),
|
|
hoursPerDay: 4,
|
|
dailyCostCents: 500,
|
|
status: "PROPOSED",
|
|
project: {
|
|
id: "proj_2",
|
|
name: "Bravo",
|
|
shortCode: "BRAVO",
|
|
status: "DRAFT",
|
|
orderType: "INTERNAL",
|
|
dynamicFields: null,
|
|
},
|
|
resource: {
|
|
id: "res_2",
|
|
displayName: "Bob",
|
|
chapter: "Lighting",
|
|
},
|
|
},
|
|
]),
|
|
},
|
|
};
|
|
|
|
const result = await listAssignmentBookings(db as never, {
|
|
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-31T00:00:00.000Z"),
|
|
});
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
id: "asn_1",
|
|
projectId: "proj_1",
|
|
resourceId: "res_1",
|
|
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-02T00:00:00.000Z"),
|
|
hoursPerDay: 6,
|
|
dailyCostCents: 900,
|
|
status: "CONFIRMED",
|
|
project: {
|
|
id: "proj_1",
|
|
name: "Alpha",
|
|
shortCode: "ALPHA",
|
|
status: "ACTIVE",
|
|
orderType: "CHARGEABLE",
|
|
dynamicFields: null,
|
|
},
|
|
resource: {
|
|
id: "res_1",
|
|
displayName: "Alice",
|
|
chapter: "CGI",
|
|
},
|
|
},
|
|
{
|
|
id: "asn_2",
|
|
projectId: "proj_2",
|
|
resourceId: "res_2",
|
|
startDate: new Date("2026-03-03T00:00:00.000Z"),
|
|
endDate: new Date("2026-03-03T00:00:00.000Z"),
|
|
hoursPerDay: 4,
|
|
dailyCostCents: 500,
|
|
status: "PROPOSED",
|
|
project: {
|
|
id: "proj_2",
|
|
name: "Bravo",
|
|
shortCode: "BRAVO",
|
|
status: "DRAFT",
|
|
orderType: "INTERNAL",
|
|
dynamicFields: null,
|
|
},
|
|
resource: {
|
|
id: "res_2",
|
|
displayName: "Bob",
|
|
chapter: "Lighting",
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("supports unbounded resource context queries when no date window is provided", async () => {
|
|
const db = {
|
|
assignment: {
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
},
|
|
};
|
|
|
|
await listAssignmentBookings(db as never, {
|
|
resourceIds: ["res_1", "res_2"],
|
|
});
|
|
|
|
expect(db.assignment.findMany).toHaveBeenCalledWith({
|
|
where: {
|
|
status: { not: "CANCELLED" },
|
|
resourceId: { in: ["res_1", "res_2"] },
|
|
},
|
|
select: {
|
|
id: true,
|
|
projectId: true,
|
|
resourceId: true,
|
|
startDate: true,
|
|
endDate: true,
|
|
hoursPerDay: true,
|
|
dailyCostCents: true,
|
|
status: true,
|
|
project: {
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
shortCode: true,
|
|
status: true,
|
|
orderType: true,
|
|
clientId: true,
|
|
dynamicFields: true,
|
|
},
|
|
},
|
|
resource: {
|
|
select: { id: true, displayName: true, chapter: true },
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it("rejects partial date bounds", async () => {
|
|
const db = {
|
|
assignment: {
|
|
findMany: vi.fn(),
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
listAssignmentBookings(db as never, {
|
|
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
|
}),
|
|
).rejects.toThrow("startDate and endDate must be provided together");
|
|
|
|
expect(db.assignment.findMany).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("supports excluding explicit assignment ids", async () => {
|
|
const db = {
|
|
assignment: {
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
},
|
|
};
|
|
|
|
await listAssignmentBookings(db as never, {
|
|
resourceIds: ["res_1"],
|
|
excludeAssignmentIds: ["asn_1"],
|
|
});
|
|
|
|
expect(db.assignment.findMany).toHaveBeenCalledWith({
|
|
where: {
|
|
status: { not: "CANCELLED" },
|
|
resourceId: { in: ["res_1"] },
|
|
id: { notIn: ["asn_1"] },
|
|
},
|
|
select: {
|
|
id: true,
|
|
projectId: true,
|
|
resourceId: true,
|
|
startDate: true,
|
|
endDate: true,
|
|
hoursPerDay: true,
|
|
dailyCostCents: true,
|
|
status: true,
|
|
project: {
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
shortCode: true,
|
|
status: true,
|
|
orderType: true,
|
|
clientId: true,
|
|
dynamicFields: true,
|
|
},
|
|
},
|
|
resource: {
|
|
select: { id: true, displayName: true, chapter: true },
|
|
},
|
|
},
|
|
});
|
|
});
|
|
});
|