import { CommandId, EventId, ProjectId } from "@t3tools/contracts"; import { assert, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { PersistenceDecodeError } from "../Errors.ts"; import { OrchestrationEventStore } from "../Services/OrchestrationEventStore.ts"; import { OrchestrationEventStoreLive } from "./OrchestrationEventStore.ts"; import { SqlitePersistenceMemory } from "./Sqlite.ts"; const isPersistenceDecodeError = Schema.is(PersistenceDecodeError); const layer = it.layer( OrchestrationEventStoreLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)), ); layer("OrchestrationEventStore", (it) => { it.effect("stores json columns as strings and replays decoded events", () => Effect.gen(function* () { const eventStore = yield* OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const now = "2026-01-01T00:00:00.000Z"; const appended = yield* eventStore.append({ type: "project.created", eventId: EventId.make("evt-store-roundtrip"), aggregateKind: "project", aggregateId: ProjectId.make("project-roundtrip"), occurredAt: now, commandId: CommandId.make("cmd-store-roundtrip"), causationEventId: null, correlationId: CommandId.make("cmd-store-roundtrip"), metadata: { adapterKey: "codex", }, payload: { projectId: ProjectId.make("project-roundtrip"), title: "Roundtrip Project", workspaceRoot: "/tmp/project-roundtrip", defaultModelSelection: null, scripts: [], createdAt: now, updatedAt: now, }, }); const storedRows = yield* sql<{ readonly payloadJson: string; readonly metadataJson: string; }>` SELECT payload_json AS "payloadJson", metadata_json AS "metadataJson" FROM orchestration_events WHERE event_id = ${appended.eventId} `; assert.equal(storedRows.length, 1); assert.equal(typeof storedRows[0]?.payloadJson, "string"); assert.equal(typeof storedRows[0]?.metadataJson, "string"); const replayed = yield* Stream.runCollect(eventStore.readFromSequence(0, 10)).pipe( Effect.map((chunk) => Array.from(chunk)), ); assert.equal(replayed.length, 1); assert.equal(replayed[0]?.type, "project.created"); assert.equal(replayed[0]?.metadata.adapterKey, "codex"); }), ); it.effect("fails with PersistenceDecodeError when stored json is invalid", () => Effect.gen(function* () { const eventStore = yield* OrchestrationEventStore; const sql = yield* SqlClient.SqlClient; const now = "2026-01-01T00:00:00.000Z"; yield* sql` INSERT INTO orchestration_events ( event_id, aggregate_kind, stream_id, stream_version, event_type, occurred_at, command_id, causation_event_id, correlation_id, actor_kind, payload_json, metadata_json ) VALUES ( ${EventId.make("evt-store-invalid-json")}, ${"project"}, ${ProjectId.make("project-invalid-json")}, ${0}, ${"project.created"}, ${now}, ${CommandId.make("cmd-store-invalid-json")}, ${null}, ${null}, ${"server"}, ${"{"}, ${"{}"} ) `; const replayResult = yield* Effect.result( Stream.runCollect(eventStore.readFromSequence(0, 10)), ); assert.equal(replayResult._tag, "Failure"); if (replayResult._tag === "Failure") { assert.ok(isPersistenceDecodeError(replayResult.failure)); assert.ok( replayResult.failure.operation.includes( "OrchestrationEventStore.readFromSequence:decodeRows", ), ); } }), ); });