1
- import { Container , ContainerModule } from '@theia/core/shared/inversify' ;
1
+ import {
2
+ Container ,
3
+ ContainerModule ,
4
+ injectable ,
5
+ } from '@theia/core/shared/inversify' ;
2
6
import { assert , expect } from 'chai' ;
3
7
import fetch from 'cross-fetch' ;
4
8
import { v4 } from 'uuid' ;
@@ -18,13 +22,14 @@ import queryString = require('query-string');
18
22
const timeout = 60 * 1_000 ;
19
23
20
24
describe ( 'create-api' , ( ) => {
21
- let createApi : CreateApi ;
25
+ let createApi : TestCreateApi ;
22
26
23
27
before ( async function ( ) {
24
28
this . timeout ( timeout ) ;
25
29
try {
26
30
const accessToken = await login ( ) ;
27
- createApi = createContainer ( accessToken ) . get < CreateApi > ( CreateApi ) ;
31
+ createApi =
32
+ createContainer ( accessToken ) . get < TestCreateApi > ( TestCreateApi ) ;
28
33
} catch ( err ) {
29
34
if ( err instanceof LoginFailed ) {
30
35
return this . skip ( ) ;
@@ -42,7 +47,7 @@ describe('create-api', () => {
42
47
const container = new Container ( { defaultScope : 'Singleton' } ) ;
43
48
container . load (
44
49
new ContainerModule ( ( bind ) => {
45
- bind ( CreateApi ) . toSelf ( ) . inSingletonScope ( ) ;
50
+ bind ( TestCreateApi ) . toSelf ( ) . inSingletonScope ( ) ;
46
51
bind ( SketchCache ) . toSelf ( ) . inSingletonScope ( ) ;
47
52
bind ( AuthenticationClientService ) . toConstantValue ( <
48
53
AuthenticationClientService
@@ -223,6 +228,90 @@ describe('create-api', () => {
223
228
expect ( findByName ( otherName , sketches ) ) . to . be . not . undefined ;
224
229
} ) ;
225
230
231
+ it ( 'should not run unnecessary fetches when retrieving all sketches' , async ( ) => {
232
+ const content = 'void setup(){} void loop(){}' ;
233
+ const maxLimit = 50 ; // https://github.com/arduino/arduino-ide/pull/875
234
+ const sketchNames = [ ...Array ( maxLimit - 1 ) . keys ( ) ] . map ( ( ) => v4 ( ) ) ;
235
+
236
+ // #region - sketch count < offset
237
+ await sketchNames
238
+ . map ( ( name ) => createApi . createSketch ( toPosix ( name ) , content ) )
239
+ . reduce ( async ( acc , curr ) => {
240
+ await acc ;
241
+ return curr ;
242
+ } , Promise . resolve ( ) as Promise < unknown > ) ;
243
+
244
+ createApi . resetRequestRecording ( ) ;
245
+ let sketches = await createApi . sketches ( ) ;
246
+ let actualRecording = createApi . requestRecording . slice ( ) ;
247
+
248
+ expect ( sketches . length ) . to . be . equal ( maxLimit - 1 ) ;
249
+ sketchNames . forEach (
250
+ ( name ) => expect ( findByName ( name , sketches ) ) . to . be . not . undefined
251
+ ) ;
252
+
253
+ expect ( actualRecording . length ) . to . be . equal ( 1 ) ;
254
+ let getSketchesRequests = actualRecording . filter (
255
+ ( description ) =>
256
+ description . method === 'GET' &&
257
+ description . pathname === '/create/v2/sketches' &&
258
+ description . query &&
259
+ description . query . includes ( `limit=${ maxLimit } ` )
260
+ ) ;
261
+ expect ( getSketchesRequests . length ) . to . be . equal ( 1 ) ;
262
+ // #endregion
263
+
264
+ // #region - sketch count === offset
265
+ let extraName = v4 ( ) ;
266
+ sketchNames . push ( extraName ) ;
267
+ await createApi . createSketch ( toPosix ( extraName ) , content ) ;
268
+
269
+ createApi . resetRequestRecording ( ) ;
270
+ sketches = await createApi . sketches ( ) ;
271
+ actualRecording = createApi . requestRecording . slice ( ) ;
272
+
273
+ expect ( sketches . length ) . to . be . equal ( maxLimit ) ;
274
+ sketchNames . forEach (
275
+ ( name ) => expect ( findByName ( name , sketches ) ) . to . be . not . undefined
276
+ ) ;
277
+
278
+ expect ( actualRecording . length ) . to . be . equal ( 2 ) ;
279
+ getSketchesRequests = actualRecording . filter (
280
+ ( description ) =>
281
+ description . method === 'GET' &&
282
+ description . pathname === '/create/v2/sketches' &&
283
+ description . query &&
284
+ description . query . includes ( `limit=${ maxLimit } ` )
285
+ ) ;
286
+ expect ( getSketchesRequests . length ) . to . be . equal ( 2 ) ;
287
+ // #endregion
288
+
289
+ // #region - sketch count > offset
290
+ extraName = v4 ( ) ;
291
+ sketchNames . push ( extraName ) ;
292
+ await createApi . createSketch ( toPosix ( extraName ) , content ) ;
293
+
294
+ createApi . resetRequestRecording ( ) ;
295
+ sketches = await createApi . sketches ( ) ;
296
+ actualRecording = createApi . requestRecording . slice ( ) ;
297
+
298
+ expect ( sketches . length ) . to . be . equal ( maxLimit + 1 ) ;
299
+ sketchNames . forEach (
300
+ ( name ) => expect ( findByName ( name , sketches ) ) . to . be . not . undefined
301
+ ) ;
302
+
303
+ expect ( actualRecording . length ) . to . be . equal ( 2 ) ;
304
+ getSketchesRequests = actualRecording . filter (
305
+ ( description ) =>
306
+ description . method === 'GET' &&
307
+ description . pathname === '/create/v2/sketches' &&
308
+ description . query &&
309
+ description . query . includes ( `limit=${ maxLimit } ` )
310
+ ) ;
311
+ expect ( getSketchesRequests . length ) . to . be . equal ( 2 ) ;
312
+ // #endregion
313
+ } ) ;
314
+
226
315
[ '.' , '-' , '_' ] . map ( ( char ) => {
227
316
it ( `should create a new sketch with '${ char } ' in the sketch folder name although it's disallowed from the Create Editor` , async ( ) => {
228
317
const name = `sketch${ char } ` ;
@@ -298,3 +387,44 @@ class LoginFailed extends Error {
298
387
Object . setPrototypeOf ( this , LoginFailed . prototype ) ;
299
388
}
300
389
}
390
+
391
+ @injectable ( )
392
+ class TestCreateApi extends CreateApi {
393
+ private _recording : RequestDescription [ ] = [ ] ;
394
+
395
+ constructor ( ) {
396
+ super ( ) ;
397
+ const originalRun = this [ 'run' ] ;
398
+ this [ 'run' ] = ( url , init , resultProvider ) => {
399
+ this . _recording . push ( createRequestDescription ( url , init ) ) ;
400
+ return originalRun . bind ( this ) ( url , init , resultProvider ) ;
401
+ } ;
402
+ }
403
+
404
+ resetRequestRecording ( ) : void {
405
+ this . _recording = [ ] ;
406
+ }
407
+
408
+ get requestRecording ( ) : RequestDescription [ ] {
409
+ return this . _recording ;
410
+ }
411
+ }
412
+
413
+ interface RequestDescription {
414
+ readonly origin : string ;
415
+ readonly pathname : string ;
416
+ readonly query ?: string ;
417
+
418
+ readonly method ?: string | undefined ;
419
+ readonly serializedBody ?: string | undefined ;
420
+ }
421
+
422
+ function createRequestDescription (
423
+ url : URL ,
424
+ init ?: RequestInit | undefined
425
+ ) : RequestDescription {
426
+ const { origin, pathname, search : query } = url ;
427
+ const method = init ?. method ;
428
+ const serializedBody = init ?. body ?. toString ( ) ;
429
+ return { origin, pathname, query, method, serializedBody } ;
430
+ }
0 commit comments