PR-URL: https://github.com/nodejs/node/pull/57334 Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
718 lines
24 KiB
JavaScript
718 lines
24 KiB
JavaScript
const t = require('tap')
|
|
const { load: loadMockNpm } = require('../../fixtures/mock-npm.js')
|
|
const MockRegistry = require('@npmcli/mock-registry')
|
|
|
|
const cacache = require('cacache')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
const { cleanCwd } = require('../../fixtures/clean-snapshot.js')
|
|
|
|
const pkg = 'test-package'
|
|
|
|
const createNpxCacheEntry = (npxCacheDir, hash, pkgJson, shrinkwrapJson) => {
|
|
fs.mkdirSync(path.join(npxCacheDir, hash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, hash, 'package.json'),
|
|
JSON.stringify(pkgJson)
|
|
)
|
|
if (shrinkwrapJson) {
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, hash, 'npm-shrinkwrap.json'),
|
|
JSON.stringify(shrinkwrapJson)
|
|
)
|
|
}
|
|
}
|
|
|
|
t.cleanSnapshot = str => {
|
|
return cleanCwd(str)
|
|
.replace(/Finished in [0-9.s]+/g, 'Finished in xxxs')
|
|
.replace(/Cache verified and compressed (.*)/, 'Cache verified and compressed ({PATH})')
|
|
}
|
|
|
|
t.test('cache no args', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', []),
|
|
{ code: 'EUSAGE' },
|
|
'should throw usage instructions'
|
|
)
|
|
})
|
|
|
|
t.test('cache clean', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', ['clean']),
|
|
/the npm cache self-heals/,
|
|
'should throw warning'
|
|
)
|
|
})
|
|
|
|
t.test('cache clean (force)', async t => {
|
|
const { npm } = await loadMockNpm(t, {
|
|
cacheDir: { _cacache: {} },
|
|
config: { force: true },
|
|
})
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
await npm.exec('cache', ['clean'])
|
|
t.notOk(fs.existsSync(cache), 'cache dir was removed')
|
|
})
|
|
|
|
t.test('cache add no arg', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', ['add']),
|
|
{
|
|
code: 'EUSAGE',
|
|
message: 'First argument to `add` is required',
|
|
},
|
|
'throws usage error'
|
|
)
|
|
})
|
|
|
|
t.test('cache add single pkg', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
prefixDir: {
|
|
package: {
|
|
'package.json': JSON.stringify({
|
|
name: pkg,
|
|
version: '1.0.0',
|
|
}),
|
|
},
|
|
},
|
|
})
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
const registry = new MockRegistry({
|
|
tap: t,
|
|
registry: npm.config.get('registry'),
|
|
})
|
|
const manifest = registry.manifest({ name: pkg })
|
|
await registry.package({
|
|
manifest,
|
|
times: 2,
|
|
tarballs: { '1.0.0': path.join(npm.prefix, 'package') },
|
|
})
|
|
await npm.exec('cache', ['add', pkg])
|
|
t.equal(joinedOutput(), '')
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'))
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package'))
|
|
})
|
|
|
|
t.test('cache add multiple pkgs', async t => {
|
|
const pkg2 = 'test-package-two'
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
prefixDir: {
|
|
package: {
|
|
'package.json': JSON.stringify({
|
|
name: pkg,
|
|
version: '1.0.0',
|
|
}),
|
|
},
|
|
},
|
|
})
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
const registry = new MockRegistry({
|
|
tap: t,
|
|
registry: npm.config.get('registry'),
|
|
})
|
|
const manifest = registry.manifest({ name: pkg })
|
|
const manifest2 = registry.manifest({ name: pkg2 })
|
|
await registry.package({
|
|
manifest,
|
|
times: 2,
|
|
tarballs: { '1.0.0': path.join(npm.prefix, 'package') },
|
|
})
|
|
await registry.package({
|
|
manifest: manifest2, times: 2, tarballs: { '1.0.0': path.join(npm.prefix, 'package') },
|
|
})
|
|
await npm.exec('cache', ['add', pkg, pkg2])
|
|
t.equal(joinedOutput(), '')
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'))
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package'))
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package-two/-/test-package-two-1.0.0.tgz'))
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package-two'))
|
|
})
|
|
|
|
t.test('cache ls', async t => {
|
|
const keys = [
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz',
|
|
]
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
for (const key of keys) {
|
|
await cacache.put(cache, key, 'test data')
|
|
}
|
|
await npm.exec('cache', ['ls'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entries')
|
|
})
|
|
|
|
t.test('cache ls pkgs', async t => {
|
|
const keys = [
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/npm',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/npm/-/npm-1.2.0.tgz',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/webpack/-/webpack-4.40.0.tgz',
|
|
]
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
for (const key of keys) {
|
|
await cacache.put(cache, key, 'test data')
|
|
}
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/webpack',
|
|
JSON.stringify({ versions: {
|
|
'4.40.0': { dist: { tarball: 'https://registry.npmjs.org/webpack/-/webpack-4.40.0.tgz' } },
|
|
'4.47.0': { dist: { tarball: 'https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz' } },
|
|
} })
|
|
)
|
|
await npm.exec('cache', ['ls', 'webpack@>4.44.1', 'npm'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entries for npm and webpack and one webpack tgz')
|
|
})
|
|
|
|
t.test('cache ls special', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/foo',
|
|
JSON.stringify({ versions: { '1.2.3-beta': {} } })
|
|
)
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/foo/-/foo-1.2.3-beta.tgz',
|
|
'test-data'
|
|
)
|
|
await npm.exec('cache', ['ls', 'foo@1.2.3-beta'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entries for foo')
|
|
})
|
|
|
|
t.test('cache ls nonpublic registry', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://somerepo.github.org/extemporaneously',
|
|
JSON.stringify({
|
|
versions: { '1.0.0': { dist: { tarball: 'https://somerepo.github.org/aabbcc/' } } },
|
|
})
|
|
)
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://somerepo.github.org/aabbcc/',
|
|
'test data'
|
|
)
|
|
await npm.exec('cache', ['ls', 'extemporaneously'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entry for extemporaneously and its tarball')
|
|
})
|
|
|
|
t.test('cache ls tagged', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', ['ls', 'webpack@latest']),
|
|
{ code: 'EUSAGE' },
|
|
'should throw usage error'
|
|
)
|
|
})
|
|
|
|
t.test('cache ls scoped and scoped slash', async t => {
|
|
const keys = [
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/@fritzy/staydown/-/@fritzy/staydown-3.1.1.tgz',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/@fritzy%2fstaydown',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/@gar/npm-expansion/-/@gar/npm-expansion-2.1.0.tgz',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/@gar%2fnpm-expansion',
|
|
]
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
for (const key of keys) {
|
|
await cacache.put(cache, key, 'test data')
|
|
}
|
|
await npm.exec('cache', ['ls', '@fritzy/staydown', '@gar/npm-expansion'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entries for @gar and @fritzy')
|
|
})
|
|
|
|
t.test('cache ls corrupted', async t => {
|
|
const keys = [
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/corrupted',
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/corrupted/-/corrupted-3.1.0.tgz',
|
|
]
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
for (const key of keys) {
|
|
await cacache.put(cache, key, Buffer.from('<>>>}"'))
|
|
}
|
|
await npm.exec('cache', ['ls', 'corrupted'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entries with bad data')
|
|
})
|
|
|
|
t.test('cache ls missing packument version not an object', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
await cacache.put(cache,
|
|
'make-fetch-happen:request-cache:https://registry.npmjs.org/missing-version',
|
|
JSON.stringify({ versions: 'not an object' })
|
|
)
|
|
await npm.exec('cache', ['ls', 'missing-version'])
|
|
t.matchSnapshot(joinedOutput(), 'logs cache entry for packument')
|
|
})
|
|
|
|
t.test('cache rm', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const cache = path.join(npm.cache, '_cacache')
|
|
await cacache.put(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package', '{}')
|
|
await cacache.put(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz', 'test data')
|
|
await npm.exec('cache', ['rm', 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'])
|
|
t.matchSnapshot(joinedOutput(), 'logs deleting single entry')
|
|
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package'))
|
|
t.rejects(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'))
|
|
})
|
|
|
|
t.test('cache rm unfound', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
await npm.exec('cache', ['rm', 'made-up-key'])
|
|
t.same(joinedOutput(), '', 'no errors, no output')
|
|
})
|
|
|
|
t.test('cache verify', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
await npm.exec('cache', ['verify'])
|
|
t.matchSnapshot(joinedOutput(), 'shows verified cache output')
|
|
})
|
|
|
|
t.test('cache verify as part of home', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
globals: ({ prefix }) => ({ 'process.env.HOME': path.dirname(prefix) }),
|
|
})
|
|
await npm.exec('cache', ['verify'])
|
|
t.match(joinedOutput(), 'Cache verified and compressed (~', 'contains ~ shorthand')
|
|
})
|
|
|
|
t.test('cache verify w/ extra output', async t => {
|
|
const verify = {
|
|
runTime: {
|
|
markStartTime: 0,
|
|
fixPerms: 3,
|
|
garbageCollect: 54982,
|
|
rebuildIndex: 62779,
|
|
cleanTmp: 62781,
|
|
writeVerifile: 62783,
|
|
markEndTime: 62783,
|
|
total: 62783,
|
|
},
|
|
verifiedContent: 17057,
|
|
reclaimedCount: 1144,
|
|
reclaimedSize: 248164665,
|
|
badContentCount: 12345,
|
|
keptSize: 1644485260,
|
|
missingContent: 92,
|
|
rejectedEntries: 92,
|
|
totalEntries: 20175,
|
|
}
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
mocks: { cacache: { verify: () => verify } },
|
|
})
|
|
await npm.exec('cache', ['verify'])
|
|
t.matchSnapshot(joinedOutput(), 'shows extra output')
|
|
})
|
|
|
|
t.test('cache completion', async t => {
|
|
const { cache } = await loadMockNpm(t, { command: 'cache' })
|
|
const { completion } = cache
|
|
|
|
const testComp = (argv, expect) => {
|
|
return t.resolveMatch(completion({ conf: { argv: { remain: argv } } }), expect, argv.join(' '))
|
|
}
|
|
|
|
await Promise.all([
|
|
testComp(['npm', 'cache'], ['add', 'clean', 'verify']),
|
|
testComp(['npm', 'cache', 'add'], []),
|
|
testComp(['npm', 'cache', 'clean'], []),
|
|
testComp(['npm', 'cache', 'verify'], []),
|
|
])
|
|
})
|
|
|
|
t.test('cache npx ls: empty cache', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
await npm.exec('cache', ['npx', 'ls'])
|
|
t.matchSnapshot(joinedOutput(), 'logs message for empty npx cache')
|
|
})
|
|
|
|
t.test('cache npx ls: some entries', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
// Make two fake entries: one valid, one invalid
|
|
const hash1 = 'abc123'
|
|
const hash2 = 'z9y8x7'
|
|
fs.mkdirSync(path.join(npxCacheDir, hash1))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, hash1, 'package.json'),
|
|
JSON.stringify({
|
|
name: 'fake-npx-package',
|
|
version: '1.0.0',
|
|
_npx: { packages: ['fake-npx-package@1.0.0'] },
|
|
})
|
|
)
|
|
// invalid (missing or broken package.json) directory
|
|
fs.mkdirSync(path.join(npxCacheDir, hash2))
|
|
|
|
await npm.exec('cache', ['npx', 'ls'])
|
|
t.matchSnapshot(joinedOutput(), 'lists one valid and one invalid entry')
|
|
})
|
|
|
|
t.test('cache npx info: valid and invalid entry', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const goodHash = 'deadbeef'
|
|
fs.mkdirSync(path.join(npxCacheDir, goodHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, goodHash, 'package.json'),
|
|
JSON.stringify({
|
|
name: 'good-npx-package',
|
|
version: '2.0.0',
|
|
dependencies: {
|
|
rimraf: '^3.0.0',
|
|
},
|
|
_npx: { packages: ['good-npx-package@2.0.0'] },
|
|
})
|
|
)
|
|
|
|
const badHash = 'badc0de'
|
|
fs.mkdirSync(path.join(npxCacheDir, badHash))
|
|
|
|
await npm.exec('cache', ['npx', 'info', goodHash])
|
|
t.matchSnapshot(joinedOutput(), 'shows valid package info')
|
|
|
|
await npm.exec('cache', ['npx', 'info', badHash])
|
|
t.matchSnapshot(joinedOutput(), 'shows invalid package info')
|
|
})
|
|
|
|
t.test('cache npx rm: remove single entry', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const removableHash = '123removeme'
|
|
fs.mkdirSync(path.join(npxCacheDir, removableHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, removableHash, 'package.json'),
|
|
JSON.stringify({ name: 'removable-package', _npx: { packages: ['removable-package@1.0.0'] } })
|
|
)
|
|
|
|
npm.config.set('dry-run', true)
|
|
await npm.exec('cache', ['npx', 'rm', removableHash])
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, removableHash)), 'entry folder remains')
|
|
npm.config.set('dry-run', false)
|
|
|
|
await npm.exec('cache', ['npx', 'rm', removableHash])
|
|
t.matchSnapshot(joinedOutput(), 'logs removing single npx cache entry')
|
|
t.notOk(fs.existsSync(path.join(npxCacheDir, removableHash)), 'entry folder removed')
|
|
})
|
|
|
|
t.test('cache npx rm: removing all without --force fails', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const testHash = 'remove-all-no-force'
|
|
fs.mkdirSync(path.join(npxCacheDir, testHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, testHash, 'package.json'),
|
|
JSON.stringify({ name: 'no-force-pkg', _npx: { packages: ['no-force-pkg@1.0.0'] } })
|
|
)
|
|
|
|
await t.rejects(
|
|
npm.exec('cache', ['npx', 'rm']),
|
|
/Please use --force to remove entire npx cache/,
|
|
'fails without --force'
|
|
)
|
|
t.matchSnapshot(joinedOutput(), 'logs usage error when removing all without --force')
|
|
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, testHash)), 'folder still exists')
|
|
})
|
|
|
|
t.test('cache npx rm: removing all with --force works', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
config: { force: true },
|
|
})
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const testHash = 'remove-all-yes-force'
|
|
fs.mkdirSync(path.join(npxCacheDir, testHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, testHash, 'package.json'),
|
|
JSON.stringify({ name: 'yes-force-pkg', _npx: { packages: ['yes-force-pkg@1.0.0'] } })
|
|
)
|
|
|
|
npm.config.set('dry-run', true)
|
|
await npm.exec('cache', ['npx', 'rm'])
|
|
t.ok(fs.existsSync(npxCacheDir), 'npx cache directory remains')
|
|
npm.config.set('dry-run', false)
|
|
|
|
await npm.exec('cache', ['npx', 'rm'])
|
|
|
|
t.matchSnapshot(joinedOutput(), 'logs removing everything')
|
|
t.notOk(fs.existsSync(npxCacheDir), 'npx cache directory removed')
|
|
})
|
|
|
|
t.test('cache npx rm: removing more than 1, less than all entries', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
// Removable folder
|
|
const removableHash = '123removeme'
|
|
fs.mkdirSync(path.join(npxCacheDir, removableHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, removableHash, 'package.json'),
|
|
JSON.stringify({ name: 'removable-package', _npx: { packages: ['removable-package@1.0.0'] } })
|
|
)
|
|
|
|
// Another Removable folder
|
|
const anotherRemovableHash = '456removeme'
|
|
fs.mkdirSync(path.join(npxCacheDir, anotherRemovableHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, anotherRemovableHash, 'package.json'),
|
|
JSON.stringify({ name: 'another-removable-package', _npx: { packages: ['another-removable-package@1.0.0'] } })
|
|
)
|
|
|
|
// Another folder that should remain
|
|
const keepHash = '999keep'
|
|
fs.mkdirSync(path.join(npxCacheDir, keepHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, keepHash, 'package.json'),
|
|
JSON.stringify({ name: 'keep-package', _npx: { packages: ['keep-package@1.0.0'] } })
|
|
)
|
|
|
|
npm.config.set('dry-run', true)
|
|
await npm.exec('cache', ['npx', 'rm', removableHash, anotherRemovableHash])
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, removableHash)), 'entry folder remains')
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, anotherRemovableHash)), 'entry folder remains')
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, keepHash)), 'entry folder remains')
|
|
npm.config.set('dry-run', false)
|
|
|
|
await npm.exec('cache', ['npx', 'rm', removableHash, anotherRemovableHash])
|
|
t.matchSnapshot(joinedOutput(), 'logs removing 2 of 3 entries')
|
|
|
|
t.notOk(fs.existsSync(path.join(npxCacheDir, removableHash)), 'removed folder no longer exists')
|
|
t.notOk(fs.existsSync(path.join(npxCacheDir, anotherRemovableHash)), 'the other folder no longer exists')
|
|
t.ok(fs.existsSync(path.join(npxCacheDir, keepHash)), 'the other folder remains')
|
|
})
|
|
|
|
t.test('cache npx should throw usage error', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', ['npx', 'badcommand']),
|
|
{ code: 'EUSAGE' },
|
|
'should throw usage error'
|
|
)
|
|
})
|
|
|
|
t.test('cache npx should throw usage error for invalid key', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const key = 'badkey'
|
|
await t.rejects(
|
|
npm.exec('cache', ['npx', 'rm', key]),
|
|
{ code: 'EUSAGE' },
|
|
`Invalid npx key ${key}`
|
|
)
|
|
})
|
|
|
|
t.test('cache npx ls: entry with unknown package', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
// Create an entry without the _npx property
|
|
const unknownHash = 'unknown123'
|
|
fs.mkdirSync(path.join(npxCacheDir, unknownHash))
|
|
fs.writeFileSync(
|
|
path.join(npxCacheDir, unknownHash, 'package.json'),
|
|
JSON.stringify({
|
|
name: 'unknown-package',
|
|
version: '1.0.0',
|
|
})
|
|
)
|
|
|
|
await npm.exec('cache', ['npx', 'ls'])
|
|
t.matchSnapshot(joinedOutput(), 'lists entry with unknown package')
|
|
})
|
|
|
|
t.test('cache npx info: should throw usage error when no keys are provided', async t => {
|
|
const { npm } = await loadMockNpm(t)
|
|
await t.rejects(
|
|
npm.exec('cache', ['npx', 'info']),
|
|
{ code: 'EUSAGE' },
|
|
'should throw usage error when no keys are provided'
|
|
)
|
|
})
|
|
|
|
t.test('cache npx info: valid entry with _npx packages', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const validHash = 'valid123'
|
|
createNpxCacheEntry(npxCacheDir, validHash, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
_npx: { packages: ['valid-package@1.0.0'] },
|
|
}, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'valid-package': {
|
|
version: '1.0.0',
|
|
resolved: 'https://registry.npmjs.org/valid-package/-/valid-package-1.0.0.tgz',
|
|
integrity: 'sha512-...',
|
|
},
|
|
},
|
|
})
|
|
|
|
const nodeModulesDir = path.join(npxCacheDir, validHash, 'node_modules')
|
|
fs.mkdirSync(nodeModulesDir, { recursive: true })
|
|
fs.mkdirSync(path.join(nodeModulesDir, 'valid-package'))
|
|
fs.writeFileSync(
|
|
path.join(nodeModulesDir, 'valid-package', 'package.json'),
|
|
JSON.stringify({
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
})
|
|
)
|
|
|
|
await npm.exec('cache', ['npx', 'info', validHash])
|
|
t.matchSnapshot(joinedOutput(), 'shows valid package info with _npx packages')
|
|
})
|
|
|
|
t.test('cache npx info: valid entry with dependencies', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const validHash = 'valid456'
|
|
createNpxCacheEntry(npxCacheDir, validHash, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'dep-package': '1.0.0',
|
|
},
|
|
}, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'dep-package': {
|
|
version: '1.0.0',
|
|
resolved: 'https://registry.npmjs.org/dep-package/-/dep-package-1.0.0.tgz',
|
|
integrity: 'sha512-...',
|
|
},
|
|
},
|
|
})
|
|
|
|
const nodeModulesDir = path.join(npxCacheDir, validHash, 'node_modules')
|
|
fs.mkdirSync(nodeModulesDir, { recursive: true })
|
|
fs.mkdirSync(path.join(nodeModulesDir, 'dep-package'))
|
|
fs.writeFileSync(
|
|
path.join(nodeModulesDir, 'dep-package', 'package.json'),
|
|
JSON.stringify({
|
|
name: 'dep-package',
|
|
version: '1.0.0',
|
|
})
|
|
)
|
|
|
|
await npm.exec('cache', ['npx', 'info', validHash])
|
|
t.matchSnapshot(joinedOutput(), 'shows valid package info with dependencies')
|
|
})
|
|
|
|
t.test('cache npx info: valid entry with _npx directory package', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx'))
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const validHash = 'valid123'
|
|
createNpxCacheEntry(npxCacheDir, validHash, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
_npx: { packages: ['/path/to/valid-package'] },
|
|
}, {
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'valid-package': {
|
|
version: '1.0.0',
|
|
resolved: 'https://registry.npmjs.org/valid-package/-/valid-package-1.0.0.tgz',
|
|
integrity: 'sha512-...',
|
|
},
|
|
},
|
|
})
|
|
|
|
const nodeModulesDir = path.join(npxCacheDir, validHash, 'node_modules')
|
|
fs.mkdirSync(nodeModulesDir, { recursive: true })
|
|
fs.mkdirSync(path.join(nodeModulesDir, 'valid-package'))
|
|
fs.writeFileSync(
|
|
path.join(nodeModulesDir, 'valid-package', 'package.json'),
|
|
JSON.stringify({
|
|
name: 'valid-package',
|
|
version: '1.0.0',
|
|
})
|
|
)
|
|
|
|
await npm.exec('cache', ['npx', 'info', validHash])
|
|
t.matchSnapshot(joinedOutput(), 'shows valid package info with _npx directory package')
|
|
})
|
|
|
|
t.test('cache npx info: valid entry with a link dependency', async t => {
|
|
const { npm, joinedOutput } = await loadMockNpm(t)
|
|
const npxCacheDir = path.join(
|
|
npm.flatOptions.npxCache || path.join(npm.cache, '..', '_npx')
|
|
)
|
|
fs.mkdirSync(npxCacheDir, { recursive: true })
|
|
|
|
const validHash = 'link123'
|
|
const pkgDir = path.join(npxCacheDir, validHash)
|
|
fs.mkdirSync(pkgDir)
|
|
|
|
fs.writeFileSync(
|
|
path.join(pkgDir, 'package.json'),
|
|
JSON.stringify({
|
|
name: 'link-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'linked-dep': 'file:./some-other-loc',
|
|
},
|
|
})
|
|
)
|
|
|
|
fs.writeFileSync(
|
|
path.join(pkgDir, 'npm-shrinkwrap.json'),
|
|
JSON.stringify({
|
|
name: 'link-package',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'linked-dep': {
|
|
version: 'file:../some-other-loc',
|
|
},
|
|
},
|
|
})
|
|
)
|
|
|
|
const nodeModulesDir = path.join(pkgDir, 'node_modules')
|
|
fs.mkdirSync(nodeModulesDir, { recursive: true })
|
|
|
|
const linkTarget = path.join(pkgDir, 'some-other-loc')
|
|
fs.mkdirSync(linkTarget)
|
|
fs.writeFileSync(
|
|
path.join(linkTarget, 'package.json'),
|
|
JSON.stringify({ name: 'linked-dep', version: '1.0.0' })
|
|
)
|
|
|
|
fs.symlinkSync('../some-other-loc', path.join(nodeModulesDir, 'linked-dep'))
|
|
await npm.exec('cache', ['npx', 'info', validHash])
|
|
|
|
t.matchSnapshot(
|
|
joinedOutput(),
|
|
'shows link dependency realpath (child.isLink branch)'
|
|
)
|
|
})
|