import { type RealtimeRunRow, serializeRunRow, } from "~/services/realtime/electricStreamProtocol.server"; import { type RunListFilter } from "~/services/realtime/runReader.server"; import { RealtimeShadowComparator } from "~/services/realtime/shadowCompare.server"; import { describe, expect, it } from "vitest"; function sampleRow(overrides: Partial = {}): RealtimeRunRow { return { id: "run_a", taskIdentifier: "my-task", createdAt: new Date("2026-06-07T09:00:00.000Z"), updatedAt: new Date("2026-06-07T10:05:30.123Z"), startedAt: null, delayUntil: null, queuedAt: null, expiredAt: null, completedAt: null, friendlyId: "run_friendly_a", number: 7, isTest: true, status: "EXECUTING", usageDurationMs: 1234, costInCents: 0.55, baseCostInCents: 0.25, ttl: "1h", payload: '{"hello":"world"}', payloadType: "application/json", metadata: null, metadataType: "application/json", output: null, outputType: "application/json", runTags: ["a", "b"], error: null, realtimeStreams: [], ...overrides, }; } const UP_TO_DATE = { headers: { control: "up-to-date" } }; function insert(value: Record) { return { key: `"public"."TaskRun"/"${value.id}"`, value, headers: { operation: "insert" } }; } function makeComparator( rowsById: Record, resolvedIds: string[] = [] ) { return new RealtimeShadowComparator({ runReader: { getRunById: async (_env: string, id: string) => rowsById[id] ?? null, hydrateByIds: async (_env: string, ids: string[]) => ids.map((id) => rowsById[id]).filter((row): row is RealtimeRunRow => Boolean(row)), } as any, runListResolver: { resolveMatchingRunIds: async (_f: RunListFilter) => resolvedIds } as any, }); } describe("RealtimeShadowComparator serialization", () => { it("counts a faithful re-serialization as a match", async () => { const row = sampleRow(); const body = JSON.stringify([insert(serializeRunRow(row)), UP_TO_DATE]); const cmp = makeComparator({ run_a: row }); const out = await cmp.compare({ feed: "run", electricBody: body, environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, }); expect(out.serializationMatched).toBe(1); expect(out.serializationDiverged).toBe(0); expect(out.serializationSkew).toBe(0); expect(out.diffs).toEqual([]); }); it("does not flag semantically-equivalent but differently-encoded values", async () => { const row = sampleRow(); // Electric encodes bool as "true" (native uses "t"), a number with a trailing // zero, and a timestamp without millis — all equal after decoding. const value = { ...serializeRunRow(row), isTest: "true", costInCents: "0.5500", createdAt: "2026-06-07T09:00:00", }; const body = JSON.stringify([insert(value), UP_TO_DATE]); const cmp = makeComparator({ run_a: row }); const out = await cmp.compare({ feed: "run", electricBody: body, environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, }); expect(out.serializationMatched).toBe(1); expect(out.serializationDiverged).toBe(0); }); it("flags a genuine column divergence (same version)", async () => { const row = sampleRow(); const value = { ...serializeRunRow(row), payload: '{"hello":"TAMPERED"}' }; const body = JSON.stringify([insert(value), UP_TO_DATE]); const cmp = makeComparator({ run_a: row }); const out = await cmp.compare({ feed: "run", electricBody: body, environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, }); expect(out.serializationDiverged).toBe(1); expect(out.serializationMatched).toBe(0); expect(out.diffs).toEqual([ { runId: "run_a", column: "payload", electric: '{"hello":"TAMPERED"}', native: '{"hello":"world"}', }, ]); }); it("treats DEQUEUED/EXECUTING as equivalent (legacy status rewrite)", async () => { const row = sampleRow({ status: "EXECUTING" }); const value = { ...serializeRunRow(row), status: "DEQUEUED" }; const body = JSON.stringify([insert(value), UP_TO_DATE]); const cmp = makeComparator({ run_a: row }); const out = await cmp.compare({ feed: "run", electricBody: body, environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, }); expect(out.serializationDiverged).toBe(0); expect(out.serializationMatched).toBe(1); }); it("records skew when the row advanced between emit and refetch", async () => { const row = sampleRow(); // Electric emitted an older version; the refetched row is newer. const value = { ...serializeRunRow(sampleRow({ updatedAt: new Date("2026-06-07T10:00:00.000Z") })), }; const body = JSON.stringify([insert(value), UP_TO_DATE]); const cmp = makeComparator({ run_a: row }); const out = await cmp.compare({ feed: "run", electricBody: body, environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, }); expect(out.serializationSkew).toBe(1); expect(out.serializationMatched).toBe(0); expect(out.serializationDiverged).toBe(0); }); }); describe("RealtimeShadowComparator membership", () => { const filter: RunListFilter = { organizationId: "org_1", projectId: "proj_1", environmentId: "env_1", tags: ["t"], createdAtAfter: new Date("2026-06-06T00:00:00.000Z"), limit: 1000, }; function bodyFor(ids: string[]) { const msgs = ids.map((id) => insert(serializeRunRow(sampleRow({ id })))); return JSON.stringify([...msgs, UP_TO_DATE]); } it("matches when Electric's set equals the native resolver's set", async () => { const cmp = makeComparator({ a: sampleRow({ id: "a" }), b: sampleRow({ id: "b" }) }, [ "a", "b", ]); const out = await cmp.compare({ feed: "runs", electricBody: bodyFor(["a", "b"]), environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, membershipFilter: filter, }); expect(out.membershipMatch).toBe(true); expect(out.missingInNative).toEqual([]); expect(out.extraInNative).toEqual([]); }); it("reports rows missing from / extra in the native resolution", async () => { const cmp = makeComparator( { a: sampleRow({ id: "a" }), b: sampleRow({ id: "b" }) }, ["a", "c"] // native missing b, has extra c ); const out = await cmp.compare({ feed: "runs", electricBody: bodyFor(["a", "b"]), environment: { id: "env_1" }, skipColumns: [], isInitialSnapshot: true, membershipFilter: filter, }); expect(out.membershipMatch).toBe(false); expect(out.missingInNative).toEqual(["b"]); expect(out.extraInNative).toEqual(["c"]); }); });