|
2 | 2 | 'use strict'
|
3 | 3 |
|
4 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha')
|
| 5 | +const { DAGNode } = require('ipld-dag-pb') |
5 | 6 |
|
6 | 7 | module.exports = (createCommon, options) => {
|
7 | 8 | const describe = getDescribe(options)
|
@@ -41,5 +42,194 @@ module.exports = (createCommon, options) => {
|
41 | 42 | expect(res).to.exist()
|
42 | 43 | })
|
43 | 44 | })
|
| 45 | + |
| 46 | + it('should clean up unpinned data', async () => { |
| 47 | + // Get initial list of local blocks |
| 48 | + const refsBeforeAdd = await ipfs.refs.local() |
| 49 | + |
| 50 | + // Add some data. Note: this will implicitly pin the data, which causes |
| 51 | + // some blocks to be added for the data itself and for the pinning |
| 52 | + // information that refers to the blocks |
| 53 | + const addRes = await ipfs.add(Buffer.from('apples')) |
| 54 | + const hash = addRes[0].hash |
| 55 | + |
| 56 | + // Get the list of local blocks after the add, should be bigger than |
| 57 | + // the initial list and contain hash |
| 58 | + const refsAfterAdd = await ipfs.refs.local() |
| 59 | + expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length) |
| 60 | + expect(refsAfterAdd.map(r => r.ref)).includes(hash) |
| 61 | + |
| 62 | + // Run garbage collection |
| 63 | + await ipfs.repo.gc() |
| 64 | + |
| 65 | + // Get the list of local blocks after GC, should still contain the hash, |
| 66 | + // because the file is still pinned |
| 67 | + const refsAfterGc = await ipfs.refs.local() |
| 68 | + expect(refsAfterGc.map(r => r.ref)).includes(hash) |
| 69 | + |
| 70 | + // Unpin the data |
| 71 | + await ipfs.pin.rm(hash) |
| 72 | + |
| 73 | + // Run garbage collection |
| 74 | + await ipfs.repo.gc() |
| 75 | + |
| 76 | + // The list of local blocks should no longer contain the hash |
| 77 | + const refsAfterUnpinAndGc = await ipfs.refs.local() |
| 78 | + expect(refsAfterUnpinAndGc.map(r => r.ref)).not.includes(hash) |
| 79 | + }) |
| 80 | + |
| 81 | + it('should clean up removed MFS files', async () => { |
| 82 | + // Get initial list of local blocks |
| 83 | + const refsBeforeAdd = await ipfs.refs.local() |
| 84 | + |
| 85 | + // Add a file to MFS |
| 86 | + await ipfs.files.write('/test', Buffer.from('oranges'), { create: true }) |
| 87 | + const stats = await ipfs.files.stat('/test') |
| 88 | + expect(stats.type).to.equal('file') |
| 89 | + const hash = stats.hash |
| 90 | + |
| 91 | + // Get the list of local blocks after the add, should be bigger than |
| 92 | + // the initial list and contain hash |
| 93 | + const refsAfterAdd = await ipfs.refs.local() |
| 94 | + expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length) |
| 95 | + expect(refsAfterAdd.map(r => r.ref)).includes(hash) |
| 96 | + |
| 97 | + // Run garbage collection |
| 98 | + await ipfs.repo.gc() |
| 99 | + |
| 100 | + // Get the list of local blocks after GC, should still contain the hash, |
| 101 | + // because the file is in MFS |
| 102 | + const refsAfterGc = await ipfs.refs.local() |
| 103 | + expect(refsAfterGc.map(r => r.ref)).includes(hash) |
| 104 | + |
| 105 | + // Remove the file |
| 106 | + await ipfs.files.rm('/test') |
| 107 | + |
| 108 | + // Run garbage collection |
| 109 | + await ipfs.repo.gc() |
| 110 | + |
| 111 | + // The list of local blocks should no longer contain the hash |
| 112 | + const refsAfterUnpinAndGc = await ipfs.refs.local() |
| 113 | + expect(refsAfterUnpinAndGc.map(r => r.ref)).not.includes(hash) |
| 114 | + }) |
| 115 | + |
| 116 | + it('should clean up block only after unpinned and removed from MFS', async () => { |
| 117 | + // Get initial list of local blocks |
| 118 | + const refsBeforeAdd = await ipfs.refs.local() |
| 119 | + |
| 120 | + // Add a file to MFS |
| 121 | + await ipfs.files.write('/test', Buffer.from('peaches'), { create: true }) |
| 122 | + const stats = await ipfs.files.stat('/test') |
| 123 | + expect(stats.type).to.equal('file') |
| 124 | + const mfsFileHash = stats.hash |
| 125 | + |
| 126 | + // Get the CID of the data in the file |
| 127 | + const block = await ipfs.block.get(mfsFileHash) |
| 128 | + |
| 129 | + // Add the data to IPFS (which implicitly pins the data) |
| 130 | + const addRes = await ipfs.add(block.data) |
| 131 | + const dataHash = addRes[0].hash |
| 132 | + |
| 133 | + // Get the list of local blocks after the add, should be bigger than |
| 134 | + // the initial list and contain the data hash |
| 135 | + const refsAfterAdd = await ipfs.refs.local() |
| 136 | + expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length) |
| 137 | + const hashesAfterAdd = refsAfterAdd.map(r => r.ref) |
| 138 | + expect(hashesAfterAdd).includes(dataHash) |
| 139 | + |
| 140 | + // Run garbage collection |
| 141 | + await ipfs.repo.gc() |
| 142 | + |
| 143 | + // Get the list of local blocks after GC, should still contain the hash, |
| 144 | + // because the file is pinned and in MFS |
| 145 | + const refsAfterGc = await ipfs.refs.local() |
| 146 | + const hashesAfterGc = refsAfterGc.map(r => r.ref) |
| 147 | + expect(hashesAfterGc).includes(dataHash) |
| 148 | + |
| 149 | + // Remove the file |
| 150 | + await ipfs.files.rm('/test') |
| 151 | + |
| 152 | + // Run garbage collection |
| 153 | + await ipfs.repo.gc() |
| 154 | + |
| 155 | + // Get the list of local blocks after GC, should still contain the hash, |
| 156 | + // because the file is still pinned |
| 157 | + const refsAfterRmAndGc = await ipfs.refs.local() |
| 158 | + const hashesAfterRmAndGc = refsAfterRmAndGc.map(r => r.ref) |
| 159 | + expect(hashesAfterRmAndGc).not.includes(mfsFileHash) |
| 160 | + expect(hashesAfterRmAndGc).includes(dataHash) |
| 161 | + |
| 162 | + // Unpin the data |
| 163 | + await ipfs.pin.rm(dataHash) |
| 164 | + |
| 165 | + // Run garbage collection |
| 166 | + await ipfs.repo.gc() |
| 167 | + |
| 168 | + // The list of local blocks should no longer contain the hashes |
| 169 | + const refsAfterUnpinAndGc = await ipfs.refs.local() |
| 170 | + const hashesAfterUnpinAndGc = refsAfterUnpinAndGc.map(r => r.ref) |
| 171 | + expect(hashesAfterUnpinAndGc).not.includes(mfsFileHash) |
| 172 | + expect(hashesAfterUnpinAndGc).not.includes(dataHash) |
| 173 | + }) |
| 174 | + |
| 175 | + it('should clean up indirectly pinned data after recursive pin removal', async () => { |
| 176 | + // Get initial list of local blocks |
| 177 | + const refsBeforeAdd = await ipfs.refs.local() |
| 178 | + |
| 179 | + // Add some data |
| 180 | + const addRes = await ipfs.add(Buffer.from('pears')) |
| 181 | + const dataHash = addRes[0].hash |
| 182 | + |
| 183 | + // Unpin the data |
| 184 | + await ipfs.pin.rm(dataHash) |
| 185 | + |
| 186 | + // Create a link to the data from an object |
| 187 | + const obj = await DAGNode.create(Buffer.from('fruit'), [{ |
| 188 | + Name: 'p', |
| 189 | + Hash: dataHash, |
| 190 | + TSize: addRes[0].size |
| 191 | + }]) |
| 192 | + |
| 193 | + // Put the object into IPFS |
| 194 | + const objHash = (await ipfs.object.put(obj)).toString() |
| 195 | + |
| 196 | + // Putting an object doesn't pin it |
| 197 | + expect((await ipfs.pin.ls()).map(p => p.hash)).not.includes(objHash) |
| 198 | + |
| 199 | + // Get the list of local blocks after the add, should be bigger than |
| 200 | + // the initial list and contain data and object hash |
| 201 | + const refsAfterAdd = await ipfs.refs.local() |
| 202 | + expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length) |
| 203 | + const hashesAfterAdd = refsAfterAdd.map(r => r.ref) |
| 204 | + expect(hashesAfterAdd).includes(objHash) |
| 205 | + expect(hashesAfterAdd).includes(dataHash) |
| 206 | + |
| 207 | + // Recursively pin the object |
| 208 | + await ipfs.pin.add(objHash, { recursive: true }) |
| 209 | + |
| 210 | + // The data should now be indirectly pinned |
| 211 | + const pins = await ipfs.pin.ls() |
| 212 | + expect(pins.find(p => p.hash === dataHash).type).to.eql('indirect') |
| 213 | + |
| 214 | + // Run garbage collection |
| 215 | + await ipfs.repo.gc() |
| 216 | + |
| 217 | + // Get the list of local blocks after GC, should still contain the data |
| 218 | + // hash, because the data is still (indirectly) pinned |
| 219 | + const refsAfterGc = await ipfs.refs.local() |
| 220 | + expect(refsAfterGc.map(r => r.ref)).includes(dataHash) |
| 221 | + |
| 222 | + // Recursively unpin the object |
| 223 | + await ipfs.pin.rm(objHash) |
| 224 | + |
| 225 | + // Run garbage collection |
| 226 | + await ipfs.repo.gc() |
| 227 | + |
| 228 | + // The list of local blocks should no longer contain the hashes |
| 229 | + const refsAfterUnpinAndGc = await ipfs.refs.local() |
| 230 | + const hashesAfterUnpinAndGc = refsAfterUnpinAndGc.map(r => r.ref) |
| 231 | + expect(hashesAfterUnpinAndGc).not.includes(objHash) |
| 232 | + expect(hashesAfterUnpinAndGc).not.includes(dataHash) |
| 233 | + }) |
44 | 234 | })
|
45 | 235 | }
|
0 commit comments