UNPKG

11.4 kBJavaScriptView Raw
1var test = require('tape')
2var hyperlog = require('hyperlog')
3var memdb = require('memdb')
4var path = require('path')
5var fdstore = require('fd-chunk-store')
6var osmdb = require('osm-p2p-db')
7var http = require('http')
8var url = require('url')
9var concat = require('concat-stream')
10var waterfall = require('run-waterfall')
11var eos = require('end-of-stream')
12var once = require('once')
13
14var tmpdir = require('os').tmpdir()
15var createServer = require('./lib/test_server.js')
16var slowdb = require('./lib/slowdb.js')
17
18var DELAY = process.env.OSM_P2P_DB_DELAY
19
20function createOsm () {
21 var storefile = path.join(tmpdir, 'osm-store-' + Math.random())
22 return osmdb({
23 log: hyperlog(memdb(), { valueEncoding: 'json' }),
24 db: DELAY ? slowdb({delay: DELAY}) : memdb(),
25 store: fdstore(4096, storefile)
26 })
27}
28
29test('do not include points from an excluded way fork', function (t) {
30 t.plan(10)
31
32 // Has the base way
33 var osmBase = createOsm()
34
35 // Has a fork of the base way
36 var osmForkA = createOsm()
37
38 // Has a different fork of the base way
39 var osmForkB = createOsm()
40
41 var nodes = [
42 {
43 type: 'node',
44 lon: 0,
45 lat: -1
46 },
47 {
48 type: 'node',
49 lon: 0,
50 lat: 0
51 },
52 {
53 type: 'node',
54 lon: 0,
55 lat: 1
56 }
57 ]
58
59 var wayId
60 var wayVersionId
61 var keyIds
62 var forkARefs
63 var forkAWayVersionId
64 var forkBRefs
65 var forkBWayVersionId
66 var osmServer
67
68 // Run the test steps
69 waterfall([
70 step1,
71 step2,
72 step3,
73 step4,
74 step5,
75 step6,
76 step7,
77 step8,
78 step9
79 ], function (err) {
80 t.error(err)
81 })
82
83 // 1. Create base way
84 function step1 (done) {
85 createChangeset(osmBase, function (err, cs) {
86 t.error(err)
87 createNodes(osmBase, nodes, cs, function (keys) {
88 keyIds = keys
89 createWay(osmBase, keys, cs, function (err, way, wayVersion) {
90 t.error(err)
91 wayId = way
92 wayVersionId = wayVersion
93 done()
94 })
95 })
96 })
97 }
98
99 // 2. Replicate base osm to fork A osm
100 function step2 (done) {
101 sync(osmBase.log, osmForkA.log, function (err) {
102 if (err) return done(err)
103 osmForkB.ready(done)
104 })
105 }
106
107 // 3. Write modifications to fork A
108 function step3 (done) {
109 var node = {
110 type: 'node',
111 lon: 10,
112 lat: 10
113 }
114 createChangeset(osmForkA, function (err, cs) {
115 t.error(err)
116 createNodes(osmForkA, [node], cs, function (keys) {
117 var refs = keyIds.concat(keys)
118 forkARefs = refs
119 updateWay(osmForkA, wayId, wayVersionId, refs, cs, function (err, way) {
120 t.error(err)
121 forkAWayVersionId = way.key
122 done()
123 })
124 })
125 })
126 }
127
128 // 4. Replicate base osm to fork B osm
129 function step4 (done) {
130 sync(osmBase.log, osmForkB.log, function (err) {
131 if (err) return done(err)
132 osmForkB.ready(done)
133 })
134 }
135
136 // 5. Write modifications to fork B
137 function step5 (done) {
138 var node = {
139 type: 'node',
140 lon: -10,
141 lat: -10
142 }
143 createChangeset(osmForkB, function (err, cs) {
144 t.error(err)
145 createNodes(osmForkB, [node], cs, function (keys) {
146 var refs = keyIds.concat(keys)
147 forkBRefs = refs
148 updateWay(osmForkB, wayId, wayVersionId, refs, cs, function (err, way) {
149 forkBWayVersionId = way.key
150 done(err)
151 })
152 })
153 })
154 }
155
156 // 6. Replicate fork A and fork B
157 function step6 (done) {
158 sync(osmForkA.log, osmForkB.log, function (err) {
159 if (err) return done(err)
160 osmForkB.ready(done)
161 })
162 }
163
164 // 7. Create an osm-p2p-server instance from fork A
165 function step7 (done) {
166 createServer(function (d) {
167 osmServer = d
168 done()
169 })
170 }
171
172 // 8. Replicate fork A and the server
173 function step8 (done) {
174 sync(osmServer.osm.log, osmForkA.log, function (err) {
175 t.error(err)
176 osmServer.osm.ready(done)
177 })
178 }
179
180 // 9. Run an http query on the server to see which way & points are returned
181 function step9 (done) {
182 var opts = {
183 hostname: 'localhost',
184 port: url.parse(osmServer.base).port,
185 path: '/api/0.6/map?bbox=-90,-90,90,90',
186 headers: {
187 'Accept': 'application/json'
188 }
189 }
190 http.get(opts, function (res) {
191 res.pipe(concat(function (json) {
192 var data = JSON.parse(json)
193 var nodeIds = data.elements
194 .filter(function (elm) { return elm.type === 'node' })
195 .map(function (elm) { return elm.id })
196 var ways = data.elements.filter(function (elm) { return elm.type === 'way' })
197
198 t.equal(ways.length, 1)
199
200 // Ensure the way present matches one of the two possible forks and its nodes
201 if (ways[0].version === forkAWayVersionId) {
202 t.equal(ways[0].version, forkAWayVersionId)
203 t.deepEqual(nodeIds.sort(), forkARefs.sort())
204 } else if (ways[0].version === forkBWayVersionId) {
205 t.equal(ways[0].version, forkBWayVersionId)
206 t.deepEqual(nodeIds.sort(), forkBRefs.sort())
207 } else {
208 t.error('unexpected way version id')
209 }
210
211 osmServer.server.cleanup(done)
212 }))
213 })
214 }
215})
216
217test('no extra points from forks /w 1 deleted node and 1 modified node', function (t) {
218 t.plan(11)
219
220 // Has the base way
221 var osmBase = createOsm()
222
223 // Has a fork /w a modified point
224 var osmForkA = createOsm()
225
226 // Has a fork /w a deleted point
227 var osmForkB = createOsm()
228
229 var nodes = [
230 {
231 type: 'node',
232 lon: 0,
233 lat: -1
234 },
235 {
236 type: 'node',
237 lon: 0,
238 lat: 0
239 },
240 {
241 type: 'node',
242 lon: 0,
243 lat: 1
244 }
245 ]
246
247 var wayId
248 var wayVersionId
249 var keyIds
250 var forkBRefs
251 var osmServer
252
253 // Run the test steps
254 waterfall([
255 step1,
256 step2,
257 step3,
258 step4,
259 step5,
260 step6,
261 step7,
262 step8,
263 step9
264 ], function (err) {
265 t.error(err)
266 })
267
268 // 1. Create base way
269 function step1 (done) {
270 createChangeset(osmBase, function (err, cs) {
271 t.error(err)
272 createNodes(osmBase, nodes, cs, function (keys) {
273 keyIds = keys
274 createWay(osmBase, keys, cs, function (err, way, wayVersion) {
275 t.error(err)
276 wayId = way
277 wayVersionId = wayVersion
278 done()
279 })
280 })
281 })
282 }
283
284 // 2. Replicate base osm to fork A osm
285 function step2 (done) {
286 sync(osmBase.log, osmForkA.log, function (err) {
287 if (err) return done(err)
288 osmForkB.ready(done)
289 })
290 }
291
292 // 3. Edit the 3rd point on fork A
293 function step3 (done) {
294 var node = {
295 type: 'node',
296 lon: 10,
297 lat: 10
298 }
299 createChangeset(osmForkA, function (err, cs) {
300 t.error(err)
301 createNodes(osmForkA, [node], cs, function (keys) {
302 var refs = keyIds.concat([])
303 refs[2] = keys[0]
304 updateWay(osmForkA, wayId, wayVersionId, refs, cs, function (err, way) {
305 t.error(err)
306 done()
307 })
308 })
309 })
310 }
311
312 // 4. Replicate base osm to fork B osm
313 function step4 (done) {
314 sync(osmBase.log, osmForkB.log, function (err) {
315 if (err) return done(err)
316 osmForkB.ready(done)
317 })
318 }
319
320 // 5. Delete 3rd point on fork B
321 function step5 (done) {
322 createChangeset(osmForkB, function (err, cs) {
323 t.error(err)
324 deleteNode(osmForkB, keyIds[2], cs, function (err) {
325 t.error(err)
326 forkBRefs = keyIds.slice(0, 2)
327 updateWay(osmForkB, wayId, wayVersionId, keyIds.slice(0, 2), cs, function (err, way) {
328 done(err)
329 })
330 })
331 })
332 }
333
334 // 6. Replicate fork A and fork B
335 function step6 (done) {
336 sync(osmForkA.log, osmForkB.log, function (err) {
337 if (err) return done(err)
338 osmForkB.ready(done)
339 })
340 }
341
342 // 7. Create an osm-p2p-server instance from fork A
343 function step7 (done) {
344 createServer(function (d) {
345 osmServer = d
346 done()
347 })
348 }
349
350 // 8. Replicate fork A and the server
351 function step8 (done) {
352 sync(osmServer.osm.log, osmForkA.log, function (err) {
353 t.error(err)
354 osmServer.osm.ready(done)
355 })
356 }
357
358 // 9. Run an http query on the server to see which way & points are returned
359 function step9 (done) {
360
361 function query (forks, done) {
362 var opts = {
363 hostname: 'localhost',
364 port: url.parse(osmServer.base).port,
365 path: '/api/0.6/map?bbox=-90,-90,90,90' + (forks ? '&forks=true' : ''),
366 headers: {
367 'Accept': 'application/json'
368 }
369 }
370 http.get(opts, function (res) {
371 res.pipe(concat(function (json) {
372 done(null, json)
373 }))
374 })
375 }
376
377 query(false, function (_, json) {
378 var data = JSON.parse(json)
379 var nodeIds = data.elements
380 .filter(function (elm) { return elm.type === 'node' })
381 .map(function (elm) { return elm.id })
382 var ways = data.elements.filter(function (elm) { return elm.type === 'way' })
383
384 // TODO(noffle): Test for a failure, and if so, dump lots of debug
385 // information so we can try and track down this bug
386 // (https://github.com/digidem/osm-p2p-server/issues/28)
387 if (nodeIds.length !== 2) {
388 console.error('ERROR -- show @noffle this output!')
389 query(true, function (_, json) {
390 console.error(json.toString())
391 osmServer.server.cleanup(done)
392 })
393 } else {
394 // Ensure the way present matches the deleted fork, and the extra node
395 // is not returned.
396 t.equal(ways.length, 1)
397 t.equal(nodeIds.length, 2)
398 t.deepEqual(nodeIds.sort(), forkBRefs.sort())
399
400 osmServer.server.cleanup(done)
401 }
402 })
403 }
404})
405
406// creates a list of nodes
407function createNodes (osm, nodes, changesetId, done) {
408 var keys = []
409 ;(function next () {
410 var node = nodes.shift()
411 if (!node) return done(keys)
412 node.changeset = changesetId
413 node.timestamp = (new Date()).toISOString()
414 osm.create(node, function (err, key) {
415 if (err) return done(err)
416 keys.push(key)
417 next()
418 })
419 })()
420}
421
422// deletes a node
423function deleteNode (osm, nodeId, changesetId, done) {
424 var op = {
425 type: 'del',
426 key: nodeId,
427 value: { type: 'node', changeset: changesetId }
428 }
429 osm.batch([op], done)
430}
431
432// creates a changeset with a way and nodes
433function createWay (osm, nodeIds, changesetId, done) {
434 osm.create({
435 type: 'way',
436 refs: nodeIds,
437 changeset: changesetId,
438 timestamp: (new Date()).toISOString()
439 }, function (err, key, node) {
440 done(err, key, node.key)
441 })
442}
443
444// updates a changeset with a way and nodes
445function updateWay (osm, way, parentId, refs, changesetId, done) {
446 osm.put(way, {
447 type: 'way',
448 refs: refs,
449 changeset: changesetId,
450 timestamp: (new Date()).toISOString()
451 },
452 { links: [parentId] },
453 function (err, way) {
454 done(err, way)
455 })
456}
457
458// create a new changeset
459function createChangeset (osm, done) {
460 osm.create({
461 type: 'changeset'
462 }, function (err, key, node) {
463 done(err, key, node.key)
464 })
465}
466
467function sync (log1, log2, done) {
468 done = once(done)
469 var r1 = log1.replicate()
470 eos(r1, onEnd)
471
472 var r2 = log2.replicate()
473 eos(r2, onEnd)
474
475 r1.pipe(r2).pipe(r1)
476
477 var pending = 2
478 function onEnd (err) {
479 if (err) {
480 return done(err)
481 }
482 if (--pending === 0) {
483 return done()
484 }
485 }
486}