test(api): widen resource capacity edge coverage
This commit is contained in:
@@ -261,6 +261,118 @@ describe("resource router", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an empty utilization result when no active resources match the chapter filter", async () => {
|
||||
const db = {
|
||||
resource: {
|
||||
findMany: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mocked(listAssignmentBookings).mockResolvedValue([]);
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
const result = await caller.listWithUtilization({
|
||||
chapter: "Rigging",
|
||||
startDate: "2026-03-01T00:00:00.000Z",
|
||||
endDate: "2026-03-31T00:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(db.resource.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: {
|
||||
isActive: true,
|
||||
chapter: "Rigging",
|
||||
},
|
||||
take: 100,
|
||||
orderBy: { displayName: "asc" },
|
||||
}));
|
||||
expect(listAssignmentBookings).toHaveBeenCalledWith(db, {
|
||||
startDate: new Date("2026-03-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-03-31T00:00:00.000Z"),
|
||||
resourceIds: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores orphaned utilization bookings without a resourceId", async () => {
|
||||
const resource = {
|
||||
id: "resource_1",
|
||||
eid: "E-001",
|
||||
displayName: "Alice",
|
||||
email: "alice@example.com",
|
||||
chapter: "CGI",
|
||||
lcrCents: 5000,
|
||||
ucrCents: 9000,
|
||||
currency: "EUR",
|
||||
chargeabilityTarget: 80,
|
||||
availability: {
|
||||
monday: 8,
|
||||
tuesday: 8,
|
||||
wednesday: 8,
|
||||
thursday: 8,
|
||||
friday: 8,
|
||||
},
|
||||
skills: [],
|
||||
dynamicFields: {},
|
||||
blueprintId: null,
|
||||
isActive: true,
|
||||
createdAt: new Date("2026-03-01"),
|
||||
updatedAt: new Date("2026-03-01"),
|
||||
roleId: null,
|
||||
portfolioUrl: null,
|
||||
postalCode: null,
|
||||
federalState: null,
|
||||
valueScore: null,
|
||||
valueScoreBreakdown: null,
|
||||
valueScoreUpdatedAt: null,
|
||||
userId: null,
|
||||
countryId: "country_de",
|
||||
metroCityId: null,
|
||||
country: { code: "DE" },
|
||||
metroCity: null,
|
||||
};
|
||||
const db = {
|
||||
resource: {
|
||||
findMany: vi.fn().mockResolvedValue([resource]),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mocked(listAssignmentBookings).mockResolvedValue([
|
||||
{
|
||||
id: "assignment_orphaned",
|
||||
projectId: "project_1",
|
||||
resourceId: null,
|
||||
startDate: new Date("2026-03-02T00:00:00.000Z"),
|
||||
endDate: new Date("2026-03-06T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
dailyCostCents: 0,
|
||||
status: "CONFIRMED",
|
||||
project: {
|
||||
id: "project_1",
|
||||
name: "Project 1",
|
||||
shortCode: "P1",
|
||||
status: "ACTIVE",
|
||||
orderType: "CLIENT",
|
||||
clientId: null,
|
||||
dynamicFields: null,
|
||||
},
|
||||
resource: null,
|
||||
},
|
||||
] satisfies Awaited<ReturnType<typeof listAssignmentBookings>>);
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
const result = await caller.listWithUtilization({
|
||||
startDate: "2026-03-02T00:00:00.000Z",
|
||||
endDate: "2026-03-08T00:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
id: "resource_1",
|
||||
bookingCount: 0,
|
||||
bookedHours: 0,
|
||||
utilizationPercent: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("shifts marketplace availability when a local holiday blocks today", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-01-06T10:00:00.000Z"));
|
||||
@@ -581,6 +693,190 @@ describe("resource router", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("returns zero chargeability when a resource has no working availability", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-15T12:00:00.000Z"));
|
||||
|
||||
try {
|
||||
const db = {
|
||||
resource: {
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{
|
||||
id: "resource_zero",
|
||||
eid: "E-000",
|
||||
displayName: "No Capacity",
|
||||
chapter: "CGI",
|
||||
chargeabilityTarget: 80,
|
||||
availability: {
|
||||
monday: 0,
|
||||
tuesday: 0,
|
||||
wednesday: 0,
|
||||
thursday: 0,
|
||||
friday: 0,
|
||||
saturday: 0,
|
||||
sunday: 0,
|
||||
},
|
||||
countryId: "country_de",
|
||||
federalState: null,
|
||||
metroCityId: null,
|
||||
country: { code: "DE" },
|
||||
metroCity: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mocked(listAssignmentBookings).mockResolvedValue([
|
||||
{
|
||||
id: "assignment_zero",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_zero",
|
||||
startDate: new Date("2026-03-02T00:00:00.000Z"),
|
||||
endDate: new Date("2026-03-06T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
dailyCostCents: 0,
|
||||
status: "CONFIRMED",
|
||||
project: {
|
||||
id: "project_1",
|
||||
name: "Project 1",
|
||||
shortCode: "P1",
|
||||
status: "ACTIVE",
|
||||
orderType: "CLIENT",
|
||||
dynamicFields: null,
|
||||
},
|
||||
resource: { id: "resource_zero", displayName: "No Capacity", chapter: "CGI" },
|
||||
},
|
||||
]);
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
const result = await caller.getChargeabilityStats({});
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
id: "resource_zero",
|
||||
actualChargeability: 0,
|
||||
expectedChargeability: 0,
|
||||
availableHours: 0,
|
||||
});
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("returns an empty chargeability stats result when no active resources match", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-15T12:00:00.000Z"));
|
||||
|
||||
try {
|
||||
const db = {
|
||||
resource: {
|
||||
findMany: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mocked(listAssignmentBookings).mockResolvedValue([]);
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
const result = await caller.getChargeabilityStats({ resourceId: "missing_resource" });
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(db.resource.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
isActive: true,
|
||||
id: "missing_resource",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
eid: true,
|
||||
displayName: true,
|
||||
chapter: true,
|
||||
chargeabilityTarget: true,
|
||||
availability: true,
|
||||
countryId: true,
|
||||
federalState: true,
|
||||
metroCityId: true,
|
||||
country: { select: { code: true } },
|
||||
metroCity: { select: { name: true } },
|
||||
},
|
||||
});
|
||||
expect(listAssignmentBookings).toHaveBeenCalledWith(db, expect.objectContaining({
|
||||
resourceIds: [],
|
||||
}));
|
||||
|
||||
const [, bookingQuery] = vi.mocked(listAssignmentBookings).mock.calls.at(-1)!;
|
||||
expect(bookingQuery.startDate).toEqual(new Date(2026, 2, 1));
|
||||
expect(bookingQuery.endDate).toEqual(new Date(2026, 3, 0));
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("passes the explicit resource filter through to chargeability stats reads", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-15T12:00:00.000Z"));
|
||||
|
||||
try {
|
||||
const db = {
|
||||
resource: {
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{
|
||||
id: "resource_target",
|
||||
eid: "E-TARGET",
|
||||
displayName: "Target",
|
||||
chapter: "CGI",
|
||||
chargeabilityTarget: 80,
|
||||
availability: {
|
||||
monday: 8,
|
||||
tuesday: 8,
|
||||
wednesday: 8,
|
||||
thursday: 8,
|
||||
friday: 8,
|
||||
},
|
||||
countryId: "country_de",
|
||||
federalState: null,
|
||||
metroCityId: null,
|
||||
country: { code: "DE" },
|
||||
metroCity: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mocked(listAssignmentBookings).mockResolvedValue([]);
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
await caller.getChargeabilityStats({ resourceId: "resource_target" });
|
||||
|
||||
expect(db.resource.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
isActive: true,
|
||||
id: "resource_target",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
eid: true,
|
||||
displayName: true,
|
||||
chapter: true,
|
||||
chargeabilityTarget: true,
|
||||
availability: true,
|
||||
countryId: true,
|
||||
federalState: true,
|
||||
metroCityId: true,
|
||||
country: { select: { code: true } },
|
||||
metroCity: { select: { name: true } },
|
||||
},
|
||||
});
|
||||
expect(listAssignmentBookings).toHaveBeenCalledWith(db, expect.objectContaining({
|
||||
resourceIds: ["resource_target"],
|
||||
}));
|
||||
|
||||
const [, bookingQuery] = vi.mocked(listAssignmentBookings).mock.calls.at(-1)!;
|
||||
expect(bookingQuery.startDate).toEqual(new Date(2026, 2, 1));
|
||||
expect(bookingQuery.endDate).toEqual(new Date(2026, 3, 0));
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("returns a holiday-aware chargeability summary readmodel", async () => {
|
||||
const resourceRecord = {
|
||||
id: "res_1",
|
||||
|
||||
Reference in New Issue
Block a user