235 行
6.7 KiB
JavaScript
235 行
6.7 KiB
JavaScript
const {
|
|
calculateRequestedDays,
|
|
calculateRequestedDaysByWorkingCalendar,
|
|
calculateRequestedDaysPreviewByWorkingCalendar,
|
|
leaveRangesOverlap,
|
|
summarizeLeaveLedgerEntries,
|
|
} = require("../../../src/services/leave");
|
|
|
|
describe("Leave Service", () => {
|
|
describe("calculateRequestedDays", () => {
|
|
it("supports same-day half-day requests", () => {
|
|
expect(
|
|
calculateRequestedDays({
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-10",
|
|
startSession: "am",
|
|
endSession: "am",
|
|
}),
|
|
).toBe(0.5);
|
|
|
|
expect(
|
|
calculateRequestedDays({
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-10",
|
|
startSession: "pm",
|
|
endSession: "pm",
|
|
}),
|
|
).toBe(0.5);
|
|
});
|
|
|
|
it("supports cross-day half-day boundaries", () => {
|
|
expect(
|
|
calculateRequestedDays({
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-12",
|
|
startSession: "pm",
|
|
endSession: "am",
|
|
}),
|
|
).toBe(2);
|
|
});
|
|
|
|
it("rejects invalid session ranges", () => {
|
|
expect(() =>
|
|
calculateRequestedDays({
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-10",
|
|
startSession: "pm",
|
|
endSession: "am",
|
|
}),
|
|
).toThrow(/参数校验失败/);
|
|
});
|
|
});
|
|
|
|
describe("calculateRequestedDaysByWorkingCalendar", () => {
|
|
it("skips weekend days by default and respects adjusted workdays", () => {
|
|
expect(
|
|
calculateRequestedDaysByWorkingCalendar({
|
|
startDate: "2026-03-20",
|
|
endDate: "2026-03-22",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
calendarDays: [],
|
|
}),
|
|
).toBe(1);
|
|
|
|
expect(
|
|
calculateRequestedDaysByWorkingCalendar({
|
|
startDate: "2026-03-20",
|
|
endDate: "2026-03-22",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
calendarDays: [{ date: "2026-03-21", day_type: "adjusted_workday" }],
|
|
}),
|
|
).toBe(2);
|
|
});
|
|
|
|
it("skips configured holidays inside a weekday span", () => {
|
|
expect(
|
|
calculateRequestedDaysByWorkingCalendar({
|
|
startDate: "2026-03-16",
|
|
endDate: "2026-03-17",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
calendarDays: [{ date: "2026-03-17", day_type: "holiday" }],
|
|
}),
|
|
).toBe(1);
|
|
});
|
|
|
|
it("rejects ranges that become fully non-working after calendar adjustment", () => {
|
|
expect(() =>
|
|
calculateRequestedDaysByWorkingCalendar({
|
|
startDate: "2026-03-21",
|
|
endDate: "2026-03-22",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
calendarDays: [],
|
|
}),
|
|
).toThrow(/参数校验失败/);
|
|
});
|
|
});
|
|
|
|
describe("calculateRequestedDaysPreviewByWorkingCalendar", () => {
|
|
it("returns official holiday counts while excluding holidays from requested days", () => {
|
|
expect(
|
|
calculateRequestedDaysPreviewByWorkingCalendar({
|
|
startDate: "2026-01-01",
|
|
endDate: "2026-01-04",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
calendarDays: [
|
|
{ date: "2026-01-01", day_type: "holiday" },
|
|
{ date: "2026-01-02", day_type: "holiday" },
|
|
{ date: "2026-01-03", day_type: "holiday" },
|
|
{ date: "2026-01-04", day_type: "adjusted_workday" },
|
|
],
|
|
}),
|
|
).toEqual({
|
|
requestedDays: 1,
|
|
naturalRequestedDays: 4,
|
|
holidayDayCount: 3,
|
|
weekendDayCount: 0,
|
|
adjustedWorkdayCount: 1,
|
|
calendarApplied: true,
|
|
isNonWorkingRange: false,
|
|
});
|
|
});
|
|
|
|
it("keeps half-day precision for adjusted workdays in preview payload", () => {
|
|
expect(
|
|
calculateRequestedDaysPreviewByWorkingCalendar({
|
|
startDate: "2026-01-03",
|
|
endDate: "2026-01-04",
|
|
startSession: "pm",
|
|
endSession: "am",
|
|
calendarDays: [
|
|
{ date: "2026-01-03", day_type: "holiday" },
|
|
{ date: "2026-01-04", day_type: "adjusted_workday" },
|
|
],
|
|
}),
|
|
).toEqual({
|
|
requestedDays: 0.5,
|
|
naturalRequestedDays: 1,
|
|
holidayDayCount: 0.5,
|
|
weekendDayCount: 0,
|
|
adjustedWorkdayCount: 0.5,
|
|
calendarApplied: true,
|
|
isNonWorkingRange: false,
|
|
});
|
|
});
|
|
|
|
it("returns zero-day preview for fully non-working ranges when explicitly allowed", () => {
|
|
expect(
|
|
calculateRequestedDaysPreviewByWorkingCalendar({
|
|
startDate: "2026-05-01",
|
|
endDate: "2026-05-05",
|
|
startSession: "am",
|
|
endSession: "pm",
|
|
allowNonWorkingRange: true,
|
|
calendarDays: [
|
|
{ date: "2026-05-01", day_type: "holiday" },
|
|
{ date: "2026-05-02", day_type: "holiday" },
|
|
{ date: "2026-05-03", day_type: "holiday" },
|
|
{ date: "2026-05-04", day_type: "holiday" },
|
|
{ date: "2026-05-05", day_type: "holiday" },
|
|
],
|
|
}),
|
|
).toEqual({
|
|
requestedDays: 0,
|
|
naturalRequestedDays: 5,
|
|
holidayDayCount: 5,
|
|
weekendDayCount: 0,
|
|
adjustedWorkdayCount: 0,
|
|
calendarApplied: true,
|
|
isNonWorkingRange: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("leaveRangesOverlap", () => {
|
|
it("detects overlapping half-day ranges", () => {
|
|
expect(
|
|
leaveRangesOverlap(
|
|
{
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-11",
|
|
startSession: "pm",
|
|
endSession: "pm",
|
|
},
|
|
{
|
|
startDate: "2026-03-11",
|
|
endDate: "2026-03-12",
|
|
startSession: "am",
|
|
endSession: "am",
|
|
},
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("does not flag non-overlapping adjacent ranges", () => {
|
|
expect(
|
|
leaveRangesOverlap(
|
|
{
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-10",
|
|
startSession: "am",
|
|
endSession: "am",
|
|
},
|
|
{
|
|
startDate: "2026-03-10",
|
|
endDate: "2026-03-10",
|
|
startSession: "pm",
|
|
endSession: "pm",
|
|
},
|
|
),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("summarizeLeaveLedgerEntries", () => {
|
|
it("calculates reserve / consume / release balances correctly", () => {
|
|
const summary = summarizeLeaveLedgerEntries([
|
|
{ entry_type: "grant", days: 10 },
|
|
{ entry_type: "reserve", days: 2.5 },
|
|
{ entry_type: "release", days: 1 },
|
|
{ entry_type: "consume", days: 1.5 },
|
|
]);
|
|
|
|
expect(summary.granted).toBe(10);
|
|
expect(summary.reserved).toBe(1.5);
|
|
expect(summary.used).toBe(1.5);
|
|
expect(summary.available).toBe(7);
|
|
});
|
|
});
|
|
});
|