@@ -7,6 +7,7 @@ import { heteroRunOpsPostgresTest, makeNShardRunOpsPostgresTest } from "@interna
77import type { RunOpsPrismaClient } from "@internal/run-ops-database" ;
88import type { PrismaClient } from "@trigger.dev/database" ;
99import { generateRunOpsId , generateRunOpsIdV2 } from "@trigger.dev/core/v3/isomorphic" ;
10+ import { PostgresRunStore } from "@internal/run-store" ;
1011import { describe , expect , vi } from "vitest" ;
1112import type { PrismaReplicaClient } from "~/db.server" ;
1213import type { AuthenticatedEnvironment } from "~/services/apiAuth.server" ;
@@ -475,4 +476,66 @@ describe("ApiBatchResultsPresenter split mode — real run-ops dedicated schema
475476 } ,
476477 180_000
477478 ) ;
479+
480+ // A gen-2 BATCH id is minted onto its shard, so the batch row (not just its members) lives
481+ // only there. Both read paths must route the row by its own id; before this, the gen-1
482+ // probes missed and the endpoint 404'd for a batch that `batch.retrieve` found.
483+ for ( const splitEnabled of [ true , false ] ) {
484+ oneShardTest (
485+ `a gen-2 batch whose row lives only on its shard returns its members (split ${
486+ splitEnabled ? "on" : "off"
487+ } )`,
488+ async ( { legacyPrisma, newPrisma, shardPrismas } ) => {
489+ const shardPrisma = shardPrismas [ 0 ] ! ;
490+ const ctx = await seedLegacyEnv ( legacyPrisma , `gen2-batch-row-${ splitEnabled } ` ) ;
491+
492+ const memberIds = [ generateRunOpsIdV2 ( "a" ) , generateRunOpsIdV2 ( "a" ) ] ;
493+ for ( const [ i , id ] of memberIds . entries ( ) ) {
494+ await seedNewMember (
495+ shardPrisma ,
496+ { envId : ctx . environment . id , orgId : ctx . organization . id , projectId : ctx . project . id } ,
497+ {
498+ id,
499+ friendlyId : `run_shard_${ i } ` ,
500+ status : "COMPLETED_SUCCESSFULLY" ,
501+ output : JSON . stringify ( { i } ) ,
502+ }
503+ ) ;
504+ }
505+
506+ const batchFriendlyId = `batch_${ generateRunOpsIdV2 ( "a" ) } ` ;
507+ await seedBatchOnNew ( shardPrisma , ctx . environment . id , batchFriendlyId , memberIds ) ;
508+
509+ const shardReplicas = new Map ( [ [ "a" , shardPrisma as unknown as PrismaReplicaClient ] ] ) ;
510+ const presenter = splitEnabled
511+ ? new ApiBatchResultsPresenter ( throwingPrisma , throwingPrisma , {
512+ splitEnabled : true ,
513+ newClient : newPrisma as unknown as PrismaReplicaClient ,
514+ legacyReplica : legacyPrisma as unknown as PrismaReplicaClient ,
515+ shardReplicas,
516+ } )
517+ : // Passthrough hydrates members through the primary handle; the REPLICA (where the row
518+ // is read without the fix) is the gen-1 store, which does not hold this batch.
519+ new ApiBatchResultsPresenter (
520+ shardPrisma as unknown as PrismaReplicaClient ,
521+ newPrisma as unknown as PrismaReplicaClient ,
522+ { splitEnabled : false , shardReplicas } ,
523+ new PostgresRunStore ( {
524+ prisma : shardPrisma as any ,
525+ readOnlyPrisma : shardPrisma as any ,
526+ } )
527+ ) ;
528+
529+ const result = await presenter . call ( batchFriendlyId , env ( ctx ) ) ;
530+
531+ expect ( result ) . toBeDefined ( ) ;
532+ expect ( result ! . id ) . toBe ( batchFriendlyId ) ;
533+ expect ( result ! . items . map ( ( item ) => ( item as { output ?: string } ) . output ) ) . toEqual ( [
534+ JSON . stringify ( { i : 0 } ) ,
535+ JSON . stringify ( { i : 1 } ) ,
536+ ] ) ;
537+ } ,
538+ 180_000
539+ ) ;
540+ }
478541} ) ;
0 commit comments