Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit fd10958

Browse files
committed
test: add mfs interop tests
1 parent f5b4587 commit fd10958

File tree

5 files changed

+348
-4
lines changed

5 files changed

+348
-4
lines changed

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"async": "^2.6.1",
4040
"bl": "^2.0.1",
4141
"bs58": "^4.0.1",
42-
"buffer-loader": "0.0.1",
42+
"buffer-loader": "~0.0.1",
4343
"chai": "^4.1.2",
4444
"cross-env": "^5.2.0",
4545
"cids": "~0.5.3",
@@ -51,9 +51,10 @@
5151
"form-data": "^2.3.2",
5252
"go-ipfs-dep": "~0.4.17",
5353
"hat": "0.0.3",
54-
"ipfs": "~0.31.1",
55-
"ipfs-api": "^22.2.4",
56-
"ipfsd-ctl": "~0.39.0",
54+
"ipfs": "~0.31.6",
55+
"ipfs-api": "^24.0.0",
56+
"ipfsd-ctl": "~0.39.1",
57+
"ipfs-unixfs": "~0.1.15",
5758
"left-pad": "^1.3.0",
5859
"libp2p-websocket-star-rendezvous": "~0.2.3",
5960
"lodash": "^4.17.10",

test/browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
require('./kad-dht')
77
require('./circuit')
88
// require('./repo')
9+
require('./files')

