@@ -4,6 +4,7 @@ const util = require('util')
4
4
5
5
const fs = require ( 'fs' )
6
6
const path = require ( 'path' )
7
+ const requireInject = require ( 'require-inject' )
7
8
const ssri = require ( 'ssri' )
8
9
const Tacks = require ( 'tacks' )
9
10
const { test } = require ( 'tap' )
@@ -16,7 +17,26 @@ const CacheContent = require('./util/cache-content')
16
17
17
18
const read = require ( '../lib/content/read' )
18
19
19
- test ( 'read: returns a BB with cache content data' , function ( t ) {
20
+ // defines reusable errors
21
+ const genericError = new Error ( 'ERR' )
22
+ genericError . code = 'ERR'
23
+ const permissionError = new Error ( 'EPERM' )
24
+ permissionError . code = 'EPERM'
25
+
26
+ // helpers
27
+ const getRead = ( opts ) => requireInject ( '../lib/content/read' , opts )
28
+ const getReadLstatFailure = ( err ) => getRead ( {
29
+ 'graceful-fs' : Object . assign ( { } , require ( 'graceful-fs' ) , {
30
+ lstat ( path , cb ) {
31
+ cb ( err )
32
+ } ,
33
+ lstatSync ( ) {
34
+ throw err
35
+ }
36
+ } )
37
+ } )
38
+
39
+ test ( 'read: returns a Promise with cache content data' , function ( t ) {
20
40
const CONTENT = Buffer . from ( 'foobarbaz' )
21
41
const INTEGRITY = ssri . fromData ( CONTENT )
22
42
const fixture = new Tacks (
@@ -176,6 +196,159 @@ test('read: errors if content size does not match size option', function (t) {
176
196
} )
177
197
} )
178
198
199
+ test ( 'read: error while parsing provided integrity data' , function ( t ) {
200
+ const INTEGRITY = 'sha1-deadbeef'
201
+ const mockedRead = getRead ( {
202
+ ssri : {
203
+ parse ( sri ) {
204
+ throw genericError
205
+ }
206
+ }
207
+ } )
208
+
209
+ t . plan ( 1 )
210
+ return t . rejects (
211
+ mockedRead ( CACHE , INTEGRITY ) ,
212
+ genericError ,
213
+ 'should reject promise upon catching internal errors'
214
+ )
215
+ } )
216
+
217
+ test ( 'read: unknown error parsing nested integrity data' , function ( t ) {
218
+ const INTEGRITY = 'sha1-deadbeef sha1-13371337'
219
+
220
+ // patches method in order to force a last error scenario
221
+ const mockedRead = getRead ( {
222
+ ssri : {
223
+ parse ( sri ) {
224
+ if ( sri !== INTEGRITY ) {
225
+ throw genericError
226
+ }
227
+ return ssri . parse ( sri )
228
+ }
229
+ }
230
+ } )
231
+
232
+ t . plan ( 1 )
233
+ return t . rejects (
234
+ mockedRead ( CACHE , INTEGRITY ) ,
235
+ genericError ,
236
+ 'should throw unknown errors'
237
+ )
238
+ } )
239
+
240
+ test ( 'read: returns only first result if other hashes fails' , function ( t ) {
241
+ // sets up a cache that has multiple entries under the
242
+ // same algorithm but then only one has real contents in the fs
243
+ const CONTENT = {
244
+ foo : Buffer . from ( 'foo' ) ,
245
+ bar : Buffer . from ( 'bar' )
246
+ }
247
+ const INTEGRITY = ssri . fromData ( CONTENT . foo ) . concat (
248
+ ssri . fromData ( CONTENT . bar )
249
+ )
250
+ const fixture = new Tacks (
251
+ CacheContent ( {
252
+ [ INTEGRITY . sha512 [ 1 ] ] : CONTENT . bar
253
+ } )
254
+ )
255
+ fixture . create ( CACHE )
256
+
257
+ t . plan ( 1 )
258
+ return t . resolveMatch (
259
+ read ( CACHE , INTEGRITY ) ,
260
+ CONTENT . bar ,
261
+ 'should return only the first valid result'
262
+ )
263
+ } )
264
+
265
+ test ( 'read: opening large files' , function ( t ) {
266
+ const mockedRead = getRead ( {
267
+ 'graceful-fs' : Object . assign ( { } , require ( 'graceful-fs' ) , {
268
+ lstat ( path , cb ) {
269
+ cb ( null , { size : Number . MAX_SAFE_INTEGER } )
270
+ }
271
+ } ) ,
272
+ 'fs-minipass' : {
273
+ ReadStream : class {
274
+ constructor ( path , opts ) {
275
+ t . matches (
276
+ opts ,
277
+ {
278
+ readSize : 64 * 1024 * 1024 ,
279
+ size : Number . MAX_SAFE_INTEGER
280
+ } ,
281
+ 'should use fs-minipass interface'
282
+ )
283
+ }
284
+ }
285
+ } ,
286
+ 'minipass-pipeline' : Array
287
+ } )
288
+
289
+ t . plan ( 1 )
290
+ mockedRead ( CACHE , 'sha1-deadbeef' )
291
+ } )
292
+
293
+ test ( 'read.sync: unknown error parsing nested integrity data' , ( t ) => {
294
+ const INTEGRITY = 'sha1-deadbeef sha1-13371337'
295
+
296
+ // patches method in order to force a last error scenario
297
+ const mockedRead = getRead ( {
298
+ ssri : {
299
+ parse ( sri ) {
300
+ if ( sri !== INTEGRITY ) {
301
+ throw genericError
302
+ }
303
+ return ssri . parse ( sri )
304
+ }
305
+ }
306
+ } )
307
+
308
+ t . throws (
309
+ ( ) => mockedRead . sync ( CACHE , INTEGRITY ) ,
310
+ genericError ,
311
+ 'should throw last error found when parsing multiple hashes'
312
+ )
313
+ t . done ( )
314
+ } )
315
+
316
+ test ( 'read.sync: cache contains mismatching data' , ( t ) => {
317
+ const CONTENT = Buffer . from ( 'foobarbaz' )
318
+ const INTEGRITY = ssri . fromData ( CONTENT )
319
+ const fixture = new Tacks (
320
+ CacheContent ( {
321
+ [ INTEGRITY ] : CONTENT . slice ( 3 )
322
+ } )
323
+ )
324
+ fixture . create ( CACHE )
325
+
326
+ t . throws (
327
+ ( ) => read . sync ( CACHE , INTEGRITY ) ,
328
+ { code : 'EINTEGRITY' } ,
329
+ 'should throw integrity error'
330
+ )
331
+ t . done ( )
332
+ } )
333
+
334
+ test ( 'read.sync: content size value does not match option' , ( t ) => {
335
+ const CONTENT = Buffer . from ( 'foobarbaz' )
336
+ const INTEGRITY = ssri . fromData ( CONTENT )
337
+ const fixture = new Tacks (
338
+ CacheContent ( {
339
+ [ INTEGRITY ] : CONTENT . slice ( 3 )
340
+ } )
341
+ )
342
+ fixture . create ( CACHE )
343
+
344
+ t . throws (
345
+ ( ) => read . sync ( CACHE , INTEGRITY , { size : CONTENT . length } ) ,
346
+ { code : 'EBADSIZE' } ,
347
+ 'should throw size error'
348
+ )
349
+ t . done ( )
350
+ } )
351
+
179
352
test ( 'hasContent: tests content existence' , ( t ) => {
180
353
const fixture = new Tacks (
181
354
CacheContent ( {
@@ -200,6 +373,37 @@ test('hasContent: tests content existence', (t) => {
200
373
] )
201
374
} )
202
375
376
+ test ( 'hasContent: permission error' , ( t ) => {
377
+ // setup a syntetic permission error
378
+ const mockedRead = getReadLstatFailure ( permissionError )
379
+
380
+ t . plan ( 1 )
381
+ t . rejects (
382
+ mockedRead . hasContent ( CACHE , 'sha1-deadbeef sha1-13371337' ) ,
383
+ permissionError ,
384
+ 'should reject on permission errors'
385
+ )
386
+ } )
387
+
388
+ test ( 'hasContent: generic error' , ( t ) => {
389
+ const mockedRead = getReadLstatFailure ( genericError )
390
+
391
+ t . plan ( 1 )
392
+ t . resolves (
393
+ mockedRead . hasContent ( CACHE , 'sha1-deadbeef sha1-13371337' ) ,
394
+ 'should not reject on generic errors'
395
+ )
396
+ } )
397
+
398
+ test ( 'hasContent: no integrity provided' , ( t ) => {
399
+ t . resolveMatch (
400
+ read . hasContent ( CACHE , '' ) ,
401
+ false ,
402
+ 'should resolve with a value of false'
403
+ )
404
+ t . done ( )
405
+ } )
406
+
203
407
test ( 'hasContent.sync: checks content existence synchronously' , ( t ) => {
204
408
const fixture = new Tacks (
205
409
CacheContent ( {
@@ -224,6 +428,36 @@ test('hasContent.sync: checks content existence synchronously', (t) => {
224
428
t . done ( )
225
429
} )
226
430
431
+ test ( 'hasContent.sync: permission error' , ( t ) => {
432
+ const mockedRead = getReadLstatFailure ( permissionError )
433
+
434
+ t . throws (
435
+ ( ) => mockedRead . hasContent . sync ( CACHE , 'sha1-deadbeef sha1-13371337' ) ,
436
+ permissionError ,
437
+ 'should throw on permission errors'
438
+ )
439
+ t . done ( )
440
+ } )
441
+
442
+ test ( 'hasContent.sync: generic error' , ( t ) => {
443
+ const mockedRead = getReadLstatFailure ( genericError )
444
+
445
+ t . notOk (
446
+ mockedRead . hasContent . sync ( CACHE , 'sha1-deadbeef sha1-13371337' ) ,
447
+ 'should not throw on generic errors'
448
+ )
449
+ t . done ( )
450
+ } )
451
+
452
+ test ( 'hasContent.sync: no integrity provided' , ( t ) => {
453
+ t . equal (
454
+ read . hasContent . sync ( CACHE , '' ) ,
455
+ false ,
456
+ 'should returns false if no integrity provided'
457
+ )
458
+ t . done ( )
459
+ } )
460
+
227
461
test (
228
462
'copy: copies content to a destination path' ,
229
463
{
@@ -270,3 +504,14 @@ test(
270
504
t . done ( )
271
505
}
272
506
)
507
+
508
+ test ( 'copyFile not supported by file system' , ( t ) => {
509
+ const mockedRead = getRead ( {
510
+ 'graceful-fs' : Object . assign ( { } , require ( 'graceful-fs' ) , {
511
+ copyFile : undefined
512
+ } )
513
+ } )
514
+
515
+ t . notOk ( mockedRead . copy , 'should not define copy' )
516
+ t . done ( )
517
+ } )
0 commit comments