@@ -296,10 +296,10 @@ describe('[#5038] the matched row set is read ONCE', () => {
296296describe ( '[#5038] an oversized matched set is refused, never silently downgraded' , ( ) => {
297297 it ( 'rejects past the ceiling, naming the count, the limit and the routes out' , async ( ) => {
298298 const fired : string [ ] = [ ] ;
299- const { engine } = await boot ( [ hook ( 'per_row' , 'afterUpdate' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
299+ const { engine, driver } = await boot ( [ hook ( 'per_row' , 'afterUpdate' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
300300
301301 const over = ObjectQL . MAX_BULK_PER_ROW_HOOK_ROWS + 1 ;
302- await seedTasks ( engine , Array . from ( { length : over } , ( _ , i ) => ( { title : `t ${ i } ` , status : 'todo' } ) ) ) ;
302+ seedCeilingRows ( driver , over , 'todo' ) ;
303303
304304 const err = await engine
305305 . update ( 'task' , { status : 'done' } , { multi : true , where : { status : 'todo' } } as any )
@@ -322,9 +322,9 @@ describe('[#5038] an oversized matched set is refused, never silently downgraded
322322 } ) ;
323323
324324 it ( 'does not apply to an object with no after-hooks — a big batch still writes' , async ( ) => {
325- const { engine } = await boot ( [ ] ) ;
325+ const { engine, driver } = await boot ( [ ] ) ;
326326 const over = ObjectQL . MAX_BULK_PER_ROW_HOOK_ROWS + 1 ;
327- await seedTasks ( engine , Array . from ( { length : over } , ( _ , i ) => ( { title : `t ${ i } ` , status : 'todo' } ) ) ) ;
327+ seedCeilingRows ( driver , over , 'todo' ) ;
328328
329329 const affected = await engine . update (
330330 'task' , { status : 'done' } , { multi : true , where : { status : 'todo' } } as any ,
@@ -870,8 +870,8 @@ describe('[#5574 / D6] one ceiling, BOTH phases, checked before the first dispat
870870
871871 it ( 'refuses an over-ceiling batch on the BEFORE phase — zero handlers, zero rows written' , async ( ) => {
872872 const fired : string [ ] = [ ] ;
873- const { engine } = await boot ( [ hook ( 'per_row_before' , 'beforeUpdate' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
874- await seedTasks ( engine , Array . from ( { length : over } , ( _ , i ) => ( { title : `t ${ i } ` , status : 'todo' } ) ) ) ;
873+ const { engine, driver } = await boot ( [ hook ( 'per_row_before' , 'beforeUpdate' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
874+ seedCeilingRows ( driver , over , 'todo' ) ;
875875
876876 const err = await engine
877877 . update ( 'task' , { status : 'done' } , { multi : true , where : { status : 'todo' } } )
@@ -890,8 +890,8 @@ describe('[#5574 / D6] one ceiling, BOTH phases, checked before the first dispat
890890
891891 it ( 'refuses an over-ceiling bulk DELETE on the before phase too' , async ( ) => {
892892 const fired : string [ ] = [ ] ;
893- const { engine } = await boot ( [ hook ( 'per_row_before' , 'beforeDelete' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
894- await seedTasks ( engine , Array . from ( { length : over } , ( _ , i ) => ( { title : `t ${ i } ` , status : 'stale' } ) ) ) ;
893+ const { engine, driver } = await boot ( [ hook ( 'per_row_before' , 'beforeDelete' , ( ) => { fired . push ( 'x' ) ; } ) ] ) ;
894+ seedCeilingRows ( driver , over , 'stale' ) ;
895895
896896 const err = await engine
897897 . delete ( 'task' , { multi : true , where : { status : 'stale' } } )
@@ -908,12 +908,12 @@ describe('[#5574 / D6] one ceiling, BOTH phases, checked before the first dispat
908908 // refuse honest batches, which is the failure nobody reports as a bug.
909909 const beforeFired : string [ ] = [ ] ;
910910 const afterFired : string [ ] = [ ] ;
911- const { engine } = await boot ( [
911+ const { engine, driver } = await boot ( [
912912 hook ( 'pre' , 'beforeUpdate' , ( ) => { beforeFired . push ( 'x' ) ; } ) ,
913913 hook ( 'post' , 'afterUpdate' , ( ) => { afterFired . push ( 'x' ) ; } ) ,
914914 ] ) ;
915915 const at = MAX_BULK_PER_ROW_HOOK_ROWS ;
916- await seedTasks ( engine , Array . from ( { length : at } , ( _ , i ) => ( { title : `t ${ i } ` , status : 'todo' } ) ) ) ;
916+ seedCeilingRows ( driver , at , 'todo' ) ;
917917
918918 const affected = await engine . update (
919919 'task' , { status : 'done' } , { multi : true , where : { status : 'todo' } } as any ,
@@ -1186,6 +1186,24 @@ async function seedTasks(engine: ObjectQL, rows: Record<string, unknown>[]): Pro
11861186 return Array . isArray ( written ) ? written : [ written ] ;
11871187}
11881188
1189+ /**
1190+ * The matched set for the ceiling cases (section 4, D6): `count` task rows, all
1191+ * in `status`, written straight into the stub driver's store.
1192+ *
1193+ * Why not `seedTasks`: the ceiling is judged on what the driver's `find`
1194+ * returns, so how those rows got into the store is not what these cases pin —
1195+ * and pushing 10 001 rows through `engine.insert` cost about 40 µs a row, five
1196+ * times over (about 3 s of this file's 3.6 s of wall, on a quiet box). The
1197+ * stub's `create` is the only thing the engine's insert path ends in, and the
1198+ * engine adds nothing to a row on the way (a seeded row and an `engine.insert`
1199+ * one are the same `{ title, status, id }`), so `seed` lays down the very rows
1200+ * `seedTasks` would. Small seeds elsewhere in this file still go through
1201+ * `engine.insert`.
1202+ */
1203+ function seedCeilingRows ( driver : any , count : number , status : string ) : void {
1204+ driver . seed ( 'task' , Array . from ( { length : count } , ( _ , i ) => ( { title : `t${ i } ` , status } ) ) ) ;
1205+ }
1206+
11891207function makeStubDriver ( ) : any {
11901208 const stores = new Map < string , Map < string , Record < string , unknown > > > ( ) ;
11911209 const storeFor = ( o : string ) => {
@@ -1194,6 +1212,11 @@ function makeStubDriver(): any {
11941212 return s ;
11951213 } ;
11961214 let nextId = 0 ;
1215+ const put = ( o : string , data : Record < string , unknown > ) => {
1216+ nextId += 1 ;
1217+ const id = ( data . id as string ) ?? `r_${ nextId } ` ;
1218+ const row = { ...data , id } ; storeFor ( o ) . set ( id , row ) ; return row ;
1219+ } ;
11971220 const matches = ( row : Record < string , unknown > , where : any ) : boolean => {
11981221 if ( ! where || typeof where !== 'object' ) return true ;
11991222 for ( const [ k , v ] of Object . entries ( where ) ) {
@@ -1217,11 +1240,10 @@ function makeStubDriver(): any {
12171240 return Array . from ( storeFor ( o ) . values ( ) ) . filter ( ( r ) => matches ( r , ast ?. where ) ) ;
12181241 } ,
12191242 async findOne ( o : string , ast : any ) { for ( const r of storeFor ( o ) . values ( ) ) if ( matches ( r , ast ?. where ) ) return r ; return null ; } ,
1220- async create ( o : string , data : Record < string , unknown > ) {
1221- nextId += 1 ;
1222- const id = ( data . id as string ) ?? `r_${ nextId } ` ;
1223- const row = { ...data , id } ; storeFor ( o ) . set ( id , row ) ; return row ;
1224- } ,
1243+ async create ( o : string , data : Record < string , unknown > ) { return put ( o , data ) ; } ,
1244+ /** Store `rows` exactly as `create` would, skipping the engine's insert path
1245+ * (see `seedCeilingRows`). Synchronous: nothing here awaits. */
1246+ seed ( o : string , rows : Record < string , unknown > [ ] ) { for ( const r of rows ) put ( o , r ) ; } ,
12251247 async update ( o : string , id : string , data : Record < string , unknown > ) {
12261248 d . updateCalls += 1 ;
12271249 const s = storeFor ( o ) ; const cur = s . get ( id ) ; if ( ! cur ) return null ;
0 commit comments