/
cache.js
319 lines (294 loc) · 11.5 KB
/
cache.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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('fs')
const path = require('path')
const pkg = 'test-package'
t.cleanSnapshot = str => {
return 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, tarballs: { '1.0.0': path.join(npm.prefix, 'package') } })
await npm.exec('cache', ['add', pkg])
t.equal(joinedOutput(), '')
// eslint-disable-next-line max-len
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'))
// eslint-disable-next-line max-len
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, tarballs: { '1.0.0': path.join(npm.prefix, 'package') } })
await registry.package({
manifest: manifest2, tarballs: { '1.0.0': path.join(npm.prefix, 'package') },
})
await npm.exec('cache', ['add', pkg, pkg2])
t.equal(joinedOutput(), '')
// eslint-disable-next-line max-len
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz'))
// eslint-disable-next-line max-len
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package'))
// eslint-disable-next-line max-len
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package-two/-/test-package-two-1.0.0.tgz'))
// eslint-disable-next-line max-len
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',
// eslint-disable-next-line max-len
'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 = [
// eslint-disable-next-line max-len
'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',
// eslint-disable-next-line max-len
'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')
// eslint-disable-next-line max-len
await cacache.put(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package', '{}')
// eslint-disable-next-line max-len
await cacache.put(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz', 'test data')
// eslint-disable-next-line max-len
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')
// eslint-disable-next-line max-len
t.resolves(cacache.get(cache, 'make-fetch-happen:request-cache:https://registry.npmjs.org/test-package'))
// eslint-disable-next-line max-len
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 { npm } = await loadMockNpm(t)
const cache = await npm.cmd('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'], []),
])
})