Skip to content

Commit eef1801

Browse files
committed
test(webapp): route read-through presenter tests through the run store
The read-through presenter tests injected read clients the old way, so once those reads moved onto the run store they fell through to the default database and only passed where one happened to be reachable. Finish the run-store injection seam on the affected presenters (default unchanged, so behavior is identical) and wire each test to its own test-container stores.
1 parent 1b2b16c commit eef1801

14 files changed

Lines changed: 552 additions & 339 deletions

apps/webapp/app/presenters/v3/ApiRunResultPresenter.server.ts

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import type { TaskRunExecutionResult } from "@trigger.dev/core/v3";
22
import type { PrismaClientOrTransaction, PrismaReplicaClient } from "~/db.server";
3-
import { runOpsLegacyReplica } from "~/db.server";
43
import { executionResultForTaskRun } from "~/models/taskRun.server";
54
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
6-
import { readThroughRun } from "~/v3/runOpsMigration/readThrough.server";
5+
import { runStore as defaultRunStore } from "~/v3/runStore.server";
76
import { BasePresenter } from "./basePresenter.server";
87

98
type ApiRunResultReadThroughDeps = {
@@ -19,7 +18,8 @@ export class ApiRunResultPresenter extends BasePresenter {
1918
constructor(
2019
prisma?: PrismaClientOrTransaction,
2120
replica?: PrismaClientOrTransaction,
22-
private readonly _readThrough?: ApiRunResultReadThroughDeps
21+
private readonly _readThrough?: ApiRunResultReadThroughDeps,
22+
private readonly runStore = defaultRunStore
2323
) {
2424
super(prisma, replica);
2525
}
@@ -29,36 +29,14 @@ export class ApiRunResultPresenter extends BasePresenter {
2929
env: AuthenticatedEnvironment
3030
): Promise<TaskRunExecutionResult | undefined> {
3131
return this.traceWithEnv("call", env, async (span) => {
32-
// Single-run result poll routed through run-ops read-through. Split on: primary store first,
33-
// then the LEGACY RUN-OPS READ REPLICA for runs that miss on new; past-retention ids return
34-
// undefined -> the route's normal 404. Split off (single-DB / self-host): readThroughRun does
35-
// one plain findFirst against the single client (passthrough). Both legs run the identical
36-
// TaskRun(+attempts) lookup, inlined so the read resolves inside the router.
37-
const result = await readThroughRun({
38-
runId: friendlyId,
39-
environmentId: env.id,
40-
readNew: (client) =>
41-
client.taskRun.findFirst({
42-
// runops-routed-ok: readThroughRun new leg
43-
where: { friendlyId, runtimeEnvironmentId: env.id },
44-
include: { attempts: { orderBy: { createdAt: "desc" } } },
45-
}),
46-
readLegacy: (replica) =>
47-
replica.taskRun.findFirst({
48-
// runops-routed-ok: readThroughRun legacy leg
49-
where: { friendlyId, runtimeEnvironmentId: env.id },
50-
include: { attempts: { orderBy: { createdAt: "desc" } } },
51-
}),
52-
deps: {
53-
splitEnabled: this._readThrough?.splitEnabled,
54-
newClient: this._readThrough?.newClient ?? (this._prisma as PrismaReplicaClient),
55-
legacyReplica: this._readThrough?.legacyReplica ?? runOpsLegacyReplica,
56-
isPastRetention: this._readThrough?.isPastRetention,
57-
},
58-
});
59-
60-
const taskRun =
61-
result.source === "new" || result.source === "legacy-replica" ? result.value : undefined;
32+
// Single-run result poll routed through the run store, which selects the owning DB by
33+
// run-id residency (id shape): a run-ops (NEW) id reads the new store, a cuid (LEGACY) id
34+
// reads the legacy store. Single-DB / self-host collapses to one plain findFirst against the
35+
// one store (passthrough). The identical TaskRun(+attempts) lookup runs inside the router.
36+
const taskRun = await this.runStore.findRun(
37+
{ friendlyId, runtimeEnvironmentId: env.id },
38+
{ include: { attempts: { orderBy: { createdAt: "desc" } } } }
39+
);
6240

6341
if (!taskRun) {
6442
return undefined;

apps/webapp/app/presenters/v3/ApiWaitpointPresenter.server.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { BasePresenter } from "./basePresenter.server";
55
import { waitpointStatusToApiStatus } from "./WaitpointListPresenter.server";
66
import { generateHttpCallbackUrl } from "~/services/httpCallback.server";
77
import type { PrismaClientOrTransaction, PrismaReplicaClient } from "~/db.server";
8-
import { runStore } from "~/v3/runStore.server";
8+
import { runStore as defaultRunStore } from "~/v3/runStore.server";
99

1010
// Retained only to preserve the public constructor signature the route passes. Run-ops routing
1111
// (NEW vs LEGACY residency, replica reads) is now handled inside the injected `runStore`, so
@@ -21,7 +21,8 @@ export class ApiWaitpointPresenter extends BasePresenter {
2121
constructor(
2222
prismaClient?: PrismaClientOrTransaction,
2323
replicaClient?: PrismaClientOrTransaction,
24-
private readonly readThroughDeps?: ApiWaitpointPresenterReadThroughDeps
24+
private readonly readThroughDeps?: ApiWaitpointPresenterReadThroughDeps,
25+
private readonly runStore = defaultRunStore
2526
) {
2627
super(prismaClient, replicaClient);
2728
}
@@ -41,7 +42,7 @@ export class ApiWaitpointPresenter extends BasePresenter {
4142
return this.trace("call", async (span) => {
4243
// The store routes by the waitpointId's residency (id shape) and reads the owning
4344
// store's replica. waitpointId is pre-decoded from the friendlyId via WaitpointId.toId.
44-
const waitpoint = await runStore.findWaitpoint({
45+
const waitpoint = await this.runStore.findWaitpoint({
4546
where: {
4647
id: waitpointId,
4748
environmentId: environment.id,

apps/webapp/app/presenters/v3/WaitpointListPresenter.server.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { type WaitpointSearchParams } from "~/components/runs/v3/WaitpointTokenF
1111
import { determineEngineVersion } from "~/v3/engineVersion.server";
1212
import { type WaitpointTokenStatus, type WaitpointTokenItem } from "@trigger.dev/core/v3";
1313
import { generateHttpCallbackUrl } from "~/services/httpCallback.server";
14-
import { runStore } from "~/v3/runStore.server";
14+
import { runStore as defaultRunStore } from "~/v3/runStore.server";
1515

1616
const DEFAULT_PAGE_SIZE = 25;
1717

@@ -94,7 +94,8 @@ export class WaitpointListPresenter extends BasePresenter {
9494
runOpsNew?: PrismaClientOrTransaction;
9595
runOpsLegacyReplica?: PrismaClientOrTransaction;
9696
splitEnabled?: boolean;
97-
}
97+
},
98+
private readonly runStore = defaultRunStore
9899
) {
99100
super(prismaClient, replicaClient);
100101
}
@@ -179,7 +180,7 @@ export class WaitpointListPresenter extends BasePresenter {
179180

180181
const tokens = await this.#scanWaitpoints(
181182
() =>
182-
runStore.findManyWaitpoints({
183+
this.runStore.findManyWaitpoints({
183184
where: {
184185
environmentId: environment.id,
185186
type: "MANUAL",
@@ -307,7 +308,7 @@ export class WaitpointListPresenter extends BasePresenter {
307308

308309
// Empty-state probe across both residencies (runStore fans out); no single runId.
309310
async #probeAnyToken(environmentId: string): Promise<boolean> {
310-
const found = await runStore.findWaitpoint({
311+
const found = await this.runStore.findWaitpoint({
311312
where: { environmentId, type: "MANUAL" },
312313
});
313314
return Boolean(found);

apps/webapp/app/presenters/v3/WaitpointPresenter.server.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstan
44
import { generateHttpCallbackUrl } from "~/services/httpCallback.server";
55
import { logger } from "~/services/logger.server";
66
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
7-
import { runStore } from "~/v3/runStore.server";
7+
import { runStore as defaultRunStore } from "~/v3/runStore.server";
88
import { BasePresenter } from "./basePresenter.server";
99
import { NextRunListPresenter, type NextRunListItem } from "./NextRunListPresenter.server";
1010
import { waitpointStatusToApiStatus } from "./WaitpointListPresenter.server";
@@ -21,7 +21,8 @@ export class WaitpointPresenter extends BasePresenter {
2121
newClient?: PrismaClientOrTransaction;
2222
legacyReplica?: PrismaClientOrTransaction;
2323
splitEnabled?: boolean;
24-
}
24+
},
25+
private readonly runStore = defaultRunStore
2526
) {
2627
super(prisma, replica);
2728
}
@@ -30,7 +31,7 @@ export class WaitpointPresenter extends BasePresenter {
3031
// Keyed by (friendlyId, environmentId) with no classifiable waitpoint id, so the run-store
3132
// probes NEW then LEGACY and reads each store's own replica — resolving the waitpoint whichever
3233
// run store owns it. When split is off it reads the single control-plane replica (passthrough).
33-
return runStore.findWaitpoint({
34+
return this.runStore.findWaitpoint({
3435
where: { friendlyId, environmentId },
3536
select: {
3637
id: true,
@@ -58,11 +59,11 @@ export class WaitpointPresenter extends BasePresenter {
5859
// The run-store fans the connection lookup out to both DBs and resolves each run id on its owning
5960
// DB (by id-shape residency), so we get the union without joining across the seam.
6061
async #connectedRunFriendlyIds(waitpointId: string): Promise<string[]> {
61-
const runIds = await runStore.findWaitpointConnectedRunIds(waitpointId);
62+
const runIds = await this.runStore.findWaitpointConnectedRunIds(waitpointId);
6263
if (runIds.length === 0) {
6364
return [];
6465
}
65-
const runs = await runStore.findRuns({
66+
const runs = await this.runStore.findRuns({
6667
where: { id: { in: runIds } },
6768
select: { friendlyId: true },
6869
take: 5,

0 commit comments

Comments
 (0)