Browse Source

mount module

ameba23 3 years ago
parent
commit
9fa8cb721b
9 changed files with 1242 additions and 527 deletions
  1. 18
    0
      README.md
  2. 33
    14
      index.js
  3. 0
    21
      mount.js
  4. 644
    467
      package-lock.json
  5. 9
    7
      package.json
  6. 0
    18
      swarm.js
  7. 280
    0
      test/index.test.js
  8. 180
    0
      test/mount-extratests.js
  9. 78
    0
      test/util.js

+ 18
- 0
README.md View File

@@ -0,0 +1,18 @@
1
+# kappa-drive-mount
2
+
3
+mount a `kappa-drive` with `hyperdrive-fuse`.
4
+
5
+## API
6
+```
7
+const mount = require('kappa-drive-mount')
8
+```
9
+
10
+### `const destroy = mount(drive, destination)`
11
+
12
+mount a kappa-drive, `drive` at path `destination`.
13
+
14
+Returns a promise which passes a destroy function
15
+
16
+### `const cleanedUp = destroy.then((destroy) => { mount.cleanup(destroy, desination)})`  
17
+
18
+unmount and remove the mounted drive.  Returns a promise

+ 33
- 14
index.js View File

@@ -1,16 +1,35 @@
1
-const KappaDrive = require('peerfs')
2
-// const peerfsMount = require('./node_modules/peerfs/mount')
3
-const swarm = require('./swarm')
4
-const peerfsMount = require('./mount')
5
-const ram = require('random-access-memory')
6
-const MOUNTDIR = './mnt'
1
+const rimraf = require('rimraf')
2
+const { mount, unmount } = require('hyperdrive-fuse')
3
+const mkdirp = require('mkdirp')
7 4
 
8
-// temporary - get key as command line arg
9
-const key = process.argv[2] ? Buffer.from(process.argv[2], 'hex') : null
5
+module.exports = Mount
6
+module.exports.cleanup = cleanup
7
+module.exports.unmount = Unmount
10 8
 
11
-var drive = KappaDrive(ram, key)
12
-drive.ready(() => {
13
-  console.log('drive.key ', drive.key.toString('hex'))
14
-  peerfsMount(drive, MOUNTDIR)
15
-  swarm(drive)
16
-})
9
+async function Mount (drive, dest) {
10
+  mkdirp.sync(dest)
11
+  var { destroy } = await mount(drive, dest)
12
+
13
+  process.once('SIGINT', () => (
14
+    cleanup(destroy, dest)
15
+  ))
16
+
17
+  return destroy
18
+}
19
+
20
+function cleanup (destroy, dest) {
21
+  return new Promise((resolve, reject) => {
22
+    destroy(err => {
23
+      if (err) return reject(err)
24
+      rimraf(dest, err => {
25
+        if (err) return reject(err)
26
+        return resolve()
27
+      })
28
+    })
29
+  })
30
+}
31
+
32
+// TODO this should include cleanup
33
+function Unmount (dest, cb) {
34
+  return unmount(dest, cb)
35
+}

+ 0
- 21
mount.js View File

@@ -1,21 +0,0 @@
1
-const rimraf = require('rimraf')
2
-const { mount, unmount, getHandlers  } = require('hyperdrive-fuse')
3
-const mkdirp = require('mkdirp').sync
4
-
5
-module.exports = async function peerfsMount (drive, mountDir) {
6
-  mkdirp(mountDir)
7
-  var { destroy } = await mount(drive, mountDir)
8
-  process.once('SIGINT', () => cleanup(destroy))
9
-}
10
-
11
-function cleanup (destroy) {
12
-  return new Promise((resolve, reject) => {
13
-    destroy(err => {
14
-      if (err) return reject(err)
15
-      rimraf('./mnt', err => {
16
-        if (err) return reject(err)
17
-        return resolve()
18
-      })
19
-    })
20
-  })
21
-}

+ 644
- 467
package-lock.json
File diff suppressed because it is too large
View File


+ 9
- 7
package.json View File