test/files.js

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const chai = require('chai')
5+
const dirtyChai = require('dirty-chai')
6+
const expect = chai.expect
7+
chai.use(dirtyChai)
8+
const crypto = require('crypto')
9+
const UnixFs = require('ipfs-unixfs')
10+
const {
11+
spawnInitAndStartGoDaemon,
12+
spawnInitAndStartJsDaemon,
13+
stopDaemon
14+
} = require('./utils/daemon')
15+
16+
class ExpectedError extends Error {
17+
18+
}
19+
20+
function checkNodeTypes (daemon, file) {
21+
return daemon.api.object.get(file.hash)
22+
.then(node => {
23+
const meta = UnixFs.unmarshal(node.data)
24+
25+
expect(meta.type).to.equal('file')
26+
expect(node.links.length).to.equal(2)
27+
28+
return Promise.all(
29+
node.links.map(link => daemon.api.object.get(link.toJSON().multihash).then(child => {
30+
const childMeta = UnixFs.unmarshal(child.data)
31+
32+
expect(childMeta.type).to.equal('raw')
33+
}))
34+
)
35+
})
36+
}
37+
38+
function addFile (daemon, data) {
39+
const fileName = 'test-file'
40+
41+
return daemon.api.files.write(`/${fileName}`, data, {
42+
create: true
43+
})
44+
// cannot list file directly - https://github.com/ipfs/go-ipfs/issues/5044
45+
.then(() => {
46+
return daemon.api.files.ls('/', {
47+
l: true
48+
})
49+
})
50+
.then(files => {
51+
return files.filter(file => file.name === fileName).pop()
52+
})
53+
}
54+
55+
const compare = (...ops) => {
56+
expect(ops.length).to.be.above(1)
57+
58+
return Promise.all(
59+
ops
60+
)
61+
.then(results => {
62+
expect(results.length).to.equal(ops.length)
63+
64+
const result = results.pop()
65+
66+
results.forEach(res => expect(res).to.deep.equal(result))
67+
})
68+
}
69+
70+
const compareErrors = (...ops) => {
71+
expect(ops.length).to.be.above(1)
72+
73+
return Promise.all(
74+
// even if operations fail, their errors should be the same
75+
ops.map(op => op.then(() => {
76+
throw new ExpectedError('Expected operation to fail')
77+
}).catch(error => {
78+
if (error instanceof ExpectedError) {
79+
throw error
80+
}
81+
82+
return {
83+
message: error.message,
84+
code: error.code
85+
}
86+
}))
87+
)
88+
.then(results => {
89+
expect(results.length).to.equal(ops.length)
90+
91+
const result = results.pop()
92+
93+
results.forEach(res => expect(res).to.deep.equal(result))
94+
})
95+
}
96+
97+
describe('files', function () {
98+
this.timeout(50 * 1000)
99+
100+
let go
101+
let js
102+
103+
before(() => {
104+
return Promise.all([
105+
spawnInitAndStartGoDaemon(),
106+
spawnInitAndStartJsDaemon()
107+
])
108+
.then(([goDaemon, jsDaemon]) => {
109+
go = goDaemon
110+
js = jsDaemon
111+
})
112+
})
113+
114+
after(() => {
115+
return Promise.all([
116+
stopDaemon(go),
117+
stopDaemon(js)
118+
])
119+
})
120+
121+
it('returns an error when reading non-existent files', () => {
122+
const readNonExistentFile = (daemon) => {
123+
return daemon.api.files.read(`/i-do-not-exist-${Math.random()}`)
124+
}
125+
126+
return compareErrors(
127+
readNonExistentFile(go),
128+
readNonExistentFile(js)
129+
)
130+
})
131+
132+
it('returns an error when writing deeply nested files and the parents do not exist', () => {
133+
const readNonExistentFile = (daemon) => {
134+
return daemon.api.files.write(`/foo-${Math.random()}/bar-${Math.random()}/baz-${Math.random()}/i-do-not-exist-${Math.random()}`, Buffer.from([0, 1, 2, 3]))
135+
}
136+
137+
return compareErrors(
138+
readNonExistentFile(go),
139+
readNonExistentFile(js)
140+
)
141+
})
142+
143+
it('uses raw nodes for leaf data', () => {
144+
const data = crypto.randomBytes(1024 * 300)
145+
const testLeavesAreRaw = (daemon) => {
146+
return addFile(daemon, data)
147+
.then(file => checkNodeTypes(daemon, file))
148+
}
149+
150+
return compare(
151+
testLeavesAreRaw(go),
152+
testLeavesAreRaw(js)
153+
)
154+
})
155+
156+
it('errors when creating the same directory twice', () => {
157+
const path = `/test-dir-${Math.random()}`
158+
159+
return compareErrors(
160+
go.api.files.mkdir(path).then(() => go.api.files.mkdir(path)),
161+
js.api.files.mkdir(path).then(() => js.api.files.mkdir(path))
162+
)
163+
})
164+
165+
it('does not error when creating the same directory twice and -p is passed', () => {
166+
const path = `/test-dir-${Math.random()}`
167+
168+
return compare(
169+
go.api.files.mkdir(path).then(() => go.api.files.mkdir(path, {p: true})),
170+
js.api.files.mkdir(path).then(() => js.api.files.mkdir(path, {p: true}))
171+
)
172+
})
173+
174+
it('errors when creating the root directory', () => {
175+
const path = '/'
176+
177+
return compareErrors(
178+
go.api.files.mkdir(path).then(() => go.api.files.mkdir(path)),
179+
js.api.files.mkdir(path).then(() => js.api.files.mkdir(path))
180+
)
181+
})
182+
183+
describe('has the same hashes for', () => {
184+
const testHashesAreEqual = (daemon, data, options) => {
185+
return daemon.api.files.add(data, options)
186+
.then(files => files[0].hash)
187+
}
188+
189+
const _writeData = (daemon, initialData, newData, options) => {
190+
const fileName = `file-${Math.random()}.txt`
191+
192+
return daemon.api.files.write(`/${fileName}`, initialData, {
193+
create: true
194+
})
195+
.then(() => daemon.api.files.ls('/', {
196+
l: true
197+
}))
198+
.then(files => files.filter(file => file.name === fileName).pop().hash)
199+
}
200+
201+
const appendData = (daemon, initialData, appendedData) => {
202+
return _writeData(daemon, initialData, appendedData, {
203+
offset: initialData.length
204+
})
205+
}
206+
207+
const overwriteData = (daemon, initialData, newData) => {
208+
return _writeData(daemon, initialData, newData, {
209+
offset: 0
210+
})
211+
}
212+
213+
it('empty files', () => {
214+
const data = Buffer.alloc(0)
215+
216+
return compare(
217+
testHashesAreEqual(go, data),
218+
testHashesAreEqual(js, data)
219+
)
220+
})
221+
222+
it('small files', () => {
223+
const data = Buffer.from([0x00, 0x01, 0x02])
224+
225+
return compare(
226+
testHashesAreEqual(go, data),
227+
testHashesAreEqual(js, data)
228+
)
229+
})
230+
231+
it('big files', () => {
232+
const data = crypto.randomBytes(1024 * 3000)
233+
234+
return compare(
235+
testHashesAreEqual(go, data),
236+
testHashesAreEqual(js, data)
237+
)
238+
})
239+
240+
it('files that have had data appended', () => {
241+
const initialData = crypto.randomBytes(1024 * 300)
242+
const appendedData = crypto.randomBytes(1024 * 300)
243+
244+
return compare(
245+
appendData(go, initialData, appendedData),
246+
appendData(js, initialData, appendedData)
247+
)
248+
})
249+
250+
it('files that have had data overwritten', () => {
251+
const bytes = 1024 * 300
252+
const initialData = crypto.randomBytes(bytes)
253+
const newData = crypto.randomBytes(bytes)
254+
255+
return compare(
256+
overwriteData(go, initialData, newData),
257+
overwriteData(js, initialData, newData)
258+
)
259+
})
260+
261+
it('small files with CIDv1', () => {
262+
const data = Buffer.from([0x00, 0x01, 0x02])
263+
const options = {
264+
cidVersion: 1
265+
}
266+
267+
return compare(
268+
testHashesAreEqual(go, data, options),
269+
testHashesAreEqual(js, data, options)
270+
)
271+
})
272+
273+
it('big files with CIDv1', () => {
274+
const data = crypto.randomBytes(1024 * 3000)
275+
const options = {
276+
cidVersion: 1
277+
}
278+
279+
return compare(
280+
testHashesAreEqual(go, data, options),
281+
testHashesAreEqual(js, data, options)
282+
)
283+
})
284+
})
285+
})

