chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
import { describe, expect, vi } from "vitest";
|
||||
|
||||
// The presenter graph imports `~/services/clickhouse/clickhouseFactoryInstance.server` and, via
|
||||
// NextRunListPresenter -> RunsRepository / runStore, reaches `~/db.server`'s `prisma`/`$replica`
|
||||
// singletons (through findDisplayableEnvironment + getTaskIdentifiers). We stub those two wiring
|
||||
// boundaries so the module loads + the connected-runs hydrate can be pointed at the per-test REAL
|
||||
// containers. The DB is NEVER mocked: every assertion runs against real Postgres/ClickHouse.
|
||||
//
|
||||
// * `~/db.server` — `prisma`/`$replica`/`runOpsNewPrisma` singletons. The read-through cases pass
|
||||
// explicit client handles to the presenter ctor so these proxies are never read on those paths.
|
||||
// For the connected-runs hydrate the proxies forward lazily to the test's real legacy handle.
|
||||
// The run-ops new/legacy clients forward to the new/legacy per-test holders respectively.
|
||||
// * clickhouseFactory singleton — overridden to hand back the per-test ClickHouse so the
|
||||
// connected-runs hydrate uses a real CH container (a wiring override, not a DB mock).
|
||||
const legacyReplicaHolder = vi.hoisted(() => ({ client: undefined as any }));
|
||||
const newClientHolder = vi.hoisted(() => ({ client: undefined as any }));
|
||||
const clickhouseHolder = vi.hoisted(() => ({ client: undefined as any }));
|
||||
|
||||
vi.mock("~/db.server", async () => {
|
||||
const { Prisma } = await import("@trigger.dev/database");
|
||||
const lazyProxy = (holder: { client: any }, label: string) =>
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_t, prop) {
|
||||
if (!holder.client) {
|
||||
throw new Error(`${label} not set for this test`);
|
||||
}
|
||||
return holder.client[prop];
|
||||
},
|
||||
}
|
||||
);
|
||||
const replicaProxy = lazyProxy(legacyReplicaHolder, "legacyReplicaHolder.client");
|
||||
return {
|
||||
prisma: replicaProxy,
|
||||
$replica: replicaProxy,
|
||||
runOpsNewPrisma: lazyProxy(newClientHolder, "newClientHolder.client"),
|
||||
runOpsNewReplica: lazyProxy(newClientHolder, "newClientHolder.client"),
|
||||
runOpsLegacyPrisma: replicaProxy,
|
||||
runOpsLegacyReplica: replicaProxy,
|
||||
sqlDatabaseSchema: Prisma.sql([`public`]),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("~/services/clickhouse/clickhouseFactoryInstance.server", () => ({
|
||||
clickhouseFactory: {
|
||||
getClickhouseForOrganization: async () => {
|
||||
if (!clickhouseHolder.client) {
|
||||
throw new Error("clickhouseHolder.client not set for this test");
|
||||
}
|
||||
return clickhouseHolder.client;
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
import {
|
||||
createPostgresContainer,
|
||||
heteroPostgresTest,
|
||||
replicationContainerTest,
|
||||
} from "@internal/testcontainers";
|
||||
import type { PrismaClient, WaitpointType } from "@trigger.dev/database";
|
||||
import { PrismaClient as PrismaClientCtor } from "@trigger.dev/database";
|
||||
import { setTimeout } from "node:timers/promises";
|
||||
import type { PrismaReplicaClient } from "~/db.server";
|
||||
import { WaitpointPresenter } from "~/presenters/v3/WaitpointPresenter.server";
|
||||
import { setupClickhouseReplication } from "./utils/replicationUtils";
|
||||
|
||||
vi.setConfig({ testTimeout: 90_000 });
|
||||
|
||||
// A read client whose waitpoint.findFirst is recorded; throws if used after being marked
|
||||
// forbidden, so we can prove a store was NEVER read. Every other access forwards to the real
|
||||
// client (so the inlined `environment` join + connectedRuns relation still resolve).
|
||||
function recording(client: PrismaClient, opts: { forbidden?: boolean } = {}) {
|
||||
const calls: unknown[] = [];
|
||||
return {
|
||||
handle: new Proxy(client, {
|
||||
get(target, prop) {
|
||||
if (prop === "waitpoint") {
|
||||
return new Proxy((target as any).waitpoint, {
|
||||
get(wpTarget, wpProp) {
|
||||
if (wpProp === "findFirst") {
|
||||
return (args: unknown) => {
|
||||
calls.push(args);
|
||||
if (opts.forbidden) {
|
||||
throw new Error("this store must never be read");
|
||||
}
|
||||
return (wpTarget as any).findFirst(args);
|
||||
};
|
||||
}
|
||||
return (wpTarget as any)[wpProp];
|
||||
},
|
||||
});
|
||||
}
|
||||
return (target as any)[prop];
|
||||
},
|
||||
}) as unknown as PrismaReplicaClient,
|
||||
calls,
|
||||
};
|
||||
}
|
||||
|
||||
type SeedContext = {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
};
|
||||
|
||||
async function seedParents(prisma: PrismaClient, slug: string): Promise<SeedContext> {
|
||||
const organization = await prisma.organization.create({
|
||||
data: { title: `org-${slug}`, slug: `org-${slug}` },
|
||||
});
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
name: `proj-${slug}`,
|
||||
slug: `proj-${slug}`,
|
||||
organizationId: organization.id,
|
||||
externalRef: `proj-${slug}`,
|
||||
},
|
||||
});
|
||||
const runtimeEnvironment = await prisma.runtimeEnvironment.create({
|
||||
data: {
|
||||
slug: `env-${slug}`,
|
||||
type: "DEVELOPMENT",
|
||||
projectId: project.id,
|
||||
organizationId: organization.id,
|
||||
apiKey: `tr_dev_${slug}`,
|
||||
pkApiKey: `pk_dev_${slug}`,
|
||||
shortcode: `sc-${slug}`,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
organizationId: organization.id,
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
};
|
||||
}
|
||||
|
||||
/** Mirrors the org/project/env parents onto a second DB with the SAME ids (FKs need them). */
|
||||
async function mirrorParents(prisma: PrismaClient, ctx: SeedContext, slug: string): Promise<void> {
|
||||
await prisma.organization.create({
|
||||
data: { id: ctx.organizationId, title: `org-${slug}`, slug: `org-${slug}` },
|
||||
});
|
||||
await prisma.project.create({
|
||||
data: {
|
||||
id: ctx.projectId,
|
||||
name: `proj-${slug}`,
|
||||
slug: `proj-${slug}`,
|
||||
organizationId: ctx.organizationId,
|
||||
externalRef: `proj-${slug}`,
|
||||
},
|
||||
});
|
||||
await prisma.runtimeEnvironment.create({
|
||||
data: {
|
||||
id: ctx.environmentId,
|
||||
slug: `env-${slug}`,
|
||||
type: "DEVELOPMENT",
|
||||
projectId: ctx.projectId,
|
||||
organizationId: ctx.organizationId,
|
||||
apiKey: `tr_dev_${slug}_b`,
|
||||
pkApiKey: `pk_dev_${slug}_b`,
|
||||
shortcode: `sc-${slug}-b`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function seedWaitpoint(
|
||||
prisma: PrismaClient,
|
||||
ctx: SeedContext,
|
||||
friendlyId: string,
|
||||
overrides: Partial<{
|
||||
type: WaitpointType;
|
||||
tags: string[];
|
||||
output: string;
|
||||
connectedRunFriendlyIds: string[];
|
||||
}> = {}
|
||||
) {
|
||||
return prisma.waitpoint.create({
|
||||
data: {
|
||||
friendlyId,
|
||||
type: overrides.type ?? "MANUAL",
|
||||
status: "COMPLETED",
|
||||
idempotencyKey: `idem-${friendlyId}`,
|
||||
userProvidedIdempotencyKey: false,
|
||||
output: overrides.output ?? JSON.stringify({ hello: "world" }),
|
||||
outputType: "application/json",
|
||||
outputIsError: false,
|
||||
completedAt: new Date(),
|
||||
tags: overrides.tags ?? ["a", "b"],
|
||||
projectId: ctx.projectId,
|
||||
environmentId: ctx.environmentId,
|
||||
...(overrides.connectedRunFriendlyIds
|
||||
? {
|
||||
connectedRuns: {
|
||||
connect: overrides.connectedRunFriendlyIds.map((friendlyId) => ({ friendlyId })),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function createRun(
|
||||
prisma: PrismaClient,
|
||||
ctx: SeedContext,
|
||||
run: { friendlyId: string; taskIdentifier?: string }
|
||||
) {
|
||||
return prisma.taskRun.create({
|
||||
data: {
|
||||
friendlyId: run.friendlyId,
|
||||
taskIdentifier: run.taskIdentifier ?? "my-task",
|
||||
status: "PENDING",
|
||||
payload: JSON.stringify({ foo: run.friendlyId }),
|
||||
traceId: run.friendlyId,
|
||||
spanId: run.friendlyId,
|
||||
queue: "test",
|
||||
runtimeEnvironmentId: ctx.environmentId,
|
||||
projectId: ctx.projectId,
|
||||
organizationId: ctx.organizationId,
|
||||
environmentType: "DEVELOPMENT",
|
||||
engine: "V2",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const callArgs = (ctx: SeedContext, friendlyId: string) => ({
|
||||
friendlyId,
|
||||
environmentId: ctx.environmentId,
|
||||
projectId: ctx.projectId,
|
||||
});
|
||||
|
||||
describe("WaitpointPresenter dual-DB read-through (hetero PG14 + PG17, no connected runs)", () => {
|
||||
// new-DB short-circuit. Waitpoint on NEW (PG17), legacy
|
||||
// wrapped so its waitpoint.findFirst throws if invoked. The lookup answers from NEW and must
|
||||
// NEVER fall through to legacy.
|
||||
heteroPostgresTest(
|
||||
"waitpoint on the new DB resolves without touching the legacy replica",
|
||||
async ({ prisma14, prisma17 }) => {
|
||||
const ctx = await seedParents(prisma17, "newonly");
|
||||
const seeded = await seedWaitpoint(prisma17, ctx, "waitpoint_newonly");
|
||||
|
||||
// The env lives on the new DB here; the resolver singleton reads it through the
|
||||
// `~/db.server` proxy.
|
||||
legacyReplicaHolder.client = prisma17;
|
||||
|
||||
const newClient = recording(prisma17);
|
||||
const legacy = recording(prisma14, { forbidden: true });
|
||||
|
||||
const presenter = new WaitpointPresenter(undefined, undefined, {
|
||||
splitEnabled: true,
|
||||
newClient: newClient.handle,
|
||||
legacyReplica: legacy.handle,
|
||||
});
|
||||
|
||||
const result = await presenter.call(callArgs(ctx, seeded.friendlyId));
|
||||
|
||||
expect(result?.id).toBe(seeded.friendlyId);
|
||||
// New-first short-circuit: legacy never probed (the throwing handle proves it).
|
||||
expect(newClient.calls.length).toBe(1);
|
||||
expect(legacy.calls.length).toBe(0);
|
||||
}
|
||||
);
|
||||
|
||||
// single-DB passthrough. No read-through deps -> exactly one plain
|
||||
// findFirst against the single `_replica` handle; the split branch is structurally never entered
|
||||
// (no second handle is injected). The connected-runs hydrate forwards `undefined` deps.
|
||||
heteroPostgresTest(
|
||||
"no read-through deps -> one plain findFirst on the single replica (passthrough)",
|
||||
async ({ prisma14, prisma17 }) => {
|
||||
const ctx = await seedParents(prisma14, "passthrough");
|
||||
const seeded = await seedWaitpoint(prisma14, ctx, "waitpoint_passthrough", {
|
||||
tags: ["one"],
|
||||
});
|
||||
|
||||
const single = recording(prisma14);
|
||||
const second = recording(prisma17, { forbidden: true });
|
||||
legacyReplicaHolder.client = single.handle;
|
||||
newClientHolder.client = second.handle;
|
||||
|
||||
// No readThroughDeps -> ctor defaults _replica to the (mocked) `$replica` singleton, which
|
||||
// forwards to `single.handle`. The split branch needs an injected second handle to fire, so
|
||||
// it cannot: passthrough is structural.
|
||||
const presenter = new WaitpointPresenter();
|
||||
|
||||
const result = await presenter.call(callArgs(ctx, seeded.friendlyId));
|
||||
|
||||
expect(result?.id).toBe(seeded.friendlyId);
|
||||
expect(result?.tags).toEqual(["one"]);
|
||||
// Exactly one read on the single client; the second handle is never touched.
|
||||
expect(single.calls.length).toBe(1);
|
||||
expect(second.calls.length).toBe(0);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("WaitpointPresenter connected-runs hydrate routed through read-through (PG14 + PG17 + CH)", () => {
|
||||
// Waitpoint detail + connected runs resolve on run-ops NEW. The
|
||||
// waitpoint + its 2 connected runs live on the new (PG17) DB; CH gets the run id-set so the
|
||||
// threaded NextRunListPresenter hydrate returns them. Proves the read-through deps are forwarded
|
||||
// so the connected-runs hydrate flows through the routed store.
|
||||
replicationContainerTest(
|
||||
"waitpoint + 2 connected runs resolve on the new DB via the routed hydrate",
|
||||
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma, network }) => {
|
||||
// `prisma`/`postgresContainer` is the PG14 legacy + CH replication source. The new DB (PG17)
|
||||
// is created alongside; we seed the waitpoint + runs on it so CH replicates from PG14 — so we
|
||||
// mirror the runs onto PG14 (replication source) and the waitpoint+runs onto PG17 (authoritative).
|
||||
const { clickhouse } = await setupClickhouseReplication({
|
||||
prisma,
|
||||
databaseUrl: postgresContainer.getConnectionUri(),
|
||||
clickhouseUrl: clickhouseContainer.getConnectionUrl(),
|
||||
redisOptions,
|
||||
});
|
||||
|
||||
const { url: newUrl } = await createPostgresContainer(network, {
|
||||
imageTag: "docker.io/postgres:17",
|
||||
});
|
||||
const prismaNew = new PrismaClientCtor({ datasources: { db: { url: newUrl } } });
|
||||
legacyReplicaHolder.client = prisma;
|
||||
newClientHolder.client = prismaNew;
|
||||
clickhouseHolder.client = clickhouse;
|
||||
|
||||
try {
|
||||
const ctx = await seedParents(prisma, "connectednew");
|
||||
await mirrorParents(prismaNew, ctx, "connectednew");
|
||||
|
||||
// The connected runs land on PG14 (CH replication source) AND on PG17 (authoritative,
|
||||
// same ids/friendlyIds) so the routed hydrate resolves them from NEW.
|
||||
const runA = await createRun(prisma, ctx, { friendlyId: "run_connA" });
|
||||
const runB = await createRun(prisma, ctx, { friendlyId: "run_connB" });
|
||||
const newRunA = await createRun(prismaNew, ctx, {
|
||||
friendlyId: "run_connA",
|
||||
taskIdentifier: "my-task-NEW",
|
||||
});
|
||||
const newRunB = await createRun(prismaNew, ctx, {
|
||||
friendlyId: "run_connB",
|
||||
taskIdentifier: "my-task-NEW",
|
||||
});
|
||||
await prismaNew.taskRun.update({
|
||||
where: { friendlyId: "run_connA" },
|
||||
data: { id: runA.id },
|
||||
});
|
||||
await prismaNew.taskRun.update({
|
||||
where: { friendlyId: "run_connB" },
|
||||
data: { id: runB.id },
|
||||
});
|
||||
|
||||
// Waitpoint authoritative on NEW, connected to the 2 runs.
|
||||
const seeded = await seedWaitpoint(prismaNew, ctx, "waitpoint_connectednew", {
|
||||
connectedRunFriendlyIds: ["run_connA", "run_connB"],
|
||||
});
|
||||
|
||||
// Wait for CH replication so the connected-run id-set page is non-empty.
|
||||
await setTimeout(1500);
|
||||
|
||||
const presenter = new WaitpointPresenter(prisma, prisma, {
|
||||
splitEnabled: true,
|
||||
newClient: prismaNew,
|
||||
legacyReplica: prisma,
|
||||
});
|
||||
|
||||
const result = await presenter.call(callArgs(ctx, seeded.friendlyId));
|
||||
|
||||
expect(result?.id).toBe(seeded.friendlyId);
|
||||
expect(result?.connectedRuns.map((r) => r.friendlyId).sort()).toEqual([
|
||||
"run_connA",
|
||||
"run_connB",
|
||||
]);
|
||||
// The connected runs carry the PG17-only taskIdentifier -> they hydrated from the threaded
|
||||
// newClient (PG17), proving the routed store is armed.
|
||||
expect(result?.connectedRuns.every((r) => r.taskIdentifier === "my-task-NEW")).toBe(true);
|
||||
void newRunA;
|
||||
void newRunB;
|
||||
} finally {
|
||||
await prismaNew.$disconnect();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("WaitpointPresenter bare-ctor production default activates readThroughRun", () => {
|
||||
heteroPostgresTest(
|
||||
"run-ops waitpoint on the new DB resolves via readThroughRun production defaults",
|
||||
async ({ prisma14, prisma17 }) => {
|
||||
const ctx = await seedParents(prisma17, "proddefault");
|
||||
const seeded = await seedWaitpoint(prisma17, ctx, "waitpoint_proddefault", {
|
||||
tags: ["p", "q"],
|
||||
});
|
||||
|
||||
// runOpsNewReplica default -> PG17; env resolver reads via legacyReplicaHolder.
|
||||
newClientHolder.client = prisma17;
|
||||
legacyReplicaHolder.client = prisma17;
|
||||
|
||||
// No newClient/legacyReplica injected — production ctor shape.
|
||||
const presenter = new WaitpointPresenter(undefined, undefined, { splitEnabled: true });
|
||||
|
||||
const result = await presenter.call(callArgs(ctx, seeded.friendlyId));
|
||||
|
||||
expect(result?.id).toBe(seeded.friendlyId);
|
||||
expect(result?.tags).toEqual(["p", "q"]);
|
||||
}
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user