Skip to content

Commit d5d25ba

Browse files
authored
feat(rm): limited rm.all and fixed bugs (#66)
Fixes: #65
1 parent 3a21b2f commit d5d25ba

File tree

4 files changed

+118
-6
lines changed

4 files changed

+118
-6
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ cacache.rm.all(cachePath).then(() => {
386386

387387
#### <a name="rm-entry"></a> `> cacache.rm.entry(cache, key) -> Promise`
388388

389+
Alias: `cacache.rm`
390+
389391
Removes the index entry for `key`. Content will still be accessible if
390392
requested directly by content address ([`get.stream.byDigest`](#get-stream)).
391393

Diff for: lib/entry-index.js

+2
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ function hashKey (key) {
181181
}
182182

183183
function formatEntry (cache, entry) {
184+
// Treat null digests as deletions. They'll shadow any previous entries.
185+
if (!entry.digest) { return null }
184186
return {
185187
key: entry.key,
186188
digest: entry.digest,

Diff for: rm.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@ const Promise = require('bluebird')
44

55
const index = require('./lib/entry-index')
66
const memo = require('./lib/memoization')
7+
const path = require('path')
78
const rimraf = Promise.promisify(require('rimraf'))
89
const rmContent = require('./lib/content/rm')
910

10-
module.exports.all = all
11-
function all (cache) {
12-
memo.clearMemoized()
13-
return rimraf(cache)
14-
}
15-
11+
module.exports = entry
1612
module.exports.entry = entry
1713
function entry (cache, key) {
1814
memo.clearMemoized()
@@ -24,3 +20,9 @@ function content (cache, address) {
2420
memo.clearMemoized()
2521
return rmContent(cache, address)
2622
}
23+
24+
module.exports.all = all
25+
function all (cache) {
26+
memo.clearMemoized()
27+
return rimraf(path.join(cache, '*(content-*|index-*)'))
28+
}

Diff for: test/rm.js

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict'
2+
3+
const Promise = require('bluebird')
4+
5+
const crypto = require('crypto')
6+
const fromString = require('./util/from-string')
7+
const fs = Promise.promisifyAll(require('fs'))
8+
const index = require('../lib/entry-index')
9+
const memo = require('../lib/memoization')
10+
const path = require('path')
11+
const pipe = Promise.promisify(require('mississippi').pipe)
12+
const Tacks = require('tacks')
13+
const test = require('tap').test
14+
const testDir = require('./util/test-dir')(__filename)
15+
16+
const CacheContent = require('./util/cache-content')
17+
const CACHE = path.join(testDir, 'cache')
18+
const CONTENT = bufferise('foobarbaz')
19+
const KEY = 'my-test-key'
20+
const ALGO = 'sha512'
21+
const DIGEST = crypto.createHash(ALGO).update(CONTENT).digest('hex')
22+
const METADATA = { foo: 'bar' }
23+
const contentPath = require('../lib/content/path')
24+
25+
const get = require('../get')
26+
27+
const rm = require('../rm')
28+
29+
function bufferise (string) {
30+
return Buffer.from
31+
? Buffer.from(string, 'utf8')
32+
: new Buffer(string, 'utf8')
33+
}
34+
35+
test('rm.entry removes entries, not content', t => {
36+
const fixture = new Tacks(CacheContent({
37+
[DIGEST]: CONTENT
38+
}, ALGO))
39+
fixture.create(CACHE)
40+
return index.insert(CACHE, KEY, DIGEST, {
41+
metadata: METADATA,
42+
hashAlgorithm: ALGO
43+
}).then(() => {
44+
t.equal(rm, rm.entry, 'rm is an alias for rm.entry')
45+
return rm.entry(CACHE, KEY)
46+
}).then(() => {
47+
return get(CACHE, KEY)
48+
}).then(res => {
49+
throw new Error('unexpected success')
50+
}).catch({code: 'ENOENT'}, err => {
51+
t.match(err.message, /not found/, 'entry no longer accessible')
52+
}).then(() => {
53+
return fs.readFileAsync(contentPath(CACHE, DIGEST, ALGO))
54+
}).then(data => {
55+
t.deepEqual(data, CONTENT, 'content remains in cache')
56+
})
57+
})
58+
59+
test('rm.content removes content, not entries', t => {
60+
const fixture = new Tacks(CacheContent({
61+
[DIGEST]: CONTENT
62+
}, ALGO))
63+
fixture.create(CACHE)
64+
return index.insert(CACHE, KEY, DIGEST, {
65+
metadata: METADATA,
66+
hashAlgorithm: ALGO
67+
}).then(() => {
68+
return rm.content(CACHE, DIGEST)
69+
}).then(() => {
70+
return get(CACHE, KEY)
71+
}).then(res => {
72+
throw new Error('unexpected success')
73+
}).catch({code: 'ENOENT'}, err => {
74+
t.match(err.message, /no such file/, 'entry no longer accessible')
75+
}).then(() => {
76+
return fs.readFileAsync(contentPath(CACHE, DIGEST, ALGO))
77+
}).then(() => {
78+
throw new Error('unexpected success')
79+
}).catch({code: 'ENOENT'}, err => {
80+
t.match(err.message, /no such file/, 'content gone')
81+
})
82+
})
83+
84+
test('rm.all deletes content and index dirs', t => {
85+
const fixture = new Tacks(CacheContent({
86+
[DIGEST]: CONTENT
87+
}, ALGO))
88+
fixture.create(CACHE)
89+
return index.insert(CACHE, KEY, DIGEST, {
90+
metadata: METADATA,
91+
hashAlgorithm: ALGO
92+
}).then(() => {
93+
return fs.mkdirAsync(path.join(CACHE, 'tmp'))
94+
}).then(() => {
95+
return fs.writeFileAsync(path.join(CACHE, 'other.js'), 'hi')
96+
}).then(() => {
97+
return rm.all(CACHE)
98+
}).then(() => {
99+
return fs.readdirAsync(CACHE)
100+
}).then(files => {
101+
t.deepEqual(files.sort(), [
102+
'other.js',
103+
'tmp'
104+
], 'removes content and index directories without touching other stuff')
105+
})
106+
})

0 commit comments

Comments
 (0)