文件
hyb-oa/server/tests/unit/routes/leave.working-days.test.js
T

232 行
6.3 KiB
JavaScript

const Fastify = require("fastify");
const { AppError } = require("../../../src/utils/AppError");
const leaveRoutes = require("../../../src/routes/leave");
function buildFlagRecord(overrides = {}) {
return {
id: "flag-1",
flag_key: "leave_holiday_calendar",
override_mode: "force_on",
rollout_type: "global",
department_ids: [],
note: "",
version: 1,
created_at: new Date("2026-04-01T00:00:00.000Z"),
created_by: null,
updated_at: new Date("2026-04-01T00:00:00.000Z"),
updated_by: null,
...overrides,
};
}
function buildCalendarDay(date, dayType) {
return {
date: new Date(`${date}T00:00:00.000Z`),
day_type: dayType,
};
}
function buildLegalEntity(overrides = {}) {
return {
id: "legal-entity-1",
default_holiday_calendar: {
id: "calendar-1",
archived_at: null,
days: [
buildCalendarDay("2026-01-01", "holiday"),
buildCalendarDay("2026-01-02", "holiday"),
buildCalendarDay("2026-01-03", "holiday"),
buildCalendarDay("2026-01-04", "adjusted_workday"),
],
},
...overrides,
};
}
async function buildApp(options = {}) {
const app = Fastify();
const employee = options.employee ?? {
id: "employee-1",
department_id: "department-1",
legal_entity_id: "legal-entity-1",
permissionCodes: [],
};
const featureFlagFindUnique = vi
.fn()
.mockResolvedValue(options.featureFlagRecord ?? buildFlagRecord());
const legalEntityFindUnique = vi
.fn()
.mockResolvedValue(options.legalEntity ?? buildLegalEntity());
app.decorate("prisma", {
featureFlag: {
findUnique: featureFlagFindUnique,
},
legalEntity: {
findUnique: legalEntityFindUnique,
},
});
app.decorate("config", {
cutoverConsoleEnabled: false,
leaveHolidayCalendarEnabled:
options.leaveHolidayCalendarEnabled === undefined
? true
: options.leaveHolidayCalendarEnabled,
});
app.decorate("authenticate", async (request) => {
if (!request.headers.authorization) {
throw new AppError({
code: "AUTH_001",
message: "未登录",
statusCode: 401,
});
}
request.employee = employee;
});
app.decorate("hasPermission", () => false);
app.decorate("requireEntityPermission", () => async (request) => {
await app.authenticate(request);
});
app.setErrorHandler((error, request, reply) => {
if (error instanceof AppError) {
return reply.status(error.statusCode).send({
requestId: request.id,
error: {
code: error.code,
message: error.message,
details: error.details ?? {},
},
});
}
return reply.status(500).send({
requestId: request.id,
error: { code: "SYS_001", message: "服务器内部错误", details: {} },
});
});
await app.register(leaveRoutes, { prefix: "/api" });
return {
app,
featureFlagFindUnique,
legalEntityFindUnique,
};
}
describe("leave working-days route", () => {
it("returns preview fields and counts for ranges that include official holidays", async () => {
const { app } = await buildApp();
const response = await app.inject({
method: "GET",
url: "/api/leave-requests/working-days?startDate=2026-01-01&endDate=2026-01-04&startSession=am&endSession=pm",
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
requestedDays: 1,
naturalRequestedDays: 4,
holidayDayCount: 3,
weekendDayCount: 0,
adjustedWorkdayCount: 1,
calendarApplied: true,
isNonWorkingRange: false,
});
await app.close();
});
it("keeps half-day precision in preview results", async () => {
const { app } = await buildApp();
const response = await app.inject({
method: "GET",
url: "/api/leave-requests/working-days?startDate=2026-01-03&endDate=2026-01-04&startSession=pm&endSession=am",
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
requestedDays: 0.5,
naturalRequestedDays: 1,
holidayDayCount: 0.5,
weekendDayCount: 0,
adjustedWorkdayCount: 0.5,
calendarApplied: true,
isNonWorkingRange: false,
});
await app.close();
});
it("returns zero days instead of error for fully non-working preview ranges", async () => {
const { app } = await buildApp({
legalEntity: buildLegalEntity({
default_holiday_calendar: {
id: "calendar-1",
archived_at: null,
days: [
buildCalendarDay("2026-05-01", "holiday"),
buildCalendarDay("2026-05-02", "holiday"),
buildCalendarDay("2026-05-03", "holiday"),
buildCalendarDay("2026-05-04", "holiday"),
buildCalendarDay("2026-05-05", "holiday"),
],
},
}),
});
const response = await app.inject({
method: "GET",
url: "/api/leave-requests/working-days?startDate=2026-05-01&endDate=2026-05-05&startSession=am&endSession=pm",
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
requestedDays: 0,
naturalRequestedDays: 5,
holidayDayCount: 5,
weekendDayCount: 0,
adjustedWorkdayCount: 0,
calendarApplied: true,
isNonWorkingRange: true,
});
await app.close();
});
it("falls back to natural days when the holiday-calendar feature is disabled", async () => {
const { app, featureFlagFindUnique, legalEntityFindUnique } = await buildApp({
leaveHolidayCalendarEnabled: false,
});
const response = await app.inject({
method: "GET",
url: "/api/leave-requests/working-days?startDate=2026-01-01&endDate=2026-01-04&startSession=am&endSession=pm",
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(200);
expect(response.json().data).toEqual({
requestedDays: 4,
naturalRequestedDays: 4,
holidayDayCount: 0,
weekendDayCount: 0,
adjustedWorkdayCount: 0,
calendarApplied: false,
isNonWorkingRange: false,
});
expect(featureFlagFindUnique).not.toHaveBeenCalled();
expect(legalEntityFindUnique).not.toHaveBeenCalled();
await app.close();
});
});