Skip to content

Commit cdb4e48

Browse files
ruyadornoisaacs
authored andcommittedOct 2, 2019
test: Add lib/content/read.js tests
- Add test coverage to lib/content/read.js module - Added coverage ignore lines to platform specific checks PR-URL: #15 Credit: @ruyadorno Close: #15 Reviewed-by: @isaacs
1 parent 6a6716a commit cdb4e48

File tree

2 files changed

+248
-1
lines changed

2 files changed

+248
-1
lines changed
 

‎lib/content/read.js

+2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ function hasContent (cache, integrity) {
127127
return false
128128
}
129129
if (err.code === 'EPERM') {
130+
/* istanbul ignore else */
130131
if (process.platform !== 'win32') {
131132
throw err
132133
} else {
@@ -151,6 +152,7 @@ function hasContentSync (cache, integrity) {
151152
return false
152153
}
153154
if (err.code === 'EPERM') {
155+
/* istanbul ignore else */
154156
if (process.platform !== 'win32') {
155157
throw err
156158
} else {

‎test/content.read.js

+246-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const util = require('util')
44

55
const fs = require('fs')
66
const path = require('path')
7+
const requireInject = require('require-inject')
78
const ssri = require('ssri')
89
const Tacks = require('tacks')
910
const { test } = require('tap')
@@ -16,7 +17,26 @@ const CacheContent = require('./util/cache-content')
1617

1718
const read = require('../lib/content/read')
1819

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) {
2040
const CONTENT = Buffer.from('foobarbaz')
2141
const INTEGRITY = ssri.fromData(CONTENT)
2242
const fixture = new Tacks(
@@ -176,6 +196,159 @@ test('read: errors if content size does not match size option', function (t) {
176196
})
177197
})
178198

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+
179352
test('hasContent: tests content existence', (t) => {
180353
const fixture = new Tacks(
181354
CacheContent({
@@ -200,6 +373,37 @@ test('hasContent: tests content existence', (t) => {
200373
])
201374
})
202375

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+
203407
test('hasContent.sync: checks content existence synchronously', (t) => {
204408
const fixture = new Tacks(
205409
CacheContent({
@@ -224,6 +428,36 @@ test('hasContent.sync: checks content existence synchronously', (t) => {
224428
t.done()
225429
})
226430

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+
227461
test(
228462
'copy: copies content to a destination path',
229463
{
@@ -270,3 +504,14 @@ test(
270504
t.done()
271505
}
272506
)
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

Comments
 (0)
Please sign in to comment.