Skip to content

Commit

Permalink
tests for npm cache command, and 2 tiny bugfixes
Browse files Browse the repository at this point in the history
* Attach EUSAGE code to usage errors
* add verify to completions

PR-URL: #2122
Credit: @nlf
Close: #2122
Reviewed-by: @isaacs
  • Loading branch information
nlf authored and isaacs committed Nov 6, 2020
1 parent f0efe96 commit 7046fe1
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 10 deletions.
20 changes: 10 additions & 10 deletions lib/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ const usage = usageUtil('cache',
const completion = (opts, cb) => {
const argv = opts.conf.argv.remain
if (argv.length === 2)
return cb(null, ['add', 'clean'])
return cb(null, ['add', 'clean', 'verify'])

// TODO - eventually...
switch (argv[2]) {
case 'verify':
case 'clean':
case 'add':
return cb(null, [])
Expand All @@ -40,11 +41,11 @@ const cache = async (args) => {
case 'rm': case 'clear': case 'clean':
return await clean(args)
case 'add':
return await add(args, npm.prefix)
return await add(args)
case 'verify': case 'check':
return await verify()
default:
throw usage
throw Object.assign(new Error(usage), { code: 'EUSAGE' })
}
}

Expand Down Expand Up @@ -77,22 +78,21 @@ with --force.`)
// npm cache add <pkg> <ver>
// npm cache add <tarball>
// npm cache add <folder>
const add = async (args, where) => {
const add = async (args) => {
const usage = 'Usage:\n' +
' npm cache add <tarball-url>\n' +
' npm cache add <pkg>@<ver>\n' +
' npm cache add <tarball>\n' +
' npm cache add <folder>\n'
log.silly('cache add', 'args', args)
const spec = args[0] +
const spec = args[0] && args[0] +
(args[1] === undefined || args[1] === null ? '' : `@${args[1]}`)

log.verbose('cache add', 'spec', spec)
if (!spec)
throw new Error(usage)
throw Object.assign(new Error(usage), { code: 'EUSAGE' })

log.silly('cache add', 'parsed spec', spec)
const opts = { ...npm.flatOptions, where }
log.silly('cache add', 'spec', spec)
const opts = { ...npm.flatOptions }

// we ask pacote for the thing, and then just throw the data
// away so that it tee-pipes it into the cache like it does
Expand All @@ -109,7 +109,7 @@ const verify = async () => {
? `~${cache.substr(process.env.HOME.length)}`
: cache
const stats = await cacache.verify(cache)
output(`Cache verified and compressed (${prefix}):`)
output(`Cache verified and compressed (${prefix})`)
output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`)
stats.badContentCount && output(`Corrupted content removed: ${stats.badContentCount}`)
stats.reclaimedCount && output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`)
Expand Down
232 changes: 232 additions & 0 deletions test/lib/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
const t = require('tap')
const requireInject = require('require-inject')
const path = require('path')

const usageUtil = () => 'usage instructions'

const flatOptions = {
force: false
}

const npm = {
flatOptions,
cache: '/fake/path'
}

let rimrafPath = ''
const rimraf = (path, cb) => {
rimrafPath = path
return cb()
}

let logOutput = []
const npmlog = {
silly: (...args) => {
logOutput.push(['silly', ...args])
}
}

let tarballStreamSpec = ''
let tarballStreamOpts = {}
const pacote = {
tarball: {
stream: (spec, cb, opts) => {
tarballStreamSpec = spec
tarballStreamOpts = opts
return cb({
resume: () => {},
promise: () => Promise.resolve()
})
}
}
}

let outputOutput = []
const output = (msg) => {
outputOutput.push(msg)
}

let cacacheVerifyPath = ''
const cacacheVerifyStats = {
keptSize: 100,
verifiedContent: 1,
totalEntries: 1,
runTime: { total: 2000 }
}
const cacache = {
verify: (path) => {
cacacheVerifyPath = path
return cacacheVerifyStats
}
}

const mocks = {
cacache,
npmlog,
pacote,
rimraf,
'../../lib/npm.js': npm,
'../../lib/utils/output.js': output,
'../../lib/utils/usage.js': usageUtil
}

const cache = requireInject('../../lib/cache.js', mocks)

t.test('cache no args', t => {
cache([], err => {
t.equal(err.message, 'usage instructions', 'should throw usage instructions')
t.end()
})
})

t.test('cache clean', t => {
cache(['clean'], err => {
t.match(err.message, 'the npm cache self-heals', 'should throw warning')
t.end()
})
})

t.test('cache clean (force)', t => {
flatOptions.force = true
t.teardown(() => {
rimrafPath = ''
flatOptions.force = false
})

cache(['clear'], err => {
t.ifError(err)
t.equal(rimrafPath, path.join(npm.cache, '_cacache'))
t.end()
})
})

t.test('cache clean with arg', t => {
cache(['rm', 'pkg'], err => {
t.match(err.message, 'does not accept arguments', 'should throw error')
t.end()
})
})

t.test('cache add no arg', t => {
t.teardown(() => {
logOutput = []
})

cache(['add'], err => {
t.strictSame(logOutput, [
['silly', 'cache add', 'args', []],
], 'logs correctly')
t.equal(err.code, 'EUSAGE', 'throws usage error')
t.end()
})
})

t.test('cache add pkg only', t => {
t.teardown(() => {
logOutput = []
tarballStreamSpec = ''
tarballStreamOpts = {}
})

cache(['add', 'mypkg'], err => {
t.ifError(err)
t.strictSame(logOutput, [
['silly', 'cache add', 'args', ['mypkg']],
['silly', 'cache add', 'spec', 'mypkg']
], 'logs correctly')
t.equal(tarballStreamSpec, 'mypkg', 'passes the correct spec to pacote')
t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote')
t.end()
})
})

t.test('cache add pkg w/ spec modifier', t => {
t.teardown(() => {
logOutput = []
tarballStreamSpec = ''
tarballStreamOpts = {}
})

cache(['add', 'mypkg', 'latest'], err => {
t.ifError(err)
t.strictSame(logOutput, [
['silly', 'cache add', 'args', ['mypkg', 'latest']],
['silly', 'cache add', 'spec', 'mypkg@latest']
], 'logs correctly')
t.equal(tarballStreamSpec, 'mypkg@latest', 'passes the correct spec to pacote')
t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote')
t.end()
})
})

t.test('cache verify', t => {
t.teardown(() => {
outputOutput = []
cacacheVerifyPath = ''
})

cache(['verify'], err => {
t.ifError(err)
t.match(outputOutput, [
`Cache verified and compressed (${path.join(npm.cache, '_cacache')})`,
'Content verified: 1 (100 bytes)',
'Index entries: 1',
'Finished in 2s'
], 'prints correct output')
t.end()
})
})

t.test('cache verify w/ extra output', t => {
npm.cache = `${process.env.HOME}/fake/path`
cacacheVerifyStats.badContentCount = 1
cacacheVerifyStats.reclaimedCount = 2
cacacheVerifyStats.reclaimedSize = 200
cacacheVerifyStats.missingContent = 3
t.teardown(() => {
npm.cache = '/fake/path'
outputOutput = []
cacacheVerifyPath = ''
delete cacacheVerifyStats.badContentCount
delete cacacheVerifyStats.reclaimedCount
delete cacacheVerifyStats.reclaimedSize
delete cacacheVerifyStats.missingContent
})

cache(['check'], err => {
t.ifError(err)
t.match(outputOutput, [
`Cache verified and compressed (~${path.join('/fake/path', '_cacache')})`,
'Content verified: 1 (100 bytes)',
'Corrupted content removed: 1',
'Content garbage-collected: 2 (200 bytes)',
'Missing content: 3',
'Index entries: 1',
'Finished in 2s'
], 'prints correct output')
t.end()
})
})

t.test('cache completion', t => {
const { completion } = cache

const testComp = (argv, expect) => {
completion({ conf: { argv: { remain: argv } } }, (err, res) => {
t.ifError(err)
t.strictSame(res, expect, argv.join(' '))
})
}

testComp(['npm', 'cache'], [
'add',
'clean',
'verify'
])

testComp(['npm', 'cache', 'add'], [])
testComp(['npm', 'cache', 'clean'], [])
testComp(['npm', 'cache', 'verify'], [])

t.end()
})

0 comments on commit 7046fe1

Please sign in to comment.