@@ -6,6 +6,25 @@ import ResultsProviderStub from '../.stubs/ResultsProviderStub';
66import ClientContextStub from '../.stubs/ClientContextStub' ;
77
88describe ( 'ResultSlicer' , ( ) => {
9+ it ( 'should retain at most twice the unread rows while draining a batch' , async ( ) => {
10+ const rows = Array . from ( { length : 256 } , ( _ , value ) => ( { value } ) ) ;
11+ Object . freeze ( rows ) ;
12+ const provider = new ResultsProviderStub ( [ rows ] , [ ] ) ;
13+ const slicer = new ResultSlicer ( new ClientContextStub ( ) , provider ) ;
14+ let consumed = 0 ;
15+
16+ for ( const limit of [ 150 , 25 , 25 , 25 , 25 , 25 ] ) {
17+ // eslint-disable-next-line no-await-in-loop
18+ const chunk = await slicer . fetchNext ( { limit } ) ;
19+ expect ( chunk ) . to . deep . eq ( rows . slice ( consumed , consumed + limit ) ) ;
20+ consumed += chunk . length ;
21+ expect ( slicer [ 'remainingResults' ] . length ) . to . be . at . most ( 2 * ( rows . length - consumed ) ) ;
22+ }
23+
24+ expect ( consumed ) . to . eq ( rows . length ) ;
25+ expect ( await slicer . hasMore ( ) ) . to . be . false ;
26+ } ) ;
27+
928 it ( 'should carry leftovers across several calls without mutating the source batch' , async ( ) => {
1029 const rows = Array . from ( { length : 250 } , ( _ , index ) => index ) ;
1130 Object . freeze ( rows ) ;
@@ -135,6 +154,57 @@ describe('ResultSlicer', () => {
135154 expect ( await slicer . fetchNext ( { limit : 3 } ) ) . to . deep . eq ( [ 1 , 2 , 3 ] ) ;
136155 } ) ;
137156
157+ it ( 'should preserve leftovers for a non-numeric limit from a JavaScript caller' , async ( ) => {
158+ const provider = new ResultsProviderStub (
159+ [
160+ [ 1 , 2 , 3 , 4 , 5 , 6 ] ,
161+ [ 7 , 8 , 9 ] ,
162+ ] ,
163+ [ ] ,
164+ ) ;
165+ const fetchNext = sinon . spy ( provider , 'fetchNext' ) ;
166+ const slicer = new ResultSlicer ( new ClientContextStub ( ) , provider ) ;
167+ const options = { limit : 'abc' as unknown as number } ;
168+
169+ expect ( await slicer . fetchNext ( options ) ) . to . deep . eq ( [ ] ) ;
170+ expect ( fetchNext . called ) . to . be . false ;
171+ expect ( await slicer . fetchNext ( { limit : 3 } ) ) . to . deep . eq ( [ 1 , 2 , 3 ] ) ;
172+ expect ( await slicer . fetchNext ( options ) ) . to . deep . eq ( [ 4 , 5 , 6 ] ) ;
173+ expect ( fetchNext . callCount ) . to . eq ( 1 ) ;
174+ expect ( await slicer . hasMore ( ) ) . to . be . true ;
175+ expect ( await slicer . fetchNext ( { limit : 3 } ) ) . to . deep . eq ( [ 7 , 8 , 9 ] ) ;
176+ expect ( await slicer . hasMore ( ) ) . to . be . false ;
177+ } ) ;
178+
179+ it ( 'should normalize numeric string limits before passing them to the source' , async ( ) => {
180+ const provider = new ResultsProviderStub (
181+ [
182+ [ 1 , 2 ] ,
183+ [ 3 , 4 , 5 ] ,
184+ ] ,
185+ [ ] ,
186+ ) ;
187+ const fetchNext = sinon . spy ( provider , 'fetchNext' ) ;
188+ const slicer = new ResultSlicer ( new ClientContextStub ( ) , provider ) ;
189+
190+ expect ( await slicer . fetchNext ( { limit : '3.5' as unknown as number } ) ) . to . deep . eq ( [ 1 , 2 , 3 ] ) ;
191+ expect ( fetchNext . firstCall . args ) . to . deep . eq ( [ { limit : 3.5 } ] ) ;
192+ expect ( await slicer . fetchNext ( { limit : '1.5' as unknown as number } ) ) . to . deep . eq ( [ 4 ] ) ;
193+ expect ( await slicer . fetchNext ( { limit : 3 } ) ) . to . deep . eq ( [ 5 ] ) ;
194+ expect ( await slicer . hasMore ( ) ) . to . be . false ;
195+ } ) ;
196+
197+ it ( 'should proxy unbuffered fetches without coercing the limit' , async ( ) => {
198+ const rows = [ 1 , 2 , 3 ] ;
199+ const provider = new ResultsProviderStub ( [ rows ] , [ ] ) ;
200+ const fetchNext = sinon . spy ( provider , 'fetchNext' ) ;
201+ const slicer = new ResultSlicer ( new ClientContextStub ( ) , provider ) ;
202+ const options = { limit : Symbol ( 'ignored' ) as unknown as number , disableBuffering : true } ;
203+
204+ expect ( await slicer . fetchNext ( options ) ) . to . eq ( rows ) ;
205+ expect ( fetchNext . lastCall . args ) . to . deep . eq ( [ options ] ) ;
206+ } ) ;
207+
138208 for ( const limit of [ 3 , 3.5 ] ) {
139209 it ( `should truncate fractional counts after fetching with limit ${ limit } ` , async ( ) => {
140210 const provider = new ResultsProviderStub ( [ [ 1 , 2 , 3 , 4 , 5 , 6 ] ] , [ ] ) ;
0 commit comments