// Real PG14 (legacy) + PG17 (new) proof for the dev-session-cancel TaskRun read. // The DB is never mocked: reads hit the two real containers. Only the pure // splitEnabled boundary and recording client wrappers are injected. import { heteroPostgresTest, postgresTest } from "@internal/testcontainers"; import type { PrismaClient } from "@trigger.dev/database"; import { generateRunOpsId } from "@trigger.dev/core/v3/isomorphic"; import { describe, expect, vi } from "vitest"; import type { PrismaReplicaClient } from "~/db.server"; import { CancelDevSessionRunsService } from "~/v3/services/cancelDevSessionRuns.server"; vi.setConfig({ testTimeout: 60_000 }); // 25-char cuid body (no v1 version marker) → LEGACY residency. function generateLegacyCuid() { const suffix = Array.from( { length: 24 }, () => "0123456789abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 36)] ).join(""); return `c${suffix}`; } async function seedOrgProjectEnv(prisma: PrismaClient, suffix: string) { const organization = await prisma.organization.create({ data: { title: `test-${suffix}`, slug: `test-${suffix}` }, }); const project = await prisma.project.create({ data: { name: `test-${suffix}`, slug: `test-${suffix}`, organizationId: organization.id, externalRef: `test-${suffix}`, }, }); const runtimeEnvironment = await prisma.runtimeEnvironment.create({ data: { slug: `test-${suffix}`, type: "DEVELOPMENT", projectId: project.id, organizationId: organization.id, apiKey: `test-${suffix}`, pkApiKey: `test-${suffix}`, shortcode: `test-${suffix}`, }, }); return { organization, project, runtimeEnvironment }; } async function seedRun( prisma: PrismaClient, ids: { id: string; friendlyId: string }, env: { runtimeEnvironmentId: string; projectId: string; organizationId: string } ) { return prisma.taskRun.create({ data: { id: ids.id, friendlyId: ids.friendlyId, taskIdentifier: "my-task", payload: JSON.stringify({ foo: "bar" }), payloadType: "application/json", traceId: "1234", spanId: "1234", queue: "test", runtimeEnvironmentId: env.runtimeEnvironmentId, projectId: env.projectId, organizationId: env.organizationId, environmentType: "DEVELOPMENT", // V1 so the (best-effort, error-swallowed) cancel does not require the V2 engine; // the unit under test is the READ resolution, not the cancel side effect. engine: "V1", status: "EXECUTING", }, }); } // A read client whose taskRun.findFirst is recorded; throws if used after being marked // forbidden, so we can prove a store was NEVER read. function recording(client: PrismaClient, opts: { forbidden?: boolean } = {}) { const calls: unknown[] = []; const taskRun = { findFirst: (args: unknown) => { calls.push(args); if (opts.forbidden) { throw new Error("this store must never be read"); } return (client as unknown as PrismaReplicaClient).taskRun.findFirst(args as never); }, }; return { handle: { ...client, taskRun } as unknown as PrismaReplicaClient, calls }; } describe("CancelDevSessionRunsService store routing (hetero)", () => { heteroPostgresTest( "a NEW run (run-ops id) resolves on the new store via read-through, by friendlyId and by id", async ({ prisma17, prisma14 }) => { const id = generateRunOpsId(); expect(id.length).toBe(26); const friendlyId = `run_${id}`; const { project, organization, runtimeEnvironment } = await seedOrgProjectEnv( prisma17, "new" ); await seedRun( prisma17, { id, friendlyId }, { runtimeEnvironmentId: runtimeEnvironment.id, projectId: project.id, organizationId: organization.id, } ); // by friendlyId { const newClient = recording(prisma17); const legacy = recording(prisma14, { forbidden: true }); const service = new CancelDevSessionRunsService({ prisma: prisma17, readThroughDeps: { splitEnabled: true, newClient: newClient.handle, legacyReplica: legacy.handle, }, }); await service.call({ runIds: [friendlyId], cancelledAt: new Date(), reason: "test", }); // run-ops id → NEW: new store served the read, legacy never touched. expect(newClient.calls.length).toBe(1); expect(legacy.calls.length).toBe(0); } // by internal id { const newClient = recording(prisma17); const legacy = recording(prisma14, { forbidden: true }); const service = new CancelDevSessionRunsService({ prisma: prisma17, readThroughDeps: { splitEnabled: true, newClient: newClient.handle, legacyReplica: legacy.handle, }, }); await service.call({ runIds: [id], cancelledAt: new Date(), reason: "test", }); expect(newClient.calls.length).toBe(1); expect(legacy.calls.length).toBe(0); } } ); heteroPostgresTest( "an OLD in-retention run (cuid) resolves off the LEGACY replica, never a legacy primary", async ({ prisma17, prisma14 }) => { const id = generateLegacyCuid(); expect(id.length).toBe(25); const friendlyId = `run_${id}`; const { project, organization, runtimeEnvironment } = await seedOrgProjectEnv( prisma14, "legacy" ); await seedRun( prisma14, { id, friendlyId }, { runtimeEnvironmentId: runtimeEnvironment.id, projectId: project.id, organizationId: organization.id, } ); const newClient = recording(prisma17); const legacy = recording(prisma14); const service = new CancelDevSessionRunsService({ prisma: prisma14, readThroughDeps: { splitEnabled: true, newClient: newClient.handle, legacyReplica: legacy.handle, }, }); await service.call({ runIds: [id], cancelledAt: new Date(), reason: "test", }); // NEW first (miss) → resolved off the LEGACY REPLICA handle (no primary handle exists). expect(newClient.calls.length).toBe(1); expect(legacy.calls.length).toBe(1); } ); }); describe("CancelDevSessionRunsService passthrough (single-DB)", () => { postgresTest( "with no read-through deps, the run is read from the single DB and session reads stay on it", async ({ prisma }) => { const id = generateRunOpsId(); const friendlyId = `run_${id}`; const { project, organization, runtimeEnvironment } = await seedOrgProjectEnv(prisma, "pt"); await seedRun( prisma, { id, friendlyId }, { runtimeEnvironmentId: runtimeEnvironment.id, projectId: project.id, organizationId: organization.id, } ); const session = await prisma.runtimeEnvironmentSession.create({ data: { environmentId: runtimeEnvironment.id, ipAddress: "127.0.0.1" }, }); // splitEnabled=false → single plain read against the one client; the session // control-plane read runs on the same prisma. const service = new CancelDevSessionRunsService({ prisma, replica: prisma, readThroughDeps: { splitEnabled: false, newClient: prisma as unknown as PrismaReplicaClient, }, }); await service.call({ runIds: [id], cancelledAt: new Date(), reason: "test", cancelledSessionId: session.id, }); // Run found + handed to cancel against the single DB; confirm the row is present. const row = await prisma.taskRun.findFirst({ where: { id } }); expect(row).not.toBeNull(); expect(row?.friendlyId).toBe(friendlyId); } ); });