fix(housekeeping): preserve audit failure evidence
This commit is contained in:
1 parent
89dec9da05
commit
21cd88ccfd
4 files changed
+80
-5
No files matched your search
@@ -96,4 +96,37 @@ describe("audit command envelope", () => {
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
it("preserves the original operation error when failure evidence rejects", async () => {
|
||||
const original = new Error("operation failed");
|
||||
const auditFailure = new Error("audit failure");
|
||||
const writer: HousekeepingAuditWriter = {
|
||||
write: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(undefined)
|
||||
.mockRejectedValueOnce(auditFailure),
|
||||
};
|
||||
await expect(
|
||||
runWithAuditIntent(writer, auditEntry, () => Promise.reject(original)),
|
||||
).rejects.toBe(original);
|
||||
expect(
|
||||
(original as Error & { auditOutcomeError?: unknown }).auditOutcomeError,
|
||||
).toBe(auditFailure);
|
||||
});
|
||||
|
||||
it("throws a completed-operation error when success evidence rejects", async () => {
|
||||
const auditFailure = new Error("audit failure");
|
||||
const writer: HousekeepingAuditWriter = {
|
||||
write: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(undefined)
|
||||
.mockRejectedValueOnce(auditFailure),
|
||||
};
|
||||
await expect(
|
||||
runWithAuditIntent(writer, auditEntry, () => Promise.resolve("done")),
|
||||
).rejects.toMatchObject({
|
||||
operationCompleted: true,
|
||||
operationResult: "done",
|
||||
auditOutcomeError: auditFailure,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,17 @@ import type {
|
||||
} from "@/lib/services/audit";
|
||||
|
||||
type AuditOutcome = Exclude<NonNullable<AuditEntry["outcome"]>, "intent">;
|
||||
export class AuditOutcomePersistenceError<T> extends Error {
|
||||
readonly operationCompleted = true;
|
||||
|
||||
constructor(
|
||||
readonly operationResult: T,
|
||||
readonly auditOutcomeError: unknown,
|
||||
) {
|
||||
super("Operation completed but audit outcome could not be persisted");
|
||||
this.name = "AuditOutcomePersistenceError";
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeIntent(
|
||||
writer: HousekeepingAuditWriter,
|
||||
@@ -35,10 +46,23 @@ export async function runWithAuditIntent<T>(
|
||||
try {
|
||||
result = await operation();
|
||||
} catch (error) {
|
||||
await writeOutcome(writer, entry, "failure", transaction);
|
||||
try {
|
||||
await writeOutcome(writer, entry, "failure", transaction);
|
||||
} catch (auditOutcomeError) {
|
||||
if (error instanceof Error) {
|
||||
Object.defineProperty(error, "auditOutcomeError", {
|
||||
value: auditOutcomeError,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
await writeOutcome(writer, entry, "success", transaction);
|
||||
try {
|
||||
await writeOutcome(writer, entry, "success", transaction);
|
||||
} catch (auditOutcomeError) {
|
||||
throw new AuditOutcomePersistenceError(result, auditOutcomeError);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in new issue
Block a user