@@ -1,22 +1,24 @@
1 1
 {
2
-  "name": "peerfs-swarm",
2
+  "name": "kappa-drive-mount",
3 3
   "version": "1.0.0",
4 4
   "description": "",
5 5
   "main": "index.js",
6 6
   "scripts": {
7
-    "test": "echo \"Error: no test specified\" && exit 1"
7
+    "test": "tape test/**/*.test.js | tap-spec"
8 8
   },
9 9
   "author": "",
10 10
   "license": "ISC",
11 11
   "dependencies": {
12
-    "dat-swarm-defaults": "^1.0.2",
13 12
     "debug": "^4.1.1",
14
-    "peerfs": "github:coboxcoop/peerfs#mount",
15
-    "discovery-swarm": "^6.0.0",
16 13
     "hyperdrive-fuse": "^1.1.2",
17 14
     "mkdirp": "^0.5.1",
18
-    "pump": "^3.0.0",
19
-    "random-access-memory": "^3.1.1",
20 15
     "rimraf": "^2.6.3"
16
+  },
17
+  "devDependencies": {
18
+    "kappa-drive": "0.0.1",
19
+    "tap-spec": "^5.0.0",
20
+    "tape": "^4.11.0",
21
+    "tape-plus": "^1.0.0",
22
+    "tmp": "^0.1.0"
21 23
   }
22 24
 }

+ 0
- 18
swarm.js View File

@@ -1,18 +0,0 @@
1
-const discovery = require('discovery-swarm')
2
-const pump = require('pump')
3
-const config = require('dat-swarm-defaults')
4
-const debug = require('debug')('kappa-drive')
5
-
6
-module.exports = function (drive, opts = {}) {
7
-  // TODO: add id property with local key  (cabal does this)
8
-  var swarm = discovery(config())
9
-
10
-  debug(`[SWARM] on ${drive.discoveryKey.toString('hex')}`)
11
-  swarm.join(drive.discoveryKey)
12
-
13
-  swarm.on('connection', (connection, peer) => {
14
-    debug(`[PEER] ${peer.id.toString('hex')}`)
15
-
16
-    pump(connection, drive.replicate({ live: true }), connection)
17
-  })
18
-}

+ 280
- 0
test/index.test.js View File

