232 行
6.3 KiB
JavaScript
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();
|
|
});
|
|
});
|