Skip to content

Commit 5b4b060

Browse files
committed
test(run-engine): prove the deriveFromRun branch against a real run row
Replaces the hand-written run-output callbacks with a real Postgres read. The branch's premise is that TaskRun.output holds the same string the waitpoint carried, and only a real row can settle that — a callback returning a literal asserted that the callback was called. Adds createRunOutputReader, the production reader over the store, so the read routes to the run's owning database. The dependency is now optional, because most cycles carry no deriveFromRun record; one that does with no reader wired throws, since that is a wiring error rather than a data condition. The equivalence suite runs against seeded child runs whose output matches each RUN row, so the parity claim is now checked end to end rather than against a value the test supplied twice. The pure suite keeps every case that performs no read and is built with no reader at all. One wrapper remains, and delegates to the real reader: it counts reads to pin one query per record rather than one per batch index, which the resolved output cannot show.
1 parent 1f42cf9 commit 5b4b060

5 files changed

Lines changed: 325 additions & 123 deletions

File tree

internal-packages/run-engine/src/engine/waitpointCoordinator/completedWaitpointEquivalence.test.ts

Lines changed: 97 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
// The resolver must produce what the executor already consumes, so the oracle is the
22
// existing hydration and not a hand-written literal. A literal cannot catch a drift in
33
// enhanceExecutionSnapshotWithWaitpoints itself; this can.
4-
import type { Waitpoint } from "@trigger.dev/database";
5-
import { describe, expect, it } from "vitest";
4+
import { postgresTest } from "@internal/testcontainers";
5+
import { PostgresRunStore } from "@internal/run-store";
6+
import type { PrismaClient, Waitpoint } from "@trigger.dev/database";
7+
import { describe, expect } from "vitest";
8+
import { seedChildRunWithOutput } from "./testFixtures/childRun.js";
69
import { enhanceExecutionSnapshotWithWaitpoints } from "../systems/executionSnapshotSystem.js";
710
import { buildCompletedWaitpointRecords } from "./completedWaitpointRecords.js";
8-
import { createCompletedWaitpointResolver } from "./completedWaitpointResolver.js";
11+
import {
12+
createCompletedWaitpointResolver,
13+
createRunOutputReader,
14+
} from "./completedWaitpointResolver.js";
915
import { envelopeSourceFromWaitpointRow } from "./completionEnvelopeSource.js";
1016
import type { CompletionEnvelopeSource } from "./types.js";
1117

1218
const COMPLETED_AT = new Date("2026-08-25T00:00:00.000Z");
1319
const RUN_ID = "run_0123456789abcdefghijklm";
14-
const CHILD_RUN_ID = "run_zyxwvutsrqponmlkjihgfe";
1520
const BATCH_ID = "batch_0123456789abcdefghijk";
1621