@@ -0,0 +1,280 @@
1
+const { describe } = require('tape-plus')
2
+const ram = require('random-access-memory')
3
+const fs = require('fs')
4
+const mount = require('../')
5
+const cleanup = mount.cleanup
6
+const KappaDrive = require('kappa-drive')
7
+const path = require('path')
8
+
9
+// TODO: test for readdir inside an empty directory - this fails, but passes with normal hyperdrive
10
+
11
+const { replicate, tmp, run } = require('./util')
12
+
13
+describe('mount', (context) => {
14
+  context('read mounted directory', (assert, next) => {
15
+    var drive = KappaDrive(ram)
16
+    var storage = tmp()
17
+    drive.ready(() => {
18
+      drive.writeFile('/hello.txt', 'world', (err) => {
19
+        assert.error(err, 'no error')
20
+        mount(drive, storage).then((destroy) => {
21
+          fs.readdir(storage, (err, files) => {
22
+            assert.error(err, 'no error')
23
+            assert.same(files, ['hello.txt'], 'readdir returns the written file')
24
+            fs.readFile(path.join(storage, 'hello.txt'), 'utf-8', (err, data) => {
25
+              assert.error(err, 'no error')
26
+              assert.same(data, 'world', 'the file has the correct content')
27
+              cleanup(destroy, storage).then(next)
28
+            })
29
+          })
30
+        })
31
+      })
32
+    })
33
+  })
34
+
35
+  context('write to mounted directory', (assert, next) => {
36
+    var drive = KappaDrive(ram)
37
+    var storage = tmp()
38
+    drive.ready(() => {
39
+      mount(drive, storage).then((destroy) => {
40
+        fs.writeFile(path.join(storage, 'hello.txt'), 'world', (err) => {
41
+          assert.error(err, 'no error')
42
+          drive.readdir('/', (err, files) => {
43
+            assert.error(err, 'no error')
44
+            assert.same(files, ['hello.txt'], 'drive.readdir returns the written file')
45
+            cleanup(destroy, storage).then(next)
46
+          })
47
+        })
48
+      })
49
+    })
50
+  })
51
+
52
+  context('mkdir in mounted directory', (assert, next) => {
53
+    var drive = KappaDrive(ram)
54
+    var storage = tmp()
55
+    drive.ready(() => {
56
+      mount(drive, storage).then((destroy) => {
57
+        fs.mkdir(path.join(storage, 'antelopes'), (err) => {
58
+          assert.error(err, 'no error')
59
+          drive.readdir('/', (err, files) => {
60
+            assert.error(err, 'no error')
61
+            assert.same(files, ['antelopes'], 'drive.readdir returns directory name')
62
+            cleanup(destroy, storage).then(next)
63
+          })
64
+        })
65
+      })
66
+    })
67
+  })
68
+
69
+  context('readdir in mounted newly created directory', (assert, next) => {
70
+    var drive = KappaDrive(ram)
71
+    var storage = tmp()
72
+    drive.ready(() => {
73
+      mount(drive, storage).then((destroy) => {
74
+        fs.mkdir(path.join(storage, 'antelopes'), (err) => {
75
+          assert.error(err, 'no error')
76
+          fs.writeFile(path.join(storage, 'antelopes', 'tree.txt'), 'flim', (err) => {
77
+            assert.error(err, 'no error')
78
+            fs.readdir(path.join(storage, 'antelopes'), (err, files) => {
79
+              assert.error(err, 'no error')
80
+              assert.same(files, ['tree.txt'], 'drive.readdir returns directory name')
81
+              cleanup(destroy, storage).then(next)
82
+            })
83
+          })
84
+        })
85
+      })
86
+    })
87
+  })
88
+
89
+  context("replicate - append to another peer's file - using unix commands", (assert, next) => {
90
+    var feedsOnDisk = {
91
+      orange: tmp(),
92
+      blue: tmp()
93
+    }
94
+    var orangeDrive = KappaDrive(feedsOnDisk.orange)
95
+    var blueDrive = KappaDrive(feedsOnDisk.blue)
96
+
97
+    var storage = {}
98
+    var destroy = {}
99
+
100
+    function setup (drive, colour, cb) {
101
+      storage[colour] = tmp()
102
+      mount(drive, storage[colour]).then((destroyFn) => {
103
+        destroy[colour] = destroyFn
104
+        drive.ready(cb)
105
+      })
106
+    }
107
+
108
+    setup(orangeDrive, 'orange', function () {
109
+      setup(blueDrive, 'blue', function () {
110
+        run(`echo "what noise does a dog make?" > ${storage.orange}/hello.txt`, (err, output) => {
111
+          assert.error(err, 'no error on write')
112
+          // 'seed' the blue drive with a file (TODO this is a hack)
113
+          run(`echo "this file is not needed" > ${storage.blue}/blue.txt`, (err, output) => {
114
+            assert.error(err, 'no error on echo to file')
115
+            replicate(orangeDrive, blueDrive, check)
116
+          })
117
+        })
118
+      })
119
+    })
120
+
121
+    function check (err) {
122
+      assert.error(err, 'no error on replicate')
123
+      run(`ls ${storage.blue}`, (err, output) => {
124
+        assert.error(err, 'no error on ls')
125
+        assert.true(output[0].split('\n').indexOf('hello.txt') > -1, 'file successfully replicated')
126
+        run(`echo "WOOF" >> ${storage.blue}/hello.txt`, (err, output) => {
127
+          assert.error(err, 'no error on echo append to file')
128
+          run(`cat ${storage.blue}/hello.txt`, (err, output) => {
129
+            assert.error(err, 'no error on cat out file')
130
+            assert.same(output[0], 'what noise does a dog make?\nWOOF\n', 'woof has correctly been appended')
131
+            replicate(orangeDrive, blueDrive, (err) => {
132
+              assert.error(err, 'no error on replicate')
133
+              // run(`ls -l ${storage.orange}`, err output)
134
+              run(`cat ${storage.orange}/hello.txt`, (err, output) => {
135
+                assert.error(err, 'no error on cat out file')
136
+                assert.same(output, ['what noise does a dog make?\nWOOF\n'], 'woof has correctly replicated')
137
+                cleanup(destroy.orange, storage.orange).then(() => {
138
+                  cleanup(destroy.blue, storage.blue).then(next)
139
+                })
140
+              })
141
+            })
142
+          })
143
+        })
144
+      })
145
+    }
146
+  })
147
+
148
+  context("replicate - append to another peer's empty file - using unix commands", (assert, next) => {
149
+    var feedsOnDisk = {
150
+      orange: tmp(),
151
+      blue: tmp()
152
+    }
153
+    var orangeDrive = KappaDrive(feedsOnDisk.orange)
154
+    var blueDrive = KappaDrive(feedsOnDisk.blue)
155
+
156
+    var storage = {}
157
+    var destroy = {}
158
+
159
+    function setup (drive, colour, cb) {
160
+      storage[colour] = tmp()
161
+      mount(drive, storage[colour]).then((destroyFn) => {
162
+        destroy[colour] = destroyFn
163
+        drive.ready(() => {
164
+          cb()
165
+        })
166
+      })
167
+    }
168
+
169
+    setup(orangeDrive, 'orange', function () {
170
+      setup(blueDrive, 'blue', function () {
171
+        run(`touch ${storage.orange}/hello.txt`, (err, output) => {
172
+          assert.error(err, 'no error on touch command')
173
+          // 'seed' the blue drive with a file (TODO this is a hack)
174
+          run(`echo "this file is not needed" >> ${storage.blue}/blue.txt`, (err, output) => {
175
+            assert.error(err, 'no error on echo append to file')
176
+            replicate(orangeDrive, blueDrive, check)
177
+          })
178
+        })
179
+      })
180
+    })
181
+    function check (err) {
182
+      assert.error(err, 'no error on replicate')
183
+      run(`ls ${storage.blue}`, (err, output) => {
184
+        assert.error(err, 'no error on ls')
185
+        assert.true(output[0].split('\n').indexOf('hello.txt') > -1, 'file successfully replicated')
186
+        run(`echo "WOOF" >> ${storage.blue}/hello.txt`, (err, output) => {
187
+          assert.error(err, 'no error on echo append to file')
188
+          run(`cat ${storage.blue}/hello.txt`, (err, output) => {
189
+            assert.error(err, 'no error on cat out file')
190
+            assert.same(output[0], 'WOOF\n', 'woof has correctly been written')
191
+            replicate(orangeDrive, blueDrive, (err) => {
192
+              assert.error(err, 'no error on replicate')
193
+              // run(`ls -l ${storage.orange}`, err output)
194
+              run(`cat ${storage.orange}/hello.txt`, (err, output) => {
195
+                assert.error(err, 'no error on cat out file')
196
+                assert.same(output, ['WOOF\n'], 'woof has correctly replicated')
197
+                cleanup(destroy.orange, storage.orange).then(() => {
198
+                  cleanup(destroy.blue, storage.blue).then(next)
199
+                })
200
+              })
201
+            })
202
+          })
203
+        })
204
+      })
205
+    }
206
+  })
207
+
208
+  context('https://ledger-git.dyne.org/CoBox/kappa-drive/issues/4', (assert, next) => {
209
+    var feedsOnDisk = {
210
+      yellow: tmp(),
211
+      purple: tmp()
212
+    }
213
+
214
+    var yellowDrive = KappaDrive(feedsOnDisk.yellow)
215
+    var purpleDrive = KappaDrive(feedsOnDisk.purple)
216
+
217
+    var storage = {}
218
+    var destroy = {}
219
+
220
+    function setup (drive, colour, cb) {
221
+      storage[colour] = tmp()
222
+      mount(drive, storage[colour]).then((destroyFn) => {
223
+        destroy[colour] = destroyFn
224
+        drive.ready(cb)
225
+      })
226
+    }
227
+
228
+    setup(yellowDrive, 'yellow', function () {
229
+      setup(purpleDrive, 'purple', function () {
230
+        fs.open(path.join(storage.yellow, 'hello.txt'), 'w+', (err, fd) => {
231
+          assert.error(err, 'no error')
232
+          fs.write(fd, Buffer.from('world'), 0, 5, (err) => {
233
+            assert.error(err, 'no error')
234
+            fs.close(fd, (err) => {
235
+              assert.error(err, 'no error')
236
+
237
+              fs.writeFile(path.join(storage.purple, 'seedfile.txt'), 'oink', (err) => {
238
+                assert.error(err, 'no error')
239
+
240
+                replicate(yellowDrive, purpleDrive, (err) => {
241
+                  assert.error(err, 'no error')
242
+
243
+                  fs.open(path.join(storage.purple, 'hello.txt'), 'r+', (err, fd) => {
244
+                    assert.error(err, 'no error')
245
+                    assert.ok(fd, 'returns a file descriptor')
246
+
247
+                    var readBuf = Buffer.alloc('world'.length)
248
+                    fs.read(fd, readBuf, 0, 5, null, (err, bytesRead) => {
249
+                      assert.error(err, 'no error')
250
+                      assert.same(bytesRead, 5, 'read correct number of bytes')
251
+                      assert.same(readBuf, Buffer.from('world'), 'successfully reads replicated file')
252
+
253
+                      fs.write(fd, Buffer.from('mundo'), 0, 5, (err) => {
254
+                        assert.error(err, 'no error')
255
+                        fs.close(fd, (err) => {
256
+                          assert.error(err, 'no error')
257
+
258
+                          replicate(yellowDrive, purpleDrive, (err) => {
259
+                            assert.error(err, 'no error')
260
+                            fs.readFile(path.join(storage.yellow, 'hello.txt'), 'utf-8', (err, data) => {
261
+                              assert.error(err, 'no error')
262
+                              assert.same(data, 'mundo', 'data corrently overwritten and replicted')
263
+                              cleanup(destroy.yellow, storage.yellow).then(() => {
264
+                                cleanup(destroy.purple, storage.purple).then(next)
265
+                              })
266
+                            })
267
+                          })
268
+                        })
269
+                      })
270
+                    })
271
+                  })
272
+                })
273
+              })
274
+            })
275
+          })
276
+        })
277
+      })
278
+    })
279
+  })
280
+})

