import { describe, expect, it, vi } from "vitest"; vi.mock("~/db.server", () => ({ prisma: {}, $replica: {} })); import { buildSyntheticSpanRun } from "~/v3/mollifier/syntheticSpanRun.server"; import type { SyntheticRun } from "~/v3/mollifier/readFallback.server"; const NOW = new Date("2026-05-21T10:00:00Z"); function makeSyntheticRun(overrides: Partial = {}): SyntheticRun { return { id: "run_internal_1", friendlyId: "run_friendly_1", status: "QUEUED", taskIdentifier: "hello-world", createdAt: NOW, payload: { message: "hi" }, payloadType: "application/json", metadata: undefined, metadataType: undefined, seedMetadata: undefined, seedMetadataType: undefined, idempotencyKey: undefined, idempotencyKeyOptions: undefined, isTest: false, depth: 0, ttl: "10m", tags: ["a", "b"], runTags: ["a", "b"], lockedToVersion: undefined, resumeParentOnCompletion: false, parentTaskRunId: undefined, traceId: "trace_1", spanId: "span_1", parentSpanId: undefined, runtimeEnvironmentId: "env_a", engine: "V2", workerQueue: "worker-queue-1", queue: "task/hello-world", concurrencyKey: undefined, machinePreset: "small-1x", realtimeStreamsVersion: "v1", maxAttempts: 3, maxDurationInSeconds: 3600, replayedFromTaskRunFriendlyId: undefined, annotations: undefined, traceContext: undefined, scheduleId: undefined, batchId: undefined, parentTaskRunFriendlyId: undefined, rootTaskRunFriendlyId: undefined, ...overrides, }; } const ENV = { id: "env_a", slug: "dev", type: "DEVELOPMENT" as const, }; describe("buildSyntheticSpanRun", () => { it("populates the core identity fields from the snapshot", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun(), environment: ENV }); expect(synth.id).toBe("run_internal_1"); expect(synth.friendlyId).toBe("run_friendly_1"); expect(synth.taskIdentifier).toBe("hello-world"); expect(synth.traceId).toBe("trace_1"); expect(synth.spanId).toBe("span_1"); expect(synth.environmentId).toBe("env_a"); expect(synth.engine).toBe("V2"); expect(synth.workerQueue).toBe("worker-queue-1"); }); it("reports PENDING status and the non-final flags", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun(), environment: ENV }); expect(synth.status).toBe("PENDING"); expect(synth.isFinished).toBe(false); expect(synth.isRunning).toBe(false); expect(synth.isError).toBe(false); expect(synth.startedAt).toBeNull(); expect(synth.completedAt).toBeNull(); }); it("pretty-prints the JSON payload from the snapshot", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun({ payload: { message: "hi" }, payloadType: "application/json" }), environment: ENV, }); // prettyPrintPacket round-trips JSON with 2-space indent. expect(synth.payload).toContain('"message": "hi"'); expect(synth.payloadType).toBe("application/json"); }); it("forwards runTags onto `tags` exactly", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun({ runTags: ["alpha", "beta"] }), environment: ENV, }); expect(synth.tags).toEqual(["alpha", "beta"]); }); it("classifies the queue name as custom when it does not start with 'task/'", async () => { const taskQueue = await buildSyntheticSpanRun({ run: makeSyntheticRun({ queue: "task/hello-world" }), environment: ENV, }); expect(taskQueue.queue.isCustomQueue).toBe(false); const customQueue = await buildSyntheticSpanRun({ run: makeSyntheticRun({ queue: "my-custom" }), environment: ENV, }); expect(customQueue.queue.isCustomQueue).toBe(true); }); it("derives idempotency status from the snapshot key/options", async () => { const withKey = await buildSyntheticSpanRun({ run: makeSyntheticRun({ idempotencyKey: "abc", idempotencyKeyOptions: ["scope"] }), environment: ENV, }); expect(withKey.idempotencyKey).toBe("abc"); expect(withKey.idempotencyKeyStatus).toBe("active"); const noKey = await buildSyntheticSpanRun({ run: makeSyntheticRun({ idempotencyKey: undefined, idempotencyKeyOptions: undefined }), environment: ENV, }); expect(noKey.idempotencyKeyStatus).toBeUndefined(); }); it("omits relationships even when parent/root friendlyIds are present, since the snapshot lacks their spanId/taskIdentifier", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun({ parentTaskRunFriendlyId: "run_parent", rootTaskRunFriendlyId: "run_root", }), environment: ENV, }); expect(synth.relationships.parent).toBeUndefined(); expect(synth.relationships.root).toBeUndefined(); }); it("returns no relationship objects when the snapshot has no parent/root", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun(), environment: ENV, }); expect(synth.relationships.parent).toBeUndefined(); expect(synth.relationships.root).toBeUndefined(); }); it("reflects a buffered CANCELED run as a finished, cancelled terminal state", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun({ status: "CANCELED", cancelledAt: NOW, cancelReason: "cancelled by user", }), environment: ENV, }); expect(synth.status).toBe("CANCELED"); expect(synth.statusReason).toBe("cancelled by user"); expect(synth.isFinished).toBe(true); expect(synth.isError).toBe(false); expect(synth.completedAt).toEqual(NOW); }); it("reflects a buffered FAILED run as a finished, errored SYSTEM_FAILURE", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun({ status: "FAILED", error: { code: "GATE_REJECTED", message: "buffer rejected the run" }, }), environment: ENV, }); expect(synth.status).toBe("SYSTEM_FAILURE"); expect(synth.isFinished).toBe(true); expect(synth.isError).toBe(true); expect(synth.statusReason).toBe("buffer rejected the run"); expect(synth.error).toEqual({ type: "STRING_ERROR", raw: "GATE_REJECTED: buffer rejected the run", }); // PG-resident SYSTEM_FAILURE rows always have completedAt set; // mirror that on the synth path so callers checking // `isFinished && completedAt` don't render a finished run with // no completion timestamp. The buffer entry has no separate // failedAt, so createdAt is the best-available proxy. expect(synth.completedAt).toEqual(NOW); }); it("flags the synthetic run as 'not cached' since cache lookup did not match it", async () => { const synth = await buildSyntheticSpanRun({ run: makeSyntheticRun(), environment: ENV }); expect(synth.isCached).toBe(false); }); });