1722
/**
@@ -70,25 +75,21 @@ function sortEntries<T extends { id: string; index?: number }>(entries: T[]): T[
7075
* variant makes — that TaskRun.output holds the same string.
7176
*/
7277
async function bothPaths(
78+
prisma: PrismaClient,
7379
pairs: ReturnType<typeof pair>[],
7480
order: string[],
7581
batchId: string | null = null
7682
) {
77-
const outputsByRunId = new Map<string, string>();
78-
for (const { row } of pairs) {
79-
if (row.completedByTaskRunId && row.output !== null) {
80-
outputsByRunId.set(row.completedByTaskRunId, row.output);
81-
}
82-
}
83-
8483
const expected = enhanceExecutionSnapshotWithWaitpoints(
8584
snapshot(batchId),
8685
pairs.map((p) => p.row),
8786
order
8887
).completedWaitpoints;
8988

89+
const runStore = new PostgresRunStore({ prisma, readOnlyPrisma: prisma });
90+
9091
const actual = await createCompletedWaitpointResolver({
91-
readRunOutput: async (taskRunId) => outputsByRunId.get(taskRunId),
92+
readRunOutput: createRunOutputReader(runStore),
9293
})({
9394
runId: RUN_ID,
9495
...(batchId ? { batchId } : {}),
@@ -102,54 +103,64 @@ async function bothPaths(
102103
}
103104

104105
describe("the resolver reproduces the existing hydration", () => {
105-
it("for a single MANUAL waitpoint with an inline output", async () => {
106+
postgresTest("for a single MANUAL waitpoint with an inline output", async ({ prisma }) => {
106107
const { expected, actual } = await bothPaths(
108+
prisma,
107109
[pair({ id: "wp_manual", type: "MANUAL", output: '{"token":1}' })],
108110
[]
109111
);
110112

111113
expect(actual).toEqual(expected);
112114
});
113115

114-
it("for a MANUAL waitpoint with a user-provided idempotency key", async () => {
115-
const { expected, actual } = await bothPaths(
116-
[
117-
pair({
118-
id: "wp_manual",
119-
type: "MANUAL",
120-
output: '{"token":1}',
121-
idempotencyKey: "user-key",
122-
userProvidedIdempotencyKey: true,
123-
}),
124-
],
125-
[]
126-
);
127-
128-
expect(actual).toEqual(expected);
129-
expect(actual[0]?.idempotencyKey).toBe("user-key");
130-
});
131-
132-
it("for an idempotency key the user provided but that went inactive", async () => {
133-
const { expected, actual } = await bothPaths(
134-
[
135-
pair({
136-
id: "wp_manual",
137-
type: "MANUAL",
138-
output: '{"token":1}',
139-
idempotencyKey: "user-key",
140-
userProvidedIdempotencyKey: true,
141-
inactiveIdempotencyKey: "old",
142-
}),
143-
],
144-
[]
145-
);
146-
147-
expect(actual).toEqual(expected);
148-
expect(actual[0]?.idempotencyKey).toBeUndefined();
149-
});
116+
postgresTest(
117+
"for a MANUAL waitpoint with a user-provided idempotency key",
118+
async ({ prisma }) => {
119+
const { expected, actual } = await bothPaths(
120+
prisma,
121+
[
122+
pair({
123+
id: "wp_manual",
124+
type: "MANUAL",
125+
output: '{"token":1}',
126+
idempotencyKey: "user-key",
127+
userProvidedIdempotencyKey: true,
128+
}),
129+
],
130+
[]
131+
);
132+
133+
expect(actual).toEqual(expected);
134+
expect(actual[0]?.idempotencyKey).toBe("user-key");
135+
}
136+
);
137+
138+
postgresTest(
139+
"for an idempotency key the user provided but that went inactive",
140+
async ({ prisma }) => {
141+
const { expected, actual } = await bothPaths(
142+
prisma,
143+
[
144+
pair({
145+
id: "wp_manual",
146+
type: "MANUAL",
147+
output: '{"token":1}',
148+
idempotencyKey: "user-key",
149+
userProvidedIdempotencyKey: true,
150+
inactiveIdempotencyKey: "old",
151+
}),
152+
],
153+
[]
154+
);
155+
156+
expect(actual).toEqual(expected);
157+
expect(actual[0]?.idempotencyKey).toBeUndefined();
158+
}
159+
);
150160

151-
it("for a DATETIME waitpoint", async () => {
161+
postgresTest("for a DATETIME waitpoint", async ({ prisma }) => {
152162
const { expected, actual } = await bothPaths(
163+
prisma,
153164
[
154165
pair({
155166
id: "wp_datetime",
@@ -163,14 +174,16 @@ describe("the resolver reproduces the existing hydration", () => {
163174
expect(actual).toEqual(expected);
164175
});
165176

166-
it("for a RUN waitpoint outside a batch", async () => {
177+
postgresTest("for a RUN waitpoint outside a batch", async ({ prisma }) => {
178+
const childRunId = await seedChildRunWithOutput(prisma, '{"ok":true}');
167179
const { expected, actual } = await bothPaths(
180+
prisma,
168181
[
169182
pair({
170183
id: "wp_run",
171184
type: "RUN",
172185
output: '{"ok":true}',
173-
completedByTaskRunId: CHILD_RUN_ID,
186+
completedByTaskRunId: childRunId,
174187
}),
175188
],
176189
[]
@@ -179,14 +192,16 @@ describe("the resolver reproduces the existing hydration", () => {
179192
expect(actual).toEqual(expected);
180193
});
181194

182-
it("for a RUN waitpoint read under a batch", async () => {
195+
postgresTest("for a RUN waitpoint read under a batch", async ({ prisma }) => {
196+
const childRunId = await seedChildRunWithOutput(prisma, '{"ok":true}');
183197
const { expected, actual } = await bothPaths(
198+
prisma,
184199
[
185200
pair({
186201
id: "wp_run",
187202
type: "RUN",
188203
output: '{"ok":true}',
189-
completedByTaskRunId: CHILD_RUN_ID,
204+
completedByTaskRunId: childRunId,
190205
}),
191206
],
192207
["wp_run"],
@@ -197,15 +212,17 @@ describe("the resolver reproduces the existing hydration", () => {
197212
expect(actual[0]?.completedByTaskRun?.batch?.id).toBe(BATCH_ID);
198213
});
199214

200-
it("for a RUN waitpoint whose output is an error", async () => {
215+
postgresTest("for a RUN waitpoint whose output is an error", async ({ prisma }) => {
216+
const childRunId = await seedChildRunWithOutput(prisma, '{"message":"boom"}');
201217
const { expected, actual } = await bothPaths(
218+
prisma,
202219
[
203220
pair({
204221
id: "wp_run",
205222
type: "RUN",
206223
output: '{"message":"boom"}',
207224
outputIsError: true,
208-
completedByTaskRunId: CHILD_RUN_ID,
225+
completedByTaskRunId: childRunId,
209226
}),
210227
],
211228
[]
@@ -214,17 +231,19 @@ describe("the resolver reproduces the existing hydration", () => {
214231
expect(actual).toEqual(expected);
215232
});
216233

217-
it("for a BATCH waitpoint", async () => {
234+
postgresTest("for a BATCH waitpoint", async ({ prisma }) => {
218235
const { expected, actual } = await bothPaths(
236+
prisma,
219237
[pair({ id: "wp_batch", type: "BATCH", completedByBatchId: BATCH_ID })],
220238
[]
221239
);
222240

223241
expect(actual).toEqual(expected);
224242
});
225243

226-
it("for an already-offloaded output", async () => {
244+
postgresTest("for an already-offloaded output", async ({ prisma }) => {
227245
const { expected, actual } = await bothPaths(
246+
prisma,
228247
[
229248
pair({
230249
id: "wp_manual",
@@ -241,15 +260,17 @@ describe("the resolver reproduces the existing hydration", () => {
241260

242261
// The case the suite was blind to, and the one the frozen reference orders the other way. The
243262
// oracle emits the ref string; so does this, by a different branch.
244-
it("for an offloaded RUN success", async () => {
263+
postgresTest("for an offloaded RUN success", async ({ prisma }) => {
264+
const childRunId = await seedChildRunWithOutput(prisma, "s3://bucket/key");
245265
const { expected, actual } = await bothPaths(
266+
prisma,
246267
[
247268
pair({
248269
id: "wp_run_ref",
249270
type: "RUN",
250271
output: "s3://bucket/key",
251272
outputType: "application/store",
252-
completedByTaskRunId: CHILD_RUN_ID,
273+
completedByTaskRunId: childRunId,
253274
}),
254275
],
255276
[]
@@ -259,14 +280,16 @@ describe("the resolver reproduces the existing hydration", () => {
259280
expect(actual[0]?.output).toBe("s3://bucket/key");
260281
});
261282

262-
it("for one run present at two batch indexes", async () => {
283+
postgresTest("for one run present at two batch indexes", async ({ prisma }) => {
284+
const childRunId = await seedChildRunWithOutput(prisma, '{"ok":true}');
263285
const { expected, actual } = await bothPaths(
286+
prisma,
264287
[
265288
pair({
266289
id: "wp_run",
267290
type: "RUN",
268291
output: '{"ok":true}',
269-
completedByTaskRunId: CHILD_RUN_ID,
292+
completedByTaskRunId: childRunId,
270293
}),
271294
],
272295
["wp_run", "wp_run"],
@@ -277,15 +300,19 @@ describe("the resolver reproduces the existing hydration", () => {
277300
expect(actual.map((w) => w.index)).toEqual([0, 1]);
278301
});
279302

280-
it("for an index-less waitpoint sitting beside indexed ones", async () => {
303+
postgresTest("for an index-less waitpoint sitting beside indexed ones", async ({ prisma }) => {
304+
// Seeded to match the RUN row's own output, which is the parity premise: TaskRun.output
305+
// holds the same string the waitpoint carried.
306+
const childRunId = await seedChildRunWithOutput(prisma, '{"ok":true}');
281307
const { expected, actual } = await bothPaths(
308+
prisma,
282309
[
283310
pair({ id: "wp_indexless", type: "MANUAL", output: '{"token":1}' }),
284311
pair({
285312
id: "wp_run",
286313
type: "RUN",
287314
output: '{"ok":true}',
288-
completedByTaskRunId: CHILD_RUN_ID,
315+
completedByTaskRunId: childRunId,
289316
}),
290317
],
291318
["wp_run"],
@@ -300,8 +327,9 @@ describe("the resolver reproduces the existing hydration", () => {
300327
// an output, but the executor never reads it (sharedRuntimeManager.resolveWaitpoint
301328
// early-returns on type). Pinned so that if that early return ever goes away, this fails and
302329
// says why, instead of the output silently being missing at resume.
303-
it("deliberately drops a BATCH output, unlike the oracle", async () => {
330+
postgresTest("deliberately drops a BATCH output, unlike the oracle", async ({ prisma }) => {
304331
const { expected, actual } = await bothPaths(
332+
prisma,
305333
[
306334
pair({
307335
id: "wp_batch",
@@ -319,14 +347,16 @@ describe("the resolver reproduces the existing hydration", () => {
319347
expect(actual[0]?.outputIsError).toBe(true);
320348
});
321349

322-
it("for every type at once, under a batch", async () => {
350+
postgresTest("for every type at once, under a batch", async ({ prisma }) => {
351+
const childRunId = await seedChildRunWithOutput(prisma, '{"ok":true}');
323352
const { expected, actual } = await bothPaths(
353+
prisma,
324354
[
325355
pair({
326356
id: "wp_run",
327357
type: "RUN",
328358
output: '{"ok":true}',
329-
completedByTaskRunId: CHILD_RUN_ID,
359+
completedByTaskRunId: childRunId,
330360
}),
331361
pair({ id: "wp_batch", type: "BATCH", completedByBatchId: BATCH_ID }),
332362
pair({

0 commit comments

Comments
 (0)