+ 180
- 0
test/mount-extratests.js View File

@@ -0,0 +1,180 @@
1
+// TODO: these are a mess but just didn't want to delete them,
2
+// we might use some of this at some point
3
+
4
+
5
+  context("replicate - overwrite another peer's changes", (assert, next) => {
6
+    var feedsOnDisk = {
7
+      orange: tmp(),
8
+      blue: tmp()
9
+    }
10
+    var orangeDrive = KappaDrive(feedsOnDisk.orange)
11
+    var blueDrive = KappaDrive(feedsOnDisk.blue)
12
+
13
+    var storage = {}
14
+    var destroy = {}
15
+
16
+    function setup (drive, colour, cb) {
17
+      storage[colour] = tmp()
18
+      mount(drive, storage[colour]).then((destroyFn) => {
19
+        destroy[colour] = destroyFn
20
+        drive.ready(() => {
21
+          cb()
22
+        })
23
+      })
24
+    }
25
+
26
+    setup(orangeDrive, 'orange', function () {
27
+      setup(blueDrive, 'blue', function () {
28
+        fs.writeFile(path.join(storage.orange, 'orange.txt'), 'i like orange', (err) => {
29
+          assert.error(err, 'no error')
30
+          // 'seed' the blue drive with a file (TODO this is a hack)
31
+          fs.writeFile(path.join(storage.blue, 'blue.txt'), 'this file is not needed', (err) => {
32
+            assert.error(err, 'no error')
33
+            replicate(orangeDrive, blueDrive, check)
34
+          })
35
+        })
36
+      })
37
+    })
38
+    function check (err) {
39
+      assert.error(err, 'no error')
40
+      fs.open(path.join(storage.blue, 'orange.txt'), 'r+', (err, fd) => {
41
+        assert.error(err, 'no error')
42
+        assert.ok(fd, 'open returns a file descriptor')
43
+        var data = Buffer.alloc(7)
44
+        fs.read(fd, data, 0, 7, null, (err, bytesRead) => {
45
+          assert.error(err, 'no error')
46
+          assert.same(bytesRead, 7, 'read corrent number of bytes')
47
+          assert.same(data.toString(), 'i like ', 'read correct data')
48
+          fs.write(fd, Buffer.from('blue'), 0, 4, 7, (err, bytesWritten, buffer) => {
49
+            assert.error(err, 'no error')
50
+            assert.same(bytesWritten, 4, 'written correct number of bytes')
51
+            assert.same(buffer.toString(), 'blue', 'written correct data')
52
+            var data2 = Buffer.alloc(11)
53
+            fs.read(fd, data2, 0, 11, 0, (err, bytesRead) => {
54
+              assert.error(err, 'no error')
55
+              assert.same(data2.toString(), 'i like blue', "the file now contains 'i like blue'")
56
+              fs.close(fd, (err) => {
57
+                assert.error(err, 'no error')
58
+                blueDrive.readFile('orange.txt', 'utf-8', (err, data) => {
59
+                  assert.error(err, 'no error')
60
+                  assert.same(data, 'i like blue', 'readFile reads the changes correctly')
61
+                  replicate(orangeDrive, blueDrive, (err) => {
62
+                    assert.error(err, 'no error')
63
+                    fs.readFile(path.join(storage.orange, 'orange.txt'), 'utf-8', (err, data) => {
64
+                      assert.error(err, 'no error')
65
+                      assert.same(data, 'i like blue', 'correct changes are replicated')
66
+                      cleanup(destroy.orange, storage.orange).then(() => {
67
+                        cleanup(destroy.blue, storage.blue).then(next)
68
+                      })
69
+                    })
70
+                  })
71
+                })
72
+              })
73
+            })
74
+          })
75
+        })
76
+      })
77
+    }
78
+  })
79
+
80
+  // inspired by replication test for multifeed
81
+  context('replicate - write to mounted filesystem', (assert, next) => {
82
+    var feedsOnDisk = {
83
+      orange: tmp(),
84
+      blue: tmp()
85
+    }
86
+    var orangeDrive = KappaDrive(feedsOnDisk.orange)
87
+    var blueDrive = KappaDrive(feedsOnDisk.blue)
88
+    var storage = {}
89
+    var destroy = {}
90
+
91
+    function setup (drive, colour, cb) {
92
+      storage[colour] = tmp()
93
+      mount(drive, storage[colour]).then((destroyFn) => {
94
+        destroy[colour] = destroyFn
95
+        drive.ready(() => {
96
+          logLengths()
97
+          fs.writeFile(path.join(storage[colour], `${colour}.txt`), `i like ${colour}`, (err) => {
98
+            assert.error(err, `no error on writing ${colour}.txt`)
99
+            cb()
100
+          })
101
+        })
102
+      })
103
+    }
104
+
105
+    function logLengths () {
106
+      console.log('orange metadata', orangeDrive.metadata.length)
107
+      console.log('orange content ', orangeDrive.content.length)
108
+      if (blueDrive.metadata) {
109
+        console.log('blue metadata', blueDrive.metadata.length)
110
+        console.log('blue content ', blueDrive.content.length)
111
+      }
112
+    }
113
+    setup(orangeDrive, 'orange', function () {
114
+      setup(blueDrive, 'blue', function () {
115
+        logLengths()
116
+        replicate(orangeDrive, blueDrive, check)
117
+      })
118
+    })
119
+
120
+    function check (err) {
121
+      assert.error(err, 'no error on replicate')
122
+      orangeDrive.readdir('/', (err, files) => {
123
+        assert.error(err, 'no error on readDir')
124
+        assert.same(files.length, 2, 'orange drive has 2 files')
125
+        logLengths()
126
+        assert.ok(files.indexOf('blue.txt') > -1, 'orangedrive has file named blue.txt')
127
+        // orangeDrive.open('/blue.txt', 'r', (err, fd) => {
128
+          // assert.error(err, 'no error')
129
+          // var readBuf = Buffer.alloc('i like blue'.length)
130
+
131
+          orangeDrive.readFile('/blue.txt', (err, data) => {
132
+          // orangeDrive.read(fd, readBuf, 0, 11, null, (err, bytes, data) => {
133
+            assert.error(err, 'no error')
134
+            assert.same(data, Buffer.from('i like blue'), 'blue.txt on orange drive has correct content')
135
+
136
+            fs.readdir(storage.orange, (err, files) => {
137
+              assert.error(err, 'no error')
138
+              assert.same(files.length, 2, 'orange mounted volume has two files')
139
+              assert.ok(files.indexOf('blue.txt') > -1, 'orange mounted volume has file named blue.txt')
140
+
141
+              fs.readFile(path.join(storage.orange, 'blue.txt'), 'utf-8', (err, data) => {
142
+                assert.error(err, 'no error')
143
+                assert.same(data, 'i like blue', 'blue.txt in mounted volume has correct content')
144
+
145
+                blueDrive.readdir('/', (err, files) => {
146
+                  assert.error(err, 'no error')
147
+                  assert.same(files.length, 2, 'blue drive has 2 files')
148
+                  assert.ok(files.indexOf('orange.txt') > -1, 'bluedrive has file named orange.txt')
149
+
150
+                  blueDrive.open('orange.txt', 'r', (err, fd) => {
151
+                    assert.error(err, 'no error')
152
+                    blueDrive.read(fd, Buffer.alloc('i like orange'.length), 0, 14, null, (err, bytes, data) => {
153
+                      assert.error(err, 'no error')
154
+                      assert.same(data, Buffer.from('i like orange'), 'orange.txt on blue drive has correct content')
155
+
156
+                      fs.readdir(storage.blue, (err, files) => {
157
+                        assert.error(err, 'no error')
158
+                        assert.same(files.length, 2, 'orange mounted volume has two files')
159
+                        assert.ok(files.indexOf('orange.txt') > -1, 'blue mounted volume has file named orange.txt')
160
+
161
+                        fs.readFile(path.join(storage.blue, 'orange.txt'), 'utf-8', (err, data) => {
162
+                          assert.error(err, 'no error')
163
+                          assert.same(data, 'i like orange', 'orange.txt in mounted volume has correct content')
164
+
165
+                          cleanup(destroy.orange, storage.orange).then(() => {
166
+                            cleanup(destroy.blue, storage.blue).then(next)
167
+                          })
168
+                        })
169
+                      })
170
+                    })
171
+                  })
172
+                })
173
+              })
174
+            })
175
+          })
176
+        // })
177
+      })
178
+    }
179
+  })
180
+

