3 changed files with 111 additions and 50 deletions
@ -0,0 +1,29 @@ |
|||||
|
type TransitionAction = () => Promise<void> | void; |
||||
|
|
||||
|
export interface TransitionTransactionConfig { |
||||
|
prepare?: TransitionAction; |
||||
|
commit: TransitionAction; |
||||
|
rollback: TransitionAction; |
||||
|
} |
||||
|
|
||||
|
export class TransitionTransaction { |
||||
|
private readonly prepare: TransitionAction; |
||||
|
private readonly commit: TransitionAction; |
||||
|
private readonly rollback: TransitionAction; |
||||
|
|
||||
|
constructor(config: TransitionTransactionConfig) { |
||||
|
this.prepare = config.prepare ?? (() => {}); |
||||
|
this.commit = config.commit; |
||||
|
this.rollback = config.rollback; |
||||
|
} |
||||
|
|
||||
|
async execute(): Promise<void> { |
||||
|
try { |
||||
|
await this.prepare(); |
||||
|
await this.commit(); |
||||
|
} catch (error) { |
||||
|
await this.rollback(); |
||||
|
throw error; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
import { describe, expect, it, vi } from "vitest"; |
||||
|
import { TransitionTransaction } from "@/scene/TransitionTransaction"; |
||||
|
|
||||
|
describe("TransitionTransaction", () => { |
||||
|
it("rolls back when prepare fails and never commits", async () => { |
||||
|
const prepareError = new Error("prepare failed"); |
||||
|
const prepare = vi.fn(async () => { |
||||
|
throw prepareError; |
||||
|
}); |
||||
|
const commit = vi.fn(async () => {}); |
||||
|
const rollback = vi.fn(async () => {}); |
||||
|
|
||||
|
const transaction = new TransitionTransaction({ |
||||
|
prepare, |
||||
|
commit, |
||||
|
rollback, |
||||
|
}); |
||||
|
|
||||
|
await expect(transaction.execute()).rejects.toThrowError("prepare failed"); |
||||
|
expect(prepare).toHaveBeenCalledTimes(1); |
||||
|
expect(rollback).toHaveBeenCalledTimes(1); |
||||
|
expect(commit).not.toHaveBeenCalled(); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue