@@ -144,220 +144,12 @@ async function createRun(
144144 } ) ;
145145}
146146
147- /**
148- * Wraps a real prisma handle in a Proxy whose `taskRun.findFirst` throws if invoked. Used to
149- * prove the empty-state probe never touches the legacy replica when the new DB already answers.
150- * All other access forwards to the real client (so FK parents etc. still resolve).
151- */
152- function throwingFindFirst ( prisma : PrismaClient , label : string ) : PrismaClient {
153- return new Proxy ( prisma , {
154- get ( target , prop ) {
155- if ( prop === "taskRun" ) {
156- return new Proxy ( ( target as any ) . taskRun , {
157- get ( trTarget , trProp ) {
158- if ( trProp === "findFirst" ) {
159- return async ( ) => {
160- throw new Error ( `${ label } .taskRun.findFirst must not be invoked` ) ;
161- } ;
162- }
163- return ( trTarget as any ) [ trProp ] ;
164- } ,
165- } ) ;
166- }
167- return ( target as any ) [ prop ] ;
168- } ,
169- } ) as unknown as PrismaClient ;
170- }
171-
172- const callOptions = ( ctx : SeedContext , overrides ?: { to ?: number } ) => ( {
147+ const callOptions = ( ctx : SeedContext ) => ( {
173148 projectId : ctx . projectId ,
174149 pageSize : 10 ,
175- ...overrides ,
176150} ) ;
177151
178- // `to` one hour in the past. The CH page filters `created_at <= to`, so a just-created run is
179- // deterministically excluded regardless of replication timing — the empty-state tests otherwise
180- // raced on the run not having replicated yet (held locally, failed on CI). The PG existence probe
181- // has no time filter, so it still finds the row and `hasAnyRuns` stays true.
182- const emptyPageWindow = ( ) : { to : number } => ( { to : Date . now ( ) - 60 * 60 * 1000 } ) ;
183-
184- describe ( "NextRunListPresenter dual-DB empty-state probe + routed hydrate (legacy + new Postgres)" , ( ) => {
185- // no-false-empty. Runs ONLY on legacy, none on new. Empty CH page -> listRuns returns [].
186- // splitEnabled true. The probe misses NEW, falls through to the legacy replica and finds the
187- // row, so the dashboard must NOT show "no runs".
188- replicationContainerTest (
189- "no-false-empty: runs only on the legacy replica still report hasAnyRuns true" ,
190- async ( { clickhouseContainer, redisOptions, postgresContainer, prisma, network } ) => {
191- const { clickhouse } = await setupClickhouseReplication ( {
192- prisma,
193- databaseUrl : postgresContainer . getConnectionUri ( ) ,
194- clickhouseUrl : clickhouseContainer . getConnectionUrl ( ) ,
195- redisOptions,
196- } ) ;
197-
198- const { url : newUrl } = await createPostgresContainer ( network , {
199- imageTag : "docker.io/postgres:17" ,
200- } ) ;
201- const prismaNew = new PrismaClient ( { datasources : { db : { url : newUrl } } } ) ;
202- legacyReplicaHolder . client = prisma ;
203-
204- try {
205- const ctx = await seedParents ( prisma , "nofalseempty" ) ;
206- await mirrorParents ( prismaNew , ctx , "nofalseempty" ) ;
207-
208- // Run lives ONLY on the legacy DB. We seed it to legacy and never wait for CH replication,
209- // so within the page window the CH id-set page is empty and listRuns returns []. The
210- // empty-state probe (NEW miss -> legacy hit) is what proves hasAnyRuns stays true.
211- await createRun ( prisma , ctx , { friendlyId : "run_legacyOnly" } ) ;
212-
213- const presenter = new NextRunListPresenter ( prisma , clickhouse , {
214- newClient : prismaNew ,
215- legacyReplica : prisma ,
216- splitEnabled : true ,
217- } ) ;
218-
219- const result = await presenter . call (
220- ctx . organizationId ,
221- ctx . environmentId ,
222- callOptions ( ctx , emptyPageWindow ( ) )
223- ) ;
224-
225- // CH id-set is empty within the page window, but the legacy probe finds the row.
226- expect ( result . runs ) . toHaveLength ( 0 ) ;
227- expect ( result . hasAnyRuns ) . toBe ( true ) ;
228- } finally {
229- await prismaNew . $disconnect ( ) ;
230- }
231- }
232- ) ;
233-
234- // new-DB short-circuit. A run on NEW, legacy replica wrapped so its taskRun.findFirst
235- // throws. Empty CH page. The probe answers from NEW and must NEVER fall through to legacy. The
236- // post-migration straggler is the same shape: present on NEW, absent from LEGACY, legacy never
237- // invoked.
238- replicationContainerTest (
239- "new-DB short-circuit: hasAnyRuns answered from the new DB without touching the legacy replica" ,
240- async ( { clickhouseContainer, redisOptions, postgresContainer, prisma, network } ) => {
241- const { clickhouse } = await setupClickhouseReplication ( {
242- prisma,
243- databaseUrl : postgresContainer . getConnectionUri ( ) ,
244- clickhouseUrl : clickhouseContainer . getConnectionUrl ( ) ,
245- redisOptions,
246- } ) ;
247-
248- const { url : newUrl } = await createPostgresContainer ( network , {
249- imageTag : "docker.io/postgres:17" ,
250- } ) ;
251- const prismaNew = new PrismaClient ( { datasources : { db : { url : newUrl } } } ) ;
252- legacyReplicaHolder . client = prisma ;
253-
254- try {
255- const ctx = await seedParents ( prisma , "newshortcircuit" ) ;
256- await mirrorParents ( prismaNew , ctx , "newshortcircuit" ) ;
257-
258- // The (migrated/straggler) run lives ONLY on NEW.
259- await createRun ( prismaNew , ctx , { friendlyId : "run_newOnly" } ) ;
260-
261- const legacySpy = throwingFindFirst ( prisma , "legacyReplica" ) ;
262-
263- const presenter = new NextRunListPresenter ( prisma , clickhouse , {
264- newClient : prismaNew ,
265- legacyReplica : legacySpy ,
266- splitEnabled : true ,
267- } ) ;
268-
269- // If the legacy spy were invoked, this would throw — the test passing IS the proof.
270- const result = await presenter . call (
271- ctx . organizationId ,
272- ctx . environmentId ,
273- callOptions ( ctx )
274- ) ;
275-
276- expect ( result . runs ) . toHaveLength ( 0 ) ;
277- expect ( result . hasAnyRuns ) . toBe ( true ) ;
278- } finally {
279- await prismaNew . $disconnect ( ) ;
280- }
281- }
282- ) ;
283-
284- // genuinely empty. Nothing on either DB. Empty CH page. splitEnabled true. Both
285- // probes run and return null -> the true empty state is preserved.
286- replicationContainerTest (
287- "genuinely empty: both DBs empty reports hasAnyRuns false" ,
288- async ( { clickhouseContainer, redisOptions, postgresContainer, prisma, network } ) => {
289- const { clickhouse } = await setupClickhouseReplication ( {
290- prisma,
291- databaseUrl : postgresContainer . getConnectionUri ( ) ,
292- clickhouseUrl : clickhouseContainer . getConnectionUrl ( ) ,
293- redisOptions,
294- } ) ;
295-
296- const { url : newUrl } = await createPostgresContainer ( network , {
297- imageTag : "docker.io/postgres:17" ,
298- } ) ;
299- const prismaNew = new PrismaClient ( { datasources : { db : { url : newUrl } } } ) ;
300- legacyReplicaHolder . client = prisma ;
301-
302- try {
303- const ctx = await seedParents ( prisma , "trulyempty" ) ;
304- await mirrorParents ( prismaNew , ctx , "trulyempty" ) ;
305-
306- const presenter = new NextRunListPresenter ( prisma , clickhouse , {
307- newClient : prismaNew ,
308- legacyReplica : prisma ,
309- splitEnabled : true ,
310- } ) ;
311-
312- const result = await presenter . call (
313- ctx . organizationId ,
314- ctx . environmentId ,
315- callOptions ( ctx )
316- ) ;
317-
318- expect ( result . runs ) . toHaveLength ( 0 ) ;
319- expect ( result . hasAnyRuns ) . toBe ( false ) ;
320- } finally {
321- await prismaNew . $disconnect ( ) ;
322- }
323- }
324- ) ;
325-
326- // passthrough single-DB (two-arg ctor). One `prisma`, seed a run, empty CH page.
327- // splitEnabled defaults false -> exactly one plain findFirst against the single handle; the
328- // split branch (new/legacy) is structurally never entered (no second handle is injected).
329- // Also covers "served from the replica only" — the ctor exposes no legacy-writer field, so a
330- // no-primary-read guarantee is structural.
331- replicationContainerTest (
332- "passthrough single-DB: two-arg ctor finds the run via the single handle" ,
333- async ( { clickhouseContainer, redisOptions, postgresContainer, prisma } ) => {
334- const { clickhouse } = await setupClickhouseReplication ( {
335- prisma,
336- databaseUrl : postgresContainer . getConnectionUri ( ) ,
337- clickhouseUrl : clickhouseContainer . getConnectionUrl ( ) ,
338- redisOptions,
339- } ) ;
340-
341- legacyReplicaHolder . client = prisma ;
342-
343- const ctx = await seedParents ( prisma , "passthrough" ) ;
344- await createRun ( prisma , ctx , { friendlyId : "run_passthrough" } ) ;
345-
346- // Two-arg ctor: no readThroughDeps -> RunsRepository.readThrough is undefined
347- // (passthrough) and the probe is one plain `this.replica.taskRun.findFirst`.
348- const presenter = new NextRunListPresenter ( prisma , clickhouse ) ;
349-
350- const result = await presenter . call (
351- ctx . organizationId ,
352- ctx . environmentId ,
353- callOptions ( ctx , emptyPageWindow ( ) )
354- ) ;
355-
356- expect ( result . runs ) . toHaveLength ( 0 ) ;
357- expect ( result . hasAnyRuns ) . toBe ( true ) ;
358- }
359- ) ;
360-
152+ describe ( "NextRunListPresenter routed hydrate (legacy + new Postgres)" , ( ) => {
361153 // list hydrate flows through the routed store: split, non-empty CH id-set whose rows are
362154 // split across NEW + the legacy replica. result.runs must be the union, id-desc ordered. This
363155 // proves the deps are threaded so the routed store is actually used.
0 commit comments