+ 78
- 0
test/util.js View File

@@ -0,0 +1,78 @@
1
+const rimraf = require('rimraf')
2
+const debug = require('debug')('cleanup')
3
+const tmpdir = require('tmp').dirSync
4
+const mkdirp = require('mkdirp')
5
+const { spawn } = require('child_process')
6
+
7
+function cleanup (dirs, cb) {
8
+  if (!cb) cb = noop
9
+  if (!Array.isArray(dirs)) dirs = [dirs]
10
+
11
+  var pending = dirs.length
12
+
13
+  function next (n) {
14
+    var dir = dirs[n]
15
+    if (!dir) return
16
+
17
+    rimraf(dir, (err) => {
18
+      debug(`[CLEANUP] ${dir} : ${ err ? 'failed' : 'success'}`)
19
+      if (err) return done(err)
20
+      process.nextTick(next, n + 1)
21
+      done()
22
+    })
23
+  }
24
+
25
+  function done (err) {
26
+    if (err) {
27
+      pending = Infinity
28
+      return cb(err)
29
+    }
30
+    if (!--pending) return cb()
31
+  }
32
+
33
+  next(0)
34
+}
35
+
36
+function tmp () {
37
+  var path = tmpdir().name
38
+  mkdirp.sync(path)
39
+  debug(`[TEMP] creating temp directory ${path}`)
40
+  return path
41
+}
42
+
43
+function replicate (drive1, drive2, cb) {
44
+  if (!cb) cb = noop
45
+  var s = drive1.replicate({ live: false })
46
+  var d = drive2.replicate({ live: false })
47
+
48
+  s.pipe(d).pipe(s)
49
+
50
+  s.on('error', () => {
51
+    debug("replicate: ", err ? "FAIL" : "SUCCESS")
52
+    if (err) return cb(err)
53
+  })
54
+
55
+  s.on('end', cb)
56
+}
57
+
58
+function uniq (array) {
59
+  if (!Array.isArray(array)) array = [array]
60
+  return Array.from(new Set(array))
61
+}
62
+
63
+function noop () {}
64
+
65
+function run (command, cb) {
66
+  const child = spawn('sh', ['-c', command])
67
+  var output = []
68
+  child.stdout.on('data', (data) => { output.push(data.toString()) })
69
+
70
+  child.stderr.on('data', (data) => { output.push(data.toString()) })
71
+
72
+  child.on('exit', function (code) {
73
+    if (code) return cb(new Error('child process exited with error ' + code))
74
+    cb(null, output)
75
+  })
76
+}
77
+
78
+module.exports = { cleanup, tmp, replicate, uniq, run }