fix: 合并后清理重复测试与修复 CEO/财务兜底脚本注入 bug
- apply-ceo-finance-fallback.js: execute() 原先在传入 mock pool 时仍强制要求 DATABASE_URL 环境变量,导致单测无法注入 mock pool 验证事务逻辑。改为仅在 未传入 pool 时才检查 DATABASE_URL。 - portal.test.js: 删除本次合并前遗留的重复用例(期望旧的 portal.feature_disabled reasonCode),候选分支自带的等价用例更完整 (校验 reasonCode 以及全部零写入断言)。 - todos.test.js: 删除本次合并前遗留的文件,候选分支的 todos.errors.test.js 覆盖等价且更完整。
这个提交包含在:
@@ -164,8 +164,11 @@ function countsEqual(left, right) {
|
|||||||
.every((field) => Number(left[field]) === Number(right[field]));
|
.every((field) => Number(left[field]) === Number(right[field]));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execute(options, pool = new Pool({ connectionString: process.env.DATABASE_URL })) {
|
async function execute(options, pool) {
|
||||||
if (!process.env.DATABASE_URL) throw new Error("missing_database_url");
|
if (!pool) {
|
||||||
|
if (!process.env.DATABASE_URL) throw new Error("missing_database_url");
|
||||||
|
pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||||
|
}
|
||||||
if (options.apply && process.env[APPLY_ENV_KEY] !== "1") {
|
if (options.apply && process.env[APPLY_ENV_KEY] !== "1") {
|
||||||
throw new Error(`apply_requires_${APPLY_ENV_KEY}`);
|
throw new Error(`apply_requires_${APPLY_ENV_KEY}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -573,26 +573,4 @@ describe("portal routes", () => {
|
|||||||
await app.close();
|
await app.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns a precise reasonCode and skips business queries when portal home is disabled", async () => {
|
|
||||||
featureFlagState.portalHome = false;
|
|
||||||
const { app, prisma } = await buildApp({});
|
|
||||||
|
|
||||||
const response = await app.inject({
|
|
||||||
method: "GET",
|
|
||||||
url: "/api/portal/home",
|
|
||||||
headers: { authorization: "Bearer test" },
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.statusCode).toBe(409);
|
|
||||||
expect(response.json()).toMatchObject({
|
|
||||||
error: {
|
|
||||||
code: "VALID_001",
|
|
||||||
details: { featureFlag: "portal_home_v1_disabled", reason: "env_hard_off", reasonCode: "portal.feature_disabled" },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(prisma.reimbursementForm.count).not.toHaveBeenCalled();
|
|
||||||
expect(prisma.workflowTask.count).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
const Fastify = require("fastify");
|
|
||||||
const todosRoutes = require("../../../src/routes/todos");
|
|
||||||
const { AppError } = require("../../../src/utils/AppError");
|
|
||||||
|
|
||||||
async function buildApp({ featureEnabled = true } = {}) {
|
|
||||||
const app = Fastify();
|
|
||||||
const prisma = {
|
|
||||||
featureFlag: {
|
|
||||||
findUnique: vi.fn(async () => ({
|
|
||||||
flag_key: "todo_board_v1",
|
|
||||||
override_mode: featureEnabled ? "force_on" : "force_off",
|
|
||||||
rollout_type: "global",
|
|
||||||
department_ids: [],
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
todoItem: {
|
|
||||||
findMany: vi.fn(async () => []),
|
|
||||||
create: vi.fn(),
|
|
||||||
findUnique: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
app.decorate("prisma", prisma);
|
|
||||||
app.decorate("config", { todoBoardEnabled: true });
|
|
||||||
app.decorate("authenticate", async (request) => {
|
|
||||||
request.employee = { id: "employee-1", department_id: "department-1", permissionCodes: [] };
|
|
||||||
});
|
|
||||||
app.decorate("hasPermission", () => false);
|
|
||||||
app.setErrorHandler((error, request, reply) => {
|
|
||||||
if (error instanceof AppError) {
|
|
||||||
return reply.code(error.statusCode).send({ error: { code: error.code, details: error.details ?? {} } });
|
|
||||||
}
|
|
||||||
return reply.code(500).send({ error: { code: "SYS_001" } });
|
|
||||||
});
|
|
||||||
await app.register(todosRoutes, { prefix: "/api" });
|
|
||||||
return { app, prisma };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("todos feature gate", () => {
|
|
||||||
it("returns a precise reasonCode and skips todo reads when the board is disabled", async () => {
|
|
||||||
const { app, prisma } = await buildApp({ featureEnabled: false });
|
|
||||||
try {
|
|
||||||
const getResponse = await app.inject({ method: "GET", url: "/api/todos" });
|
|
||||||
expect(getResponse.statusCode).toBe(409);
|
|
||||||
expect(getResponse.json()).toEqual({
|
|
||||||
error: {
|
|
||||||
code: "VALID_001",
|
|
||||||
details: { featureFlag: "todo_board_v1_disabled", reason: "flag_force_off", reasonCode: "todo.feature_disabled" },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(prisma.todoItem.findMany).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
const postResponse = await app.inject({
|
|
||||||
method: "POST",
|
|
||||||
url: "/api/todos",
|
|
||||||
payload: { title: "合成待办" },
|
|
||||||
});
|
|
||||||
expect(postResponse.statusCode).toBe(409);
|
|
||||||
expect(postResponse.json().error.details.reasonCode).toBe("todo.feature_disabled");
|
|
||||||
expect(prisma.todoItem.create).not.toHaveBeenCalled();
|
|
||||||
} finally {
|
|
||||||
await app.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("lists todos normally without leaking a reasonCode when the board is enabled", async () => {
|
|
||||||
const { app, prisma } = await buildApp({ featureEnabled: true });
|
|
||||||
try {
|
|
||||||
const response = await app.inject({ method: "GET", url: "/api/todos" });
|
|
||||||
expect(response.statusCode).toBe(200);
|
|
||||||
expect(response.json().data).toEqual({ items: [] });
|
|
||||||
expect(prisma.todoItem.findMany).toHaveBeenCalledTimes(1);
|
|
||||||
} finally {
|
|
||||||
await app.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
在新工单中引用
屏蔽一个用户