fix(api): wrap audit log writes inside their parent transactions

Prevents mutations from committing without an audit trail if the
auditLog.create call fails after the main write already succeeded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 16:40:10 +02:00
parent a01f99561d
commit 3c0179fcec
25 changed files with 758 additions and 656 deletions
@@ -143,15 +143,14 @@ describe("role router authorization", () => {
// planningEntry count queries (attachZeroAllocationCount path)
const planningEntryFindMany = vi.fn().mockResolvedValue([]);
const db: Record<string, unknown> = {
role: { create: roleCreate, findUnique: roleFindUnique },
auditLog: { create: auditLogCreate },
planningEntry: { findMany: planningEntryFindMany },
$transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(db)),
};
const caller = createCaller(
createContext(
{
role: { create: roleCreate, findUnique: roleFindUnique },
auditLog: { create: auditLogCreate },
planningEntry: { findMany: planningEntryFindMany },
},
{ role: SystemRole.MANAGER },
),
createContext(db, { role: SystemRole.MANAGER }),
);
// Should not throw UNAUTHORIZED or FORBIDDEN
@@ -180,19 +179,18 @@ describe("role router authorization", () => {
const demandRequirementFindMany = vi.fn().mockResolvedValue([]);
const assignmentFindMany = vi.fn().mockResolvedValue([]);
const adminDb: Record<string, unknown> = {
role: {
findUnique: roleFindUnique,
delete: roleDelete,
},
auditLog: { create: auditLogCreate },
demandRequirement: { findMany: demandRequirementFindMany },
assignment: { findMany: assignmentFindMany },
$transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(adminDb)),
};
const caller = createCaller(
createContext(
{
role: {
findUnique: roleFindUnique,
delete: roleDelete,
},
auditLog: { create: auditLogCreate },
demandRequirement: { findMany: demandRequirementFindMany },
assignment: { findMany: assignmentFindMany },
},
{ role: SystemRole.ADMIN },
),
createContext(adminDb, { role: SystemRole.ADMIN }),
);
const result = await caller.delete({ id: "role_1" });