修复生产回填审计标识(07-28-ceo-finance-fallback)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-07-28 14:25:03 +08:00
父节点 35948729cf
当前提交 a74cba1112
共修改 3 个文件,包含 89 行新增和 4 行删除
@@ -215,12 +215,12 @@ async function execute(options, pool = new Pool({ connectionString: process.env.
await client.query(
`
INSERT INTO audit_logs
(request_id, actor_id, action, target_type, target_id, ip_address, user_agent, extra)
(id, request_id, actor_id, action, target_type, target_id, ip_address, user_agent, extra)
VALUES
($1, $2::uuid, 'APPROVAL_RULE_UPDATED', 'approval_rule', $3::uuid, NULL,
'controlled-script/07-28-ceo-finance-fallback', $4::jsonb)
($1::uuid, $2, $3::uuid, 'APPROVAL_RULE_UPDATED', 'approval_rule', $4::uuid, NULL,
'controlled-script/07-28-ceo-finance-fallback', $5::jsonb)
`,
[requestId, admins[0].id, item.id, JSON.stringify({
[crypto.randomUUID(), requestId, admins[0].id, item.id, JSON.stringify({
task: "07-28-ceo-finance-fallback",
band: item.band,
before: item.before,
@@ -298,6 +298,7 @@ module.exports = {
TARGET_BY_BAND,
assertPeopleBaseline,
buildRulePlan,
execute,
getBand,
parseArgs,
};
@@ -1,6 +1,7 @@
const {
assertPeopleBaseline,
buildRulePlan,
execute,
parseArgs,
} = require("../../../src/scripts/apply-ceo-finance-fallback");
@@ -100,4 +101,83 @@ describe("CEO finance fallback production apply contract", () => {
expect(parseArgs(["--apply", "--format=json"])).toEqual({ apply: true, format: "json" });
expect(() => parseArgs(["--force"])).toThrow("Unsupported argument");
});
it("supplies an explicit UUID for each raw audit-log insert", async () => {
const ruleIds = [
"11111111-1111-4111-8111-111111111111",
"22222222-2222-4222-8222-222222222222",
"33333333-3333-4333-8333-333333333333",
];
const sourceRows = currentRows().map((row, index) => ({ ...row, id: ruleIds[index] }));
const targetRows = sourceRows.map((row) => ({
...row,
level2_role: row.id === ruleIds[2] ? "ceo" : "finance",
level3_role: row.id === ruleIds[2] ? "finance" : null,
owner_self_fallback_role: "finance",
}));
const adminId = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
const auditCalls = [];
const transactionCommands = [];
let ruleSelectCount = 0;
let invariantCount = 0;
const client = {
query: vi.fn(async (sql, params = []) => {
const normalized = String(sql).replace(/\s+/g, " ").trim();
if (normalized.startsWith("BEGIN ") || normalized.startsWith("ROLLBACK")) {
transactionCommands.push(normalized);
return {};
}
if (normalized.includes("FROM approval_rules")) {
ruleSelectCount += 1;
return { rows: ruleSelectCount === 1 ? sourceRows : targetRows };
}
if (normalized.includes("JOIN employee_roles selected_role")) {
if (params[0] === "ceo") {
return { rows: [{ id: "ceo-id", direct_manager_id: null, has_wecom_binding: true, roles: ["ceo"] }] };
}
if (params[0] === "finance") {
return { rows: [{ id: "finance-id", direct_manager_id: null, has_wecom_binding: true, roles: ["finance"] }] };
}
return { rows: [{ id: adminId, direct_manager_id: null, has_wecom_binding: false, roles: ["admin"] }] };
}
if (normalized.includes("AS active_employees")) {
invariantCount += 1;
return { rows: [{ active_employees: 6, active_role_rows: 8, active_bindings: 6, workflow_snapshots: 0 }] };
}
if (normalized.startsWith("UPDATE approval_rules")) return { rowCount: 1, rows: [{ id: params[0] }] };
if (normalized.startsWith("INSERT INTO audit_logs")) {
auditCalls.push({ sql: normalized, params });
return { rowCount: 1 };
}
throw new Error(`unexpected query: ${normalized}`);
}),
release: vi.fn(),
};
const pool = { connect: vi.fn(async () => client), end: vi.fn(async () => {}) };
await expect(execute({ apply: false, format: "json" }, pool)).resolves.toMatchObject({
status: "DRY_RUN_PASS",
rules: 3,
auditRows: 0,
});
expect(invariantCount).toBe(2);
expect(transactionCommands).toEqual([
"BEGIN ISOLATION LEVEL SERIALIZABLE",
"ROLLBACK",
]);
expect(auditCalls).toHaveLength(3);
for (const [index, call] of auditCalls.entries()) {
expect(call.sql).toContain("(id, request_id, actor_id");
expect(call.sql).toContain("VALUES ($1::uuid, $2, $3::uuid");
expect(call.params[0]).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
expect(call.params[1]).toMatch(/^ceo-finance-fallback-[0-9a-f-]+$/i);
expect(call.params[2]).toBe(adminId);
expect(call.params[3]).toBe(ruleIds[index]);
expect(JSON.parse(call.params[4])).toMatchObject({
task: "07-28-ceo-finance-fallback",
band: ["small", "medium", "large"][index],
});
expect(call.params).toHaveLength(5);
}
});
});