11import { describe , expect , it } from "vitest" ;
22import { RunId } from "@trigger.dev/core/v3/isomorphic" ;
33import {
4+ clientForShardKey ,
45 resolveIdempotencyDedupClient ,
56 type ResolveIdempotencyClientDeps ,
67} from "./idempotencyResidency.server" ;
@@ -9,20 +10,30 @@ import {
910const FALLBACK = { __tag : "fallback" } as never ;
1011const NEW_CLIENT = { __tag : "new" } as never ;
1112const LEGACY_CLIENT = { __tag : "legacy" } as never ;
13+ const SHARD_A_CLIENT = { __tag : "shard-a" } as never ;
14+
15+ function clientMap ( ) {
16+ return new Map ( [
17+ [ "new" , NEW_CLIENT ] ,
18+ [ "legacy" , LEGACY_CLIENT ] ,
19+ [ "a" , SHARD_A_CLIENT ] ,
20+ ] ) ;
21+ }
1222
1323function makeDeps ( over : Partial < ResolveIdempotencyClientDeps > ) : ResolveIdempotencyClientDeps {
1424 return {
1525 isSplitEnabled : async ( ) => true ,
1626 fallbackClient : FALLBACK ,
17- newClient : NEW_CLIENT ,
18- legacyClient : LEGACY_CLIENT ,
27+ clientFor : ( key ) => clientMap ( ) . get ( key ) ,
1928 resolveMintKind : async ( ) => "runOpsId" ,
29+ // Kept as an injected seam: the real resolveShard is total, so only an injected
30+ // classifier can exercise the throw-to-fallback arm below.
2031 classify : ( id ) => {
21- if ( id . length === 26 && id [ 25 ] === "1" ) return "NEW" ;
22- if ( id . length === 25 ) return "LEGACY" ;
32+ if ( id . length === 26 && id [ 25 ] === "2" ) return id [ 24 ] ! ;
33+ if ( id . length === 26 && id [ 25 ] === "1" ) return "new" ;
34+ if ( id . length === 25 ) return "legacy" ;
2335 throw new Error ( `unclassifiable: ${ id . length } ` ) ;
2436 } ,
25- isMigrated : undefined ,
2637 ...over ,
2738 } ;
2839}
@@ -72,29 +83,51 @@ describe("resolveIdempotencyDedupClient", () => {
7283 expect ( client ) . toBe ( LEGACY_CLIENT ) ;
7384 } ) ;
7485
75- it ( "routes a swept (migrated) cuid-parent child to the NEW client" , async ( ) => {
76- const cuidParent = RunId . toFriendlyId ( "c" . repeat ( 25 ) ) ;
86+ it ( "falls back to the fallback client when a present parent id is unclassifiable" , async ( ) => {
7787 const client = await resolveIdempotencyDedupClient (
78- { environmentForMint : env , parentRunFriendlyId : cuidParent } ,
79- makeDeps ( { isMigrated : async ( ) => true } )
88+ { environmentForMint : env , parentRunFriendlyId : "run_not-a-valid-length" } ,
89+ makeDeps ( { } )
8090 ) ;
81- expect ( client ) . toBe ( NEW_CLIENT ) ;
91+ expect ( client ) . toBe ( FALLBACK ) ;
8292 } ) ;
8393
84- it ( "routes a non-migrated cuid-parent child to the LEGACY client even when isMigrated is provided " , async ( ) => {
85- const cuidParent = RunId . toFriendlyId ( "d " . repeat ( 25 ) ) ;
94+ it ( "routes a child to its OWN SHARD client when the parent is a gen-2 id " , async ( ) => {
95+ const genTwoParent = RunId . toFriendlyId ( "e " . repeat ( 24 ) + "a2" ) ;
8696 const client = await resolveIdempotencyDedupClient (
87- { environmentForMint : env , parentRunFriendlyId : cuidParent } ,
88- makeDeps ( { isMigrated : async ( ) => false } )
97+ { environmentForMint : env , parentRunFriendlyId : genTwoParent } ,
98+ makeDeps ( { resolveMintKind : async ( ) => "cuid" } ) // mint flag must NOT win for a child
8999 ) ;
90- expect ( client ) . toBe ( LEGACY_CLIENT ) ;
100+ expect ( client ) . toBe ( SHARD_A_CLIENT ) ;
91101 } ) ;
92102
93- it ( "falls back to the fallback client when a present parent id is unclassifiable" , async ( ) => {
103+ it ( "falls back and logs when a gen-2 parent names an unconfigured shard key" , async ( ) => {
104+ const errors : unknown [ ] = [ ] ;
105+ const genTwoParent = RunId . toFriendlyId ( "f" . repeat ( 24 ) + "z2" ) ;
94106 const client = await resolveIdempotencyDedupClient (
95- { environmentForMint : env , parentRunFriendlyId : "run_not-a-valid-length" } ,
96- makeDeps ( { } )
107+ { environmentForMint : env , parentRunFriendlyId : genTwoParent } ,
108+ makeDeps ( { logger : { error : ( _m , meta ) => errors . push ( meta ) } } )
97109 ) ;
98110 expect ( client ) . toBe ( FALLBACK ) ;
111+ expect ( errors ) . toHaveLength ( 1 ) ;
112+ } ) ;
113+ } ) ;
114+
115+ describe ( "clientForShardKey" , ( ) => {
116+ it ( "selects the same client the map holds for each reserved key and shard key" , ( ) => {
117+ const clients = clientMap ( ) ;
118+ const clientFor = ( key : string ) => clients . get ( key ) ;
119+ expect ( clientForShardKey ( "new" , clientFor , FALLBACK ) ) . toBe ( NEW_CLIENT ) ;
120+ expect ( clientForShardKey ( "legacy" , clientFor , FALLBACK ) ) . toBe ( LEGACY_CLIENT ) ;
121+ expect ( clientForShardKey ( "a" , clientFor , FALLBACK ) ) . toBe ( SHARD_A_CLIENT ) ;
122+ } ) ;
123+
124+ it ( "returns the fallback and logs for a key the map does not hold" , ( ) => {
125+ const errors : unknown [ ] = [ ] ;
126+ const map = clientMap ( ) ;
127+ const client = clientForShardKey ( "z" , ( key ) => map . get ( key ) , FALLBACK , {
128+ error : ( _m , meta ) => errors . push ( meta ) ,
129+ } ) ;
130+ expect ( client ) . toBe ( FALLBACK ) ;
131+ expect ( errors ) . toHaveLength ( 1 ) ;
99132 } ) ;
100133} ) ;
0 commit comments