|
| 1 | +import { expect, it } from "vitest" |
| 2 | +import { Actor } from "../src/actor.js" |
| 3 | +import { sqlite } from "../src/database/sqlite.js" |
| 4 | +import { createRuntime } from "../src/runtime.js" |
| 5 | + |
| 6 | +class ReplacingProjection extends Actor { |
| 7 | + static override readonly actorType = "replacing-projection" |
| 8 | + action = "" |
| 9 | + |
| 10 | + stage({ action }: { action: string }): void { |
| 11 | + this.action = action |
| 12 | + if (action === "effect") this.emit("original") |
| 13 | + if (action === "commit_action") this.commitAction("original") |
| 14 | + } |
| 15 | + |
| 16 | + override observables(): Record<string, unknown> { |
| 17 | + if (!this.hasIntents()) return { action: this.action } |
| 18 | + |
| 19 | + this.discardIntents() |
| 20 | + if (this.action === "effect") this.emit("replacement") |
| 21 | + if (this.action === "commit_action") this.commitAction("replacement") |
| 22 | + return { action: this.action } |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +it.each(["effect", "commit_action"])( |
| 27 | + "rejects observable replacement of staged %s without retrying or committing", |
| 28 | + async (action) => { |
| 29 | + const committed: string[] = [] |
| 30 | + const runtime = createRuntime({ |
| 31 | + database: sqlite({ path: ":memory:" }), |
| 32 | + authorizeMessage: () => true, |
| 33 | + authorizeQuery: () => true, |
| 34 | + maxAttempts: 3, |
| 35 | + retryDelayMilliseconds: () => 0, |
| 36 | + }) |
| 37 | + try { |
| 38 | + runtime.register(ReplacingProjection) |
| 39 | + runtime.registerCommitAction("original", () => { |
| 40 | + committed.push("original") |
| 41 | + }) |
| 42 | + runtime.registerCommitAction("replacement", () => { |
| 43 | + committed.push("replacement") |
| 44 | + }) |
| 45 | + await runtime.install() |
| 46 | + const reference = runtime.ref(ReplacingProjection, "one") |
| 47 | + const message = await reference.send.stage({ action }) |
| 48 | + await runtime.worker().runUntilIdle() |
| 49 | + |
| 50 | + expect(await message.outcome()).toMatchObject({ |
| 51 | + status: "dead", |
| 52 | + error: { name: "QueryMutatedState" }, |
| 53 | + attempts: 1, |
| 54 | + }) |
| 55 | + expect((await reference.snapshot()).action).toBe("") |
| 56 | + expect(committed).toEqual([]) |
| 57 | + } finally { |
| 58 | + await runtime.close() |
| 59 | + } |
| 60 | + }, |
| 61 | +) |
0 commit comments