test/node.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ require('./repo')
77
require('./exchange-files')
88
require('./kad-dht')
99
require('./pin')
10+
require('./files')

test/utils/daemon.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict'
2+
3+
const os = require('os')
4+
const path = require('path')
5+
const hat = require('hat')
6+
const waterfall = require('async/waterfall')
7+
const DaemonFactory = require('ipfsd-ctl')
8+
const goDf = DaemonFactory.create()
9+
const jsDf = DaemonFactory.create({ type: 'js' })
10+
11+
const spawnInitAndStartDaemon = (factory) => {
12+
const dir = path.join(os.tmpdir(), hat())
13+
let instance
14+
15+
return new Promise((resolve, reject) => {
16+
waterfall([
17+
(cb) => factory.spawn({
18+
repoPath: dir,
19+
disposable: false,
20+
initOptions: {
21+
bits: 1024
22+
}
23+
}, cb),
24+
(node, cb) => {
25+
instance = node
26+
instance.init(cb)
27+
},
28+
(cb) => instance.start((error) => cb(error, instance))
29+
], (error) => {
30+
if (error) {
31+
return reject(error)
32+
}
33+
34+
resolve(instance)
35+
})
36+
})
37+
}
38+
39+
const stopDaemon = (daemon) => {
40+
return new Promise((resolve, reject) => {
41+
daemon.stop((error) => {
42+
if (error) {
43+
return reject(error)
44+
}
45+
46+
resolve()
47+
})
48+
})
49+
}
50+
51+
module.exports = {
52+
spawnInitAndStartDaemon,
53+
spawnInitAndStartGoDaemon: () => spawnInitAndStartDaemon(goDf),
54+
spawnInitAndStartJsDaemon: () => spawnInitAndStartDaemon(jsDf),
55+
stopDaemon
56+
}

0 commit comments

Comments
 (0)