chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:32:57 +08:00
commit cd420f9332
4811 changed files with 884702 additions and 0 deletions
@@ -0,0 +1,280 @@
import { containerTest } from "@internal/testcontainers";
import type {
Organization,
PrismaClient,
Project,
RuntimeEnvironment,
} from "@trigger.dev/database";
import { customAlphabet } from "nanoid";
import { expect, vi } from "vitest";
import { ApiBatchResultsPresenter } from "~/presenters/v3/ApiBatchResultsPresenter.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { seedTestEnvironment } from "../helpers/seedTestEnvironment";
// Neutralize the db.server singleton so importing the presenter (via BasePresenter) does not try
// to connect to the env database; the test uses the injected testcontainer prisma for all reads.
vi.mock("~/db.server", () => ({ prisma: {}, $replica: {} }));
vi.setConfig({ testTimeout: 60_000 });
const idGenerator = customAlphabet("123456789abcdefghijkmnopqrstuvwxyz", 21);
function authEnv(
environment: RuntimeEnvironment,
project: Project,
organization: Organization
): AuthenticatedEnvironment {
return { ...environment, project, organization, orgMember: null } as AuthenticatedEnvironment;
}
type SeedContext = {
environmentId: string;
projectId: string;
organizationId: string;
backgroundWorkerId: string;
backgroundWorkerTaskId: string;
queueId: string;
};
async function seedWorker(
prisma: PrismaClient,
ctx: Omit<SeedContext, "backgroundWorkerId" | "backgroundWorkerTaskId" | "queueId">
) {
const queue = await prisma.taskQueue.create({
data: {
friendlyId: `queue_${idGenerator()}`,
name: "task/test-task",
projectId: ctx.projectId,
runtimeEnvironmentId: ctx.environmentId,
},
});
const worker = await prisma.backgroundWorker.create({
data: {
friendlyId: `worker_${idGenerator()}`,
contentHash: "hash",
projectId: ctx.projectId,
runtimeEnvironmentId: ctx.environmentId,
version: "20240101.1",
metadata: {},
},
});
const task = await prisma.backgroundWorkerTask.create({
data: {
friendlyId: `task_${idGenerator()}`,
slug: "test-task",
filePath: "src/test.ts",
exportName: "testTask",
workerId: worker.id,
projectId: ctx.projectId,
runtimeEnvironmentId: ctx.environmentId,
},
});
return { queueId: queue.id, backgroundWorkerId: worker.id, backgroundWorkerTaskId: task.id };
}
async function seedRunWithAttempt(
prisma: PrismaClient,
ctx: SeedContext,
opts: {
status: "COMPLETED_SUCCESSFULLY" | "COMPLETED_WITH_ERRORS" | "CANCELED" | "EXECUTING";
attempt?: {
status: "COMPLETED" | "FAILED";
output?: string;
outputType?: string;
error?: unknown;
};
}
) {
const runInternalId = idGenerator();
const run = await prisma.taskRun.create({
data: {
id: runInternalId,
friendlyId: `run_${runInternalId}`,
taskIdentifier: "test-task",
payload: "{}",
payloadType: "application/json",
traceId: idGenerator(),
spanId: idGenerator(),
queue: "task/test-task",
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
status: opts.status,
},
});
if (opts.attempt) {
await prisma.taskRunAttempt.create({
data: {
friendlyId: `attempt_${idGenerator()}`,
taskRunId: run.id,
backgroundWorkerId: ctx.backgroundWorkerId,
backgroundWorkerTaskId: ctx.backgroundWorkerTaskId,
runtimeEnvironmentId: ctx.environmentId,
queueId: ctx.queueId,
status: opts.attempt.status,
output: opts.attempt.output,
outputType: opts.attempt.outputType ?? "application/json",
error: opts.attempt.error as any,
},
});
}
return run;
}
containerTest(
"ApiBatchResultsPresenter returns ordered results matching pre-decompose behavior",
async ({ prisma }) => {
const { environment, project, organization } = await seedTestEnvironment(prisma);
const worker = await seedWorker(prisma, {
environmentId: environment.id,
projectId: project.id,
organizationId: organization.id,
});
const ctx: SeedContext = {
environmentId: environment.id,
projectId: project.id,
organizationId: organization.id,
...worker,
};
// A successful run, a failed run, and an executing run (no terminal attempt → undefined).
const successRun = await seedRunWithAttempt(prisma, ctx, {
status: "COMPLETED_SUCCESSFULLY",
attempt: { status: "COMPLETED", output: '"hello"', outputType: "application/json" },
});
const failedRun = await seedRunWithAttempt(prisma, ctx, {
status: "COMPLETED_WITH_ERRORS",
attempt: {
status: "FAILED",
error: { type: "BUILT_IN_ERROR", name: "Error", message: "boom", stackTrace: "boom" },
},
});
const executingRun = await seedRunWithAttempt(prisma, ctx, {
status: "EXECUTING",
});
const batchInternalId = idGenerator();
const batchFriendlyId = `batch_${batchInternalId}`;
await prisma.batchTaskRun.create({
data: {
id: batchInternalId,
friendlyId: batchFriendlyId,
runtimeEnvironmentId: environment.id,
},
});
// Items inserted in a deterministic order: success, failed, executing.
for (const run of [successRun, failedRun, executingRun]) {
await prisma.batchTaskRunItem.create({
data: {
batchTaskRunId: batchInternalId,
taskRunId: run.id,
},
});
}
// Pass the testcontainer prisma as both the primary and the replica: the passthrough path
// reads the batch row off the replica and the member runs off the primary (via the run store).
const presenter = new ApiBatchResultsPresenter(prisma, prisma);
const result = await presenter.call(
batchFriendlyId,
authEnv(environment, project, organization)
);
expect(result).toBeDefined();
expect(result?.id).toBe(batchFriendlyId);
// executing run yields no execution result → filtered out. Order preserved: success then failed.
expect(result?.items).toHaveLength(2);
const [first, second] = result!.items;
expect(first.ok).toBe(true);
expect(first.id).toBe(successRun.friendlyId);
if (first.ok) {
expect(first.output).toBe('"hello"');
expect(first.taskIdentifier).toBe("test-task");
}
expect(second.ok).toBe(false);
expect(second.id).toBe(failedRun.friendlyId);
}
);
containerTest(
"ApiBatchResultsPresenter filters runs without an execution result but keeps order",
async ({ prisma }) => {
const { environment, project, organization } = await seedTestEnvironment(prisma);
const worker = await seedWorker(prisma, {
environmentId: environment.id,
projectId: project.id,
organizationId: organization.id,
});
const ctx: SeedContext = {
environmentId: environment.id,
projectId: project.id,
organizationId: organization.id,
...worker,
};
// Pending run → executionResultForTaskRun returns undefined → filtered out, like the
// pre-decompose code did via `.filter(Boolean)`.
const pendingRun = await seedRunWithAttempt(prisma, ctx, { status: "EXECUTING" });
const successRun = await seedRunWithAttempt(prisma, ctx, {
status: "COMPLETED_SUCCESSFULLY",
attempt: { status: "COMPLETED", output: '"ok"', outputType: "application/json" },
});
const batchInternalId = idGenerator();
const batchFriendlyId = `batch_${batchInternalId}`;
await prisma.batchTaskRun.create({
data: {
id: batchInternalId,
friendlyId: batchFriendlyId,
runtimeEnvironmentId: environment.id,
},
});
// pending first, success second — only the success result should survive, in order.
for (const run of [pendingRun, successRun]) {
await prisma.batchTaskRunItem.create({
data: { batchTaskRunId: batchInternalId, taskRunId: run.id },
});
}
// Pass the testcontainer prisma as both the primary and the replica: the passthrough path
// reads the batch row off the replica and the member runs off the primary (via the run store).
const presenter = new ApiBatchResultsPresenter(prisma, prisma);
const result = await presenter.call(
batchFriendlyId,
authEnv(environment, project, organization)
);
expect(result?.items).toHaveLength(1);
expect(result?.items[0]?.id).toBe(successRun.friendlyId);
}
);
containerTest("ApiBatchResultsPresenter short-circuits an empty batch", async ({ prisma }) => {
const { environment, project, organization } = await seedTestEnvironment(prisma);
const batchInternalId = idGenerator();
const batchFriendlyId = `batch_${batchInternalId}`;
await prisma.batchTaskRun.create({
data: {
id: batchInternalId,
friendlyId: batchFriendlyId,
runtimeEnvironmentId: environment.id,
},
});
// Pass the testcontainer prisma as both the primary and the replica: the passthrough path
// reads the batch row off the replica and the member runs off the primary (via the run store).
const presenter = new ApiBatchResultsPresenter(prisma, prisma);
const result = await presenter.call(batchFriendlyId, authEnv(environment, project, organization));
expect(result).toEqual({ id: batchFriendlyId, items: [] });
});
@@ -0,0 +1,157 @@
// SPLIT-NEUTRAL VERIFICATION: getActivity reads ClickHouse
// (task_runs_v2) ONLY and never touches the run-ops Prisma client. A
// heterogeneous legacy/new Postgres fixture is deliberately not applicable
// here — there is no run-ops Postgres read to validate cross-version. The
// throwing Proxy passed as `replica` proves it: any access throws.
import { describe, expect, vi } from "vitest";
import { clickhouseTest } from "@internal/testcontainers";
import { ClickHouse, type TaskRunV2 } from "@internal/clickhouse";
import { randomUUID } from "node:crypto";
import { z } from "zod";
import { TaskDetailPresenter } from "~/presenters/v3/TaskDetailPresenter.server";
vi.setConfig({ testTimeout: 60_000 });
const organizationId = "org_activity_test";
const projectId = "project_activity_test";
const environmentId = "env_activity_test";
const taskSlug = "my-activity-task";
function makeRun(overrides: Partial<TaskRunV2>): TaskRunV2 {
const createdAt = overrides.created_at ?? Date.now();
return {
environment_id: environmentId,
organization_id: organizationId,
project_id: projectId,
run_id: `run_${randomUUID()}`,
friendly_id: `friendly_${randomUUID()}`,
updated_at: createdAt,
created_at: createdAt,
status: "COMPLETED_SUCCESSFULLY",
environment_type: "PRODUCTION",
attempt: 1,
engine: "V2",
task_identifier: taskSlug,
queue: "my-queue",
schedule_id: "",
batch_id: "",
task_version: "",
sdk_version: "",
cli_version: "",
machine_preset: "",
root_run_id: "",
parent_run_id: "",
span_id: "",
trace_id: "",
idempotency_key: "",
expiration_ttl: "",
_version: "1",
_is_deleted: 0,
...overrides,
};
}
describe("TaskDetailPresenter.getActivity (ClickHouse-only)", () => {
clickhouseTest(
"buckets task_runs_v2 activity by status group, excludes deleted, never reads Postgres",
async ({ clickhouseContainer }) => {
const clickhouse = new ClickHouse({
url: clickhouseContainer.getConnectionUrl(),
name: "task-detail-activity-test",
compression: { request: true },
});
const insert = clickhouse.writer.insert({
name: "insertTaskRunsActivityTest",
table: "trigger_dev.task_runs_v2",
schema: z.any(),
settings: { async_insert: 0, enable_json_type: 1, type_json_skip_duplicated_paths: 1 },
});
// 6h window => 300s (5-minute) buckets => 72 buckets (chooseBucketSeconds targets ~72).
const from = new Date("2026-01-01T00:00:00Z");
const to = new Date("2026-01-01T06:00:00Z");
const BUCKET_MS = 5 * 60 * 1000;
// 00:30 bucket: 1 COMPLETED, 1 FAILED (+ 1 deleted, excluded).
// 02:30 bucket: 1 CANCELED, RUNNING = EXECUTING + unknown-status = 2.
const bucket0 = new Date("2026-01-01T00:30:00Z").getTime();
const bucket2 = new Date("2026-01-01T02:30:00Z").getTime();
const rows = [
makeRun({ created_at: bucket0, status: "COMPLETED_SUCCESSFULLY" }),
makeRun({ created_at: bucket0, status: "CRASHED" }), // FAILED group
makeRun({ created_at: bucket2, status: "CANCELED" }), // CANCELED group
makeRun({ created_at: bucket2, status: "EXECUTING" }), // RUNNING group
makeRun({ created_at: bucket2, status: "SOME_UNKNOWN_STATUS" }), // folds into RUNNING
// Deleted row — distinct run, _is_deleted = 1, must NOT be counted.
makeRun({ created_at: bucket0, status: "COMPLETED_SUCCESSFULLY", _is_deleted: 1 }),
];
const [insertError] = await insert(rows);
expect(insertError).toBeNull();
const throwingReplica = new Proxy(
{},
{
get() {
throw new Error("getActivity must not touch the run-ops Prisma client");
},
}
) as never;
const presenter = new TaskDetailPresenter(throwingReplica, clickhouse);
const activity = await presenter.getActivity({
organizationId,
projectId,
environmentId,
taskSlug,
from,
to,
});
// Stable legend, fixed group order.
expect(activity.statuses).toEqual(["COMPLETED", "FAILED", "CANCELED", "RUNNING"]);
// 72 five-minute buckets, every bucket carries all four group keys.
expect(activity.data).toHaveLength(72);
for (const point of activity.data) {
expect(typeof point.bucket).toBe("number");
expect(point).toHaveProperty("COMPLETED");
expect(point).toHaveProperty("FAILED");
expect(point).toHaveProperty("CANCELED");
expect(point).toHaveProperty("RUNNING");
}
// Buckets are epoch MILLISECONDS aligned to the 5-minute interval.
const byBucket = new Map(activity.data.map((p) => [p.bucket, p]));
const p0 = byBucket.get(Math.floor(bucket0 / BUCKET_MS) * BUCKET_MS)!;
const p2 = byBucket.get(Math.floor(bucket2 / BUCKET_MS) * BUCKET_MS)!;
expect(p0).toBeDefined();
expect(p2).toBeDefined();
// 00:30 bucket: 1 COMPLETED, 1 FAILED, deleted row excluded.
expect(p0.COMPLETED).toBe(1);
expect(p0.FAILED).toBe(1);
expect(p0.CANCELED).toBe(0);
expect(p0.RUNNING).toBe(0);
// 02:30 bucket: 1 CANCELED, RUNNING = EXECUTING (1) + unknown status (1) = 2.
expect(p2.COMPLETED).toBe(0);
expect(p2.FAILED).toBe(0);
expect(p2.CANCELED).toBe(1);
expect(p2.RUNNING).toBe(2);
// Every other bucket is all-zero for every group.
for (const point of activity.data) {
if (point.bucket === p0.bucket || point.bucket === p2.bucket) continue;
expect(point.COMPLETED).toBe(0);
expect(point.FAILED).toBe(0);
expect(point.CANCELED).toBe(0);
expect(point.RUNNING).toBe(0);
}
}
);
});
@@ -0,0 +1,514 @@
import { describe, expect, vi } from "vitest";
// The presenter module graph imports `~/v3/runStore.server`, which imports `~/db.server`
// at load. Stub it (the sibling runsRepository.readthrough.test.ts does the same) — the
// presenter under test is driven entirely through injected real containers, never the
// stubbed module singletons.
vi.mock("~/db.server", () => ({
prisma: {},
$replica: {},
}));
import { PostgresRunStore } from "@internal/run-store";
import { createPostgresContainer, replicationContainerTest } from "@internal/testcontainers";
import { PrismaClient } from "@trigger.dev/database";
import { setTimeout } from "node:timers/promises";
import superjson from "superjson";
import { TestTaskPresenter } from "~/presenters/v3/TestTaskPresenter.server";
import { setupClickhouseReplication } from "../utils/replicationUtils";
vi.setConfig({ testTimeout: 90_000 });
type SeedContext = {
organizationId: string;
projectId: string;
environmentId: string;
};
const JSON_TYPE = "application/json";
const SUPERJSON_TYPE = "application/super+json";
/**
* Creates the org/project/(DEVELOPMENT) env parents plus the BackgroundWorker +
* BackgroundWorkerTask the presenter resolves the task from. TaskRun FKs require
* the org/project/env to exist on every DB a run is hydrated from.
*/
async function seedParents(
prisma: PrismaClient,
slug: string,
triggerSource: "STANDARD" | "SCHEDULED"
): 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}`,
},
});
const worker = await prisma.backgroundWorker.create({
data: {
friendlyId: `worker_${slug}`,
contentHash: `hash-${slug}`,
version: "20240101.1",
engine: "V2",
metadata: {},
projectId: project.id,
runtimeEnvironmentId: runtimeEnvironment.id,
},
});
await prisma.backgroundWorkerTask.create({
data: {
friendlyId: `task_${slug}`,
slug: "my-task",
filePath: "src/trigger/my-task.ts",
exportName: "myTask",
workerId: worker.id,
projectId: project.id,
runtimeEnvironmentId: runtimeEnvironment.id,
triggerSource,
},
});
return {
organizationId: organization.id,
projectId: project.id,
environmentId: runtimeEnvironment.id,
};
}
/** Mirrors the org/project/env parents onto a second DB with the SAME ids. */
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 createRun(
prisma: PrismaClient,
ctx: SeedContext,
run: {
friendlyId: string;
payload: string;
payloadType?: string;
createdAt?: Date;
runTags?: string[];
}
) {
return prisma.taskRun.create({
data: {
friendlyId: run.friendlyId,
taskIdentifier: "my-task",
status: "COMPLETED_SUCCESSFULLY",
payload: run.payload,
payloadType: run.payloadType ?? JSON_TYPE,
traceId: run.friendlyId,
spanId: run.friendlyId,
queue: "task/my-task",
runTags: run.runTags ?? [],
runtimeEnvironmentId: ctx.environmentId,
projectId: ctx.projectId,
organizationId: ctx.organizationId,
environmentType: "DEVELOPMENT",
engine: "V2",
...(run.createdAt ? { createdAt: run.createdAt } : {}),
},
});
}
/** Copy a row created on one DB onto another DB with the SAME id. */
async function copyRunWithId(
prisma: PrismaClient,
ctx: SeedContext,
source: { id: string; friendlyId: string; payload: string; payloadType: string; createdAt: Date }
) {
const created = await createRun(prisma, ctx, {
friendlyId: source.friendlyId,
payload: source.payload,
payloadType: source.payloadType,
createdAt: source.createdAt,
});
await prisma.taskRun.update({
where: { friendlyId: source.friendlyId },
data: { id: source.id },
});
return created;
}
function envFor(ctx: SeedContext) {
return {
id: ctx.environmentId,
type: "DEVELOPMENT" as const,
projectId: ctx.projectId,
organizationId: ctx.organizationId,
};
}
/** A legacy-replica handle whose taskRun.findMany throws — proves it is never hydrated from. */
function throwingLegacyReplica(prisma: PrismaClient): PrismaClient {
return new Proxy(prisma, {
get(target, prop) {
if (prop === "taskRun") {
return new Proxy((target as any).taskRun, {
get(trTarget, trProp) {
if (trProp === "findMany") {
return async () => {
throw new Error("legacy replica hydrate must not be invoked");
};
}
return (trTarget as any)[trProp];
},
});
}
return (target as any)[prop];
},
}) as unknown as PrismaClient;
}
describe("TestTaskPresenter recent-payloads read-through (PG14 legacy + PG17 new)", () => {
// payloadType parity: split union of NEW + legacy-replica, JSON-only, createdAt desc.
replicationContainerTest(
"split mode hydrates the 10-most-recent CH id-set as the union of NEW + legacy-replica rows, payloadType filtered, createdAt desc",
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma, network }) => {
// PG14 = legacy read replica AND the replication source feeding the CH id-set.
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 PrismaClient({ datasources: { db: { url: newUrl } } });
try {
const ctx = await seedParents(prisma, "split1", "STANDARD");
await mirrorParents(prismaNew, ctx, "split1");
const base = Date.now();
const at = (offsetMs: number) => new Date(base - offsetMs);
// All rows seeded on PG14 (legacy + replication source -> CH gets the full id-set).
const legacyOld = await createRun(prisma, ctx, {
friendlyId: "run_legacy_old",
payload: JSON.stringify({ kind: "legacy-old" }),
createdAt: at(4000),
});
const nonJson = await createRun(prisma, ctx, {
friendlyId: "run_nonjson",
payload: "binary-bytes",
payloadType: "application/octet-stream",
createdAt: at(3000),
});
const migratedSuper = await createRun(prisma, ctx, {
friendlyId: "run_migrated_super",
payload: JSON.stringify({ kind: "migrated-super" }),
payloadType: SUPERJSON_TYPE,
createdAt: at(2000),
});
const migratedJson = await createRun(prisma, ctx, {
friendlyId: "run_migrated_json",
payload: JSON.stringify({ kind: "migrated-json" }),
createdAt: at(1000),
});
// The two "migrated" runs ALSO live on NEW (PG17), authoritative during retention.
await copyRunWithId(prismaNew, ctx, {
id: migratedSuper.id,
friendlyId: migratedSuper.friendlyId,
payload: migratedSuper.payload,
payloadType: SUPERJSON_TYPE,
createdAt: migratedSuper.createdAt,
});
await copyRunWithId(prismaNew, ctx, {
id: migratedJson.id,
friendlyId: migratedJson.friendlyId,
payload: migratedJson.payload,
payloadType: JSON_TYPE,
createdAt: migratedJson.createdAt,
});
await setTimeout(1500);
const presenter = new TestTaskPresenter(
prisma,
clickhouse,
{
splitEnabled: true,
newClient: prismaNew,
legacyReplica: prisma,
},
new PostgresRunStore({ prisma: prismaNew, readOnlyPrisma: prismaNew })
);
const result = await presenter.call({
userId: "user_1",
projectId: ctx.projectId,
environment: envFor(ctx),
taskIdentifier: "my-task",
});
expect(result.foundTask).toBe(true);
if (!result.foundTask || result.triggerSource !== "STANDARD") {
throw new Error("expected a STANDARD task");
}
// Union of the JSON/super+json rows across both DBs, createdAt desc, non-JSON absent.
expect(result.runs.map((r) => r.friendlyId)).toEqual([
"run_migrated_json",
"run_migrated_super",
"run_legacy_old",
]);
expect(result.runs.map((r) => r.friendlyId)).not.toContain("run_nonjson");
expect(result.runs.find((r) => r.id === nonJson.id)).toBeUndefined();
// payloadType round-trips byte-identically across PG14/PG17.
expect(result.runs.find((r) => r.id === migratedSuper.id)!.payloadType).toBe(
SUPERJSON_TYPE
);
expect(result.runs.find((r) => r.id === migratedJson.id)!.payloadType).toBe(JSON_TYPE);
expect(result.runs.find((r) => r.id === legacyOld.id)!.payloadType).toBe(JSON_TYPE);
} finally {
await prismaNew.$disconnect();
}
}
);
// Old in-retention run served from the legacy READ REPLICA only (no legacyWriter field exists).
replicationContainerTest(
"an in-retention legacy-only run hydrates from the legacy replica handle",
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma, network }) => {
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 PrismaClient({ datasources: { db: { url: newUrl } } });
try {
const ctx = await seedParents(prisma, "legacyonly", "STANDARD");
await mirrorParents(prismaNew, ctx, "legacyonly");
const legacyOnly = await createRun(prisma, ctx, {
friendlyId: "run_legacy_only",
payload: JSON.stringify({ kind: "legacy-only" }),
});
await setTimeout(1500);
// The deps shape exposes only `legacyReplica` — there is no `legacyWriter`/primary
// field, so the legacy primary is structurally unreachable from this hydrate.
const presenter = new TestTaskPresenter(
prisma,
clickhouse,
{
splitEnabled: true,
newClient: prismaNew,
legacyReplica: prisma,
},
new PostgresRunStore({ prisma: prismaNew, readOnlyPrisma: prismaNew })
);
const result = await presenter.call({
userId: "user_1",
projectId: ctx.projectId,
environment: envFor(ctx),
taskIdentifier: "my-task",
});
if (!result.foundTask || result.triggerSource !== "STANDARD") {
throw new Error("expected a STANDARD task");
}
expect(result.runs.map((r) => r.id)).toEqual([legacyOnly.id]);
} finally {
await prismaNew.$disconnect();
}
}
);
// Passthrough (single-DB): one plain store read, the legacy replica never touched.
replicationContainerTest(
"single-DB passthrough hydrates from one store read and never touches the legacy replica",
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => {
const { clickhouse } = await setupClickhouseReplication({
prisma,
databaseUrl: postgresContainer.getConnectionUri(),
clickhouseUrl: clickhouseContainer.getConnectionUrl(),
redisOptions,
});
const ctx = await seedParents(prisma, "passthrough", "STANDARD");
const base = Date.now();
const newer = await createRun(prisma, ctx, {
friendlyId: "run_newer",
payload: JSON.stringify({ n: 2 }),
createdAt: new Date(base - 1000),
});
const older = await createRun(prisma, ctx, {
friendlyId: "run_older",
payload: JSON.stringify({ n: 1 }),
createdAt: new Date(base - 2000),
});
await createRun(prisma, ctx, {
friendlyId: "run_nonjson",
payload: "bytes",
payloadType: "application/octet-stream",
createdAt: new Date(base - 500),
});
await setTimeout(1500);
// No readThrough (split off). Inject a throwing legacy replica to prove the split branch
// is never entered: a runStore whose findRuns drives the single `prisma` handle.
const presenter = new TestTaskPresenter(
prisma,
clickhouse,
{
splitEnabled: false,
legacyReplica: throwingLegacyReplica(prisma),
},
new PostgresRunStore({ prisma, readOnlyPrisma: prisma })
);
const result = await presenter.call({
userId: "user_1",
projectId: ctx.projectId,
environment: envFor(ctx),
taskIdentifier: "my-task",
});
if (!result.foundTask || result.triggerSource !== "STANDARD") {
throw new Error("expected a STANDARD task");
}
// createdAt desc, JSON-only.
expect(result.runs.map((r) => r.id)).toEqual([newer.id, older.id]);
}
);
// SCHEDULED-source parity: same hydrate path, ScheduledRun mapping exercised.
replicationContainerTest(
"SCHEDULED task: split union parses to ScheduledRun shape identically to single-DB",
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma, network }) => {
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 PrismaClient({ datasources: { db: { url: newUrl } } });
try {
const ctx = await seedParents(prisma, "scheduled", "SCHEDULED");
await mirrorParents(prismaNew, ctx, "scheduled");
// super+json so parsePacket revives the Date fields the ScheduledTaskPayload schema requires.
const schedulePayload = superjson.stringify({
scheduleId: "sched_1",
type: "IMPERATIVE",
timestamp: new Date("2026-01-01T00:00:00.000Z"),
timezone: "UTC",
externalId: "ext-1",
upcoming: [new Date("2026-01-02T00:00:00.000Z")],
});
const base = Date.now();
const migrated = await createRun(prisma, ctx, {
friendlyId: "run_sched_new",
payload: schedulePayload,
payloadType: SUPERJSON_TYPE,
createdAt: new Date(base - 1000),
});
await copyRunWithId(prismaNew, ctx, {
id: migrated.id,
friendlyId: migrated.friendlyId,
payload: schedulePayload,
payloadType: SUPERJSON_TYPE,
createdAt: migrated.createdAt,
});
const legacy = await createRun(prisma, ctx, {
friendlyId: "run_sched_legacy",
payload: schedulePayload,
payloadType: SUPERJSON_TYPE,
createdAt: new Date(base - 2000),
});
await setTimeout(1500);
const presenter = new TestTaskPresenter(
prisma,
clickhouse,
{
splitEnabled: true,
newClient: prismaNew,
legacyReplica: prisma,
},
new PostgresRunStore({ prisma: prismaNew, readOnlyPrisma: prismaNew })
);
const result = await presenter.call({
userId: "user_1",
projectId: ctx.projectId,
environment: envFor(ctx),
taskIdentifier: "my-task",
});
if (!result.foundTask || result.triggerSource !== "SCHEDULED") {
throw new Error("expected a SCHEDULED task");
}
expect(result.runs.map((r) => r.id)).toEqual([migrated.id, legacy.id]);
// Parsed ScheduledRun payload shape.
expect(result.runs[0].payload.timezone).toBe("UTC");
expect(result.runs[0].payload.externalId).toBe("ext-1");
} finally {
await prismaNew.$disconnect();
}
}
);
});
@@ -0,0 +1,79 @@
import { describe, expect, it } from "vitest";
import { mapRunToLiveFields } from "~/presenters/v3/mapRunToLiveFields.server";
describe("mapRunToLiveFields", () => {
it("maps an executing run with lockedAt fallback and non-final flags", () => {
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
const lockedAt = new Date("2026-05-07T09:59:50.000Z");
const result = mapRunToLiveFields({
friendlyId: "run_123",
status: "EXECUTING",
updatedAt,
startedAt: null,
lockedAt,
completedAt: null,
usageDurationMs: BigInt(2500),
costInCents: 10,
baseCostInCents: 5,
});
expect(result).toEqual({
friendlyId: "run_123",
status: "EXECUTING",
updatedAt: updatedAt.toISOString(),
startedAt: lockedAt.toISOString(),
finishedAt: undefined,
hasFinished: false,
isCancellable: true,
isPending: false,
usageDurationMs: 2500,
costInCents: 10,
baseCostInCents: 5,
});
});
it("maps a final run and prefers completedAt for finishedAt", () => {
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
const startedAt = new Date("2026-05-07T09:59:00.000Z");
const completedAt = new Date("2026-05-07T09:59:30.000Z");
const result = mapRunToLiveFields({
friendlyId: "run_456",
status: "COMPLETED_SUCCESSFULLY",
updatedAt,
startedAt,
lockedAt: null,
completedAt,
usageDurationMs: 1200,
costInCents: 20,
baseCostInCents: 7,
});
expect(result.finishedAt).toBe(completedAt.toISOString());
expect(result.startedAt).toBe(startedAt.toISOString());
expect(result.hasFinished).toBe(true);
expect(result.isCancellable).toBe(false);
});
it("falls back to updatedAt when a final run has no completedAt", () => {
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
const result = mapRunToLiveFields({
friendlyId: "run_789",
status: "CRASHED",
updatedAt,
startedAt: null,
lockedAt: null,
completedAt: null,
usageDurationMs: 0,
costInCents: 0,
baseCostInCents: 0,
});
expect(result.finishedAt).toBe(updatedAt.toISOString());
expect(result.hasFinished).toBe(true);
expect(result.isPending).toBe(false);
expect(result.isCancellable).toBe(false);
});
});