UNPKG

10.6 kBJavaScriptView Raw
1var test = require('tape')
2var contentType = require('content-type')
3var parsexml = require('xml-parser')
4var hyperquest = require('hyperquest')
5var concat = require('concat-stream')
6var isISODate = require('isostring')
7
8var base, server, changeId
9
10var createServer = require('./lib/test_server.js')
11
12test('changeset_upload_relation.js: setup server', function (t) {
13 createServer(function (d) {
14 base = d.base
15 server = d.server
16 t.end()
17 })
18})
19
20test('create changeset upload', function (t) {
21 t.plan(4)
22 var href = base + 'changeset/create'
23 var hq = hyperquest.put(href, {
24 headers: { 'content-type': 'text/xml' }
25 })
26 hq.once('response', function (res) {
27 t.equal(res.statusCode, 200, 'create 200 ok')
28 var contentObj = contentType.parse(res)
29 t.equal(contentObj.type, 'text/plain', 'media type correct')
30 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
31 })
32 hq.pipe(concat({ encoding: 'string' }, function (body) {
33 changeId = body.trim()
34 t.ok(/^[0-9A-Fa-f]+$/.test(changeId), 'expected changeset id response')
35 }))
36 hq.end(`<osm>
37 <changeset>
38 <tag k="comment" v="wow"/>
39 </changeset>
40 </osm>`)
41})
42
43var ids = {}
44var versions = {}
45test('add docs to changeset upload', function (t) {
46 t.plan(9)
47
48 var href = base + 'changeset/' + changeId + '/upload'
49 var hq = hyperquest.post(href, {
50 headers: { 'content-type': 'text/xml' }
51 })
52 hq.on('response', function (res) {
53 t.equal(res.statusCode, 200)
54 var contentObj = contentType.parse(res)
55 t.equal(contentObj.type, 'text/xml', 'media type correct')
56 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
57 })
58 hq.pipe(concat({ encoding: 'string' }, function (body) {
59 var xml = parsexml(body)
60 t.equal(xml.root.name, 'diffResult')
61 t.deepEqual(xml.root.children.map(function (c) {
62 return c.attributes.old_id
63 }).sort(), ['-1', '-2', '-3', '-4'])
64 xml.root.children.forEach(function (c) {
65 ids[c.attributes.old_id] = c.attributes.new_id
66 t.notEqual(c.attributes.old_id, c.attributes.new_id,
67 'placeholder id should not equal new id')
68 versions[c.attributes.old_id] = c.attributes.new_version
69 })
70 }))
71 hq.end(`<osmChange version="1.0" generator="acme osm editor">
72 <create>
73 <node id="-1" changeset="${changeId}" lat="64.5" lon="-121.5"/>
74 <node id="-2" changeset="${changeId}" lat="63.9" lon="-120.9"/>
75 <way id="-3" changeset="${changeId}">
76 <nd ref="-1"/>
77 <nd ref="-2"/>
78 </way>
79 <relation id="-4" changeset="${changeId}">
80 <tag k="hello" v="world" />
81 <member type="way" ref="-3" role="" />
82 </relation>
83 </create>
84 </osmChange>`)
85})
86
87test('get osmchange doc from upload', function (t) {
88 t.plan(8)
89 var expected = [
90 {
91 name: 'node',
92 attributes: {
93 changeset: changeId,
94 id: ids['-1'],
95 version: versions['-1'],
96 lat: '64.5',
97 lon: '-121.5'
98 },
99 children: []
100 },
101 {
102 name: 'node',
103 attributes: {
104 changeset: changeId,
105 id: ids['-2'],
106 version: versions['-2'],
107 lat: '63.9',
108 lon: '-120.9'
109 },
110 children: []
111 },
112 {
113 name: 'way',
114 attributes: {
115 changeset: changeId,
116 id: ids['-3'],
117 version: versions['-3']
118 },
119 content: '',
120 children: [
121 {
122 name: 'nd',
123 attributes: { ref: ids['-2'] },
124 children: []
125 },
126 {
127 name: 'nd',
128 attributes: { ref: ids['-1'] },
129 children: []
130 }
131 ].sort(cmpref)
132 },
133 {
134 name: 'relation',
135 attributes: {
136 changeset: changeId,
137 id: ids['-4'],
138 version: versions['-4']
139 },
140 content: '',
141 children: [
142 {
143 name: 'member',
144 attributes: { type: 'way', ref: ids['-3'], role: '' },
145 children: []
146 },
147 {
148 name: 'tag',
149 attributes: { k: 'hello', v: 'world' },
150 children: []
151 }
152 ]
153 }
154 ].sort(cmpch)
155 var href = base + 'changeset/' + changeId + '/download'
156 var hq = hyperquest(href, {
157 headers: { 'content-type': 'text/xml' }
158 })
159 hq.pipe(concat({ encoding: 'string' }, function (body) {
160 var xml = parsexml(body)
161 t.equal(xml.root.name, 'osmChange')
162 t.equal(xml.root.children.length, 1)
163 t.equal(xml.root.children[0].name, 'create')
164 xml.root.children[0].children.sort(cmpch)
165 xml.root.children[0].children.forEach(function (c) {
166 c.children.sort(cmpref)
167 })
168 xml.root.children[0].children.forEach(function (c) {
169 t.true(isISODate(c.attributes.timestamp))
170 delete c.attributes.timestamp
171 })
172 t.deepEqual(xml.root.children[0].children, expected)
173 }))
174})
175
176test('close changeset', function (t) {
177 t.plan(2)
178 var href = base + 'changeset/' + changeId + '/close'
179 var hq = hyperquest.put(href)
180 hq.once('response', function (res) {
181 t.equal(res.statusCode, 200, 'create 200 ok')
182 })
183 hq.pipe(concat({ encoding: 'string' }, function (body) {
184 t.equal(body.trim(), '', 'empty response')
185 }))
186 hq.end()
187})
188
189test('close already closed changeset', function (t) {
190 t.plan(4)
191 var href = base + 'changeset/' + changeId + '/close'
192 var hq = hyperquest.put(href)
193 hq.once('response', function (res) {
194 t.equal(res.statusCode, 409, 'expected conflict code')
195 var contentObj = contentType.parse(res)
196 t.equal(contentObj.type, 'text/plain', 'media type correct')
197 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
198 })
199 hq.pipe(concat({ encoding: 'string' }, function (body) {
200 t.equal(
201 body.trim().replace(/closed at.*/, 'closed at'),
202 'The changeset ' + changeId + ' was closed at',
203 'already closed message'
204 )
205 }))
206 hq.end()
207})
208
209test('upload to closed changeset', function (t) {
210 t.plan(4)
211 var href = base + 'changeset/' + changeId + '/upload'
212 var hq = hyperquest.post(href, {
213 headers: { 'content-type': 'text/xml' }
214 })
215 hq.on('response', function (res) {
216 t.equal(res.statusCode, 409, 'expected conflict code')
217 var contentObj = contentType.parse(res)
218 t.equal(contentObj.type, 'text/plain', 'media type correct')
219 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
220 })
221 hq.pipe(concat({ encoding: 'string' }, function (body) {
222 t.equal(
223 body.trim().replace(/closed at.*/, 'closed at'),
224 'The changeset ' + changeId + ' was closed at',
225 'already closed message'
226 )
227 }))
228 hq.end(`<osmChange version="1.0" generator="acme osm editor">
229 <create>
230 <node id="-1" changeset="${changeId}" lat="64.5" lon="-121.5"/>
231 <node id="-2" changeset="${changeId}" lat="63.9" lon="-120.9"/>
232 <way id="-3" changeset="${changeId}">
233 <nd ref="-1"/>
234 <nd ref="-2"/>
235 </way>
236 </create>
237 </osmChange>`)
238})
239
240var secondChangeId
241test('create second changeset', function (t) {
242 t.plan(4)
243 var href = base + 'changeset/create'
244 var hq = hyperquest.put(href, {
245 headers: { 'content-type': 'text/xml' }
246 })
247 hq.once('response', function (res) {
248 t.equal(res.statusCode, 200, 'create 200 ok')
249 var contentObj = contentType.parse(res)
250 t.equal(contentObj.type, 'text/plain', 'media type correct')
251 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
252 })
253 hq.pipe(concat({ encoding: 'string' }, function (body) {
254 secondChangeId = body.trim()
255 t.ok(/^[0-9A-Fa-f]+$/.test(secondChangeId), 'expected changeset id response')
256 }))
257 hq.end(`<osm>
258 <changeset>
259 <tag k="comment" v="second"/>
260 </changeset>
261 </osm>`)
262})
263
264test('second changeset upload', function (t) {
265 t.plan(13)
266 var href = base + 'changeset/' + secondChangeId + '/upload'
267 var hq = hyperquest.post(href, {
268 headers: { 'content-type': 'text/xml' }
269 })
270 hq.on('response', function (res) {
271 t.equal(res.statusCode, 200)
272 var contentObj = contentType.parse(res)
273 t.equal(contentObj.type, 'text/xml', 'media type correct')
274 t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
275 })
276 var oldv, newv
277 hq.pipe(concat({ encoding: 'string' }, function (body) {
278 var xml = parsexml(body)
279 t.equal(xml.root.name, 'diffResult')
280 t.deepEqual(xml.root.children.map(function (c) {
281 return c.attributes.old_id
282 }).sort(), [ids['-1']])
283
284 oldv = versions['-1']
285 newv = xml.root.children[0].attributes.new_version
286 hyperquest.get(base + 'node/' + ids['-1'] + '/' + newv)
287 .on('response', function (res) {
288 t.equal(res.statusCode, 200)
289 })
290 .pipe(concat({ encoding: 'string' }, onnew))
291 hyperquest.get(base + 'node/' + ids['-1'] + '/' + oldv)
292 .on('response', function (res) {
293 t.equal(res.statusCode, 200)
294 })
295 .pipe(concat({ encoding: 'string' }, onold))
296 }))
297 hq.end(`<osmChange version="1.0" generator="acme osm editor">
298 <modify>
299 <node id="${ids['-1']}" changeset="${secondChangeId}" lat="111" lon="222"/>
300 </modify>
301 </osmChange>`)
302
303 function onnew (body) {
304 var xml = parsexml(body)
305 t.equal(xml.root.name, 'osm')
306 t.true(isISODate(xml.root.children[0].attributes.timestamp))
307 delete xml.root.children[0].attributes.timestamp
308 t.deepEqual(xml.root.children, [
309 {
310 name: 'node',
311 attributes: {
312 changeset: secondChangeId,
313 lat: '111',
314 lon: '222',
315 id: ids['-1'],
316 version: newv
317 },
318 children: []
319 }
320 ])
321 }
322 function onold (body) {
323 var xml = parsexml(body)
324 t.equal(xml.root.name, 'osm')
325 t.true(isISODate(xml.root.children[0].attributes.timestamp))
326 delete xml.root.children[0].attributes.timestamp
327 t.deepEqual(xml.root.children, [
328 {
329 name: 'node',
330 attributes: {
331 changeset: changeId,
332 lat: '64.5',
333 lon: '-121.5',
334 id: ids['-1'],
335 version: oldv
336 },
337 children: []
338 }
339 ])
340 }
341})
342
343test('changeset_upload_relation.js: teardown server', function (t) {
344 server.cleanup(function () {
345 t.end()
346 })
347})
348
349function cmpch (a, b) {
350 return a.attributes.id < b.attributes.id ? -1 : 1
351}
352
353function cmpref (a, b) {
354 if (a.name === 'tag' && b.name !== 'tag') {
355 return 1
356 }
357 if (a.name !== 'tag' && b.name === 'tag') {
358 return -1
359 }
360 if (a.name === 'tag' && b.name === 'tag') {
361 return a.attributes.k < b.attributes.tag ? -1 : 1
362 }
363 return a.attributes.ref < b.attributes.ref ? -1 : 1
364}