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
@@ -462,13 +462,14 @@ describe("experienceMultiplier.applyRules", () => {
it("updates demand lines with adjusted rates and creates audit log", async () => {
const estimate = makeEstimate("WORKING");
const multiplierSet = sampleMultiplierSet();
const db = {
const db: Record<string, unknown> = {
estimate: { findUnique: vi.fn().mockResolvedValue(estimate) },
experienceMultiplierSet: { findUnique: vi.fn().mockResolvedValue(multiplierSet) },
estimateDemandLine: {
update: vi.fn().mockResolvedValue({}),
},
auditLog: { create: vi.fn().mockResolvedValue({}) },
$transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(db)),
};
const caller = createManagerCaller(db);
@@ -528,13 +529,14 @@ describe("experienceMultiplier.applyRules", () => {
const estimate = makeEstimate("WORKING");
const multiplierSet = sampleMultiplierSet();
const db = {
const db: Record<string, unknown> = {
estimate: { findUnique: vi.fn().mockResolvedValue(estimate) },
experienceMultiplierSet: { findUnique: vi.fn().mockResolvedValue(multiplierSet) },
estimateDemandLine: {
update: vi.fn(),
},
auditLog: { create: vi.fn().mockResolvedValue({}) },
$transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(db)),
};
const caller = createManagerCaller(db);
@@ -544,7 +546,7 @@ describe("experienceMultiplier.applyRules", () => {
});
expect(result.linesUpdated).toBe(0);
expect(db.estimateDemandLine.update).not.toHaveBeenCalled();
expect((db.estimateDemandLine as Record<string, ReturnType<typeof vi.fn>>).update).not.toHaveBeenCalled();
});
it("rejects applying to a non-WORKING version", async () => {
@@ -613,13 +615,14 @@ describe("experienceMultiplier.applyRules", () => {
});
const estimate = makeEstimate("WORKING", [lineWithMetadata]);
const multiplierSet = sampleMultiplierSet();
const db = {
const db: Record<string, unknown> = {
estimate: { findUnique: vi.fn().mockResolvedValue(estimate) },
experienceMultiplierSet: { findUnique: vi.fn().mockResolvedValue(multiplierSet) },
estimateDemandLine: {
update: vi.fn().mockResolvedValue({}),
},
auditLog: { create: vi.fn().mockResolvedValue({}) },
$transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(db)),
};
const caller = createManagerCaller(db);