完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)
这个提交包含在:
@@ -1,6 +1,7 @@
|
||||
const Fastify = require("fastify");
|
||||
|
||||
const employeesRoutes = require("../../../src/routes/employees");
|
||||
const { resolvePermissionsForRoles } = require("../../../src/constants/permissions");
|
||||
const { AppError } = require("../../../src/utils/AppError");
|
||||
|
||||
async function buildApp({ permissionCodes = ["system.users"], stats } = {}) {
|
||||
@@ -202,9 +203,54 @@ describe("employees stats route", () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns salary and performance summary for system settings viewers", async () => {
|
||||
const { app, payslipAggregate } = await buildApp({
|
||||
permissionCodes: ["system.users", "system.settings"],
|
||||
it("does not expose sensitive summaries to system administrators with ordinary stats access", async () => {
|
||||
const {
|
||||
app,
|
||||
performanceCycleFindFirst,
|
||||
performanceReviewAggregate,
|
||||
performanceReviewGroupBy,
|
||||
payslipAggregate,
|
||||
} = await buildApp({
|
||||
permissionCodes: [
|
||||
...resolvePermissionsForRoles(["system_admin"]),
|
||||
"report.dashboard.view",
|
||||
],
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "GET",
|
||||
url: "/api/employees/stats",
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json().data.salarySummary).toBeNull();
|
||||
expect(response.json().data.performanceSummary).toEqual({
|
||||
cycle: null,
|
||||
averageScore: null,
|
||||
reviewedCount: 0,
|
||||
distribution: [],
|
||||
});
|
||||
expect(performanceCycleFindFirst).not.toHaveBeenCalled();
|
||||
expect(performanceReviewAggregate).not.toHaveBeenCalled();
|
||||
expect(performanceReviewGroupBy).not.toHaveBeenCalled();
|
||||
expect(payslipAggregate).not.toHaveBeenCalled();
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns salary and performance summaries with explicit sensitive permissions", async () => {
|
||||
const {
|
||||
app,
|
||||
performanceCycleFindFirst,
|
||||
performanceReviewAggregate,
|
||||
performanceReviewGroupBy,
|
||||
payslipAggregate,
|
||||
} = await buildApp({
|
||||
permissionCodes: [
|
||||
"report.dashboard.view",
|
||||
"payroll.manage",
|
||||
"system.users_sensitive",
|
||||
],
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
@@ -231,6 +277,9 @@ describe("employees stats route", () => {
|
||||
id: "cycle-active",
|
||||
name: "2026 Q2",
|
||||
});
|
||||
expect(performanceCycleFindFirst).toHaveBeenCalledTimes(2);
|
||||
expect(performanceReviewAggregate).toHaveBeenCalledTimes(1);
|
||||
expect(performanceReviewGroupBy).toHaveBeenCalledTimes(1);
|
||||
expect(payslipAggregate).toHaveBeenCalledTimes(2);
|
||||
|
||||
await app.close();
|
||||
|
||||
在新工单中引用
屏蔽一个用户