1 | var test = require('tape')
|
2 | var contentType = require('content-type')
|
3 | var parsexml = require('xml-parser')
|
4 | var hyperquest = require('hyperquest')
|
5 | var concat = require('concat-stream')
|
6 | var isISODate = require('isostring')
|
7 |
|
8 | var createServer = require('./lib/test_server.js')
|
9 |
|
10 | var base, server, changeId
|
11 |
|
12 | test('changeset_placeholder.js: setup server', function (t) {
|
13 | createServer(function (d) {
|
14 | base = d.base
|
15 | server = d.server
|
16 | t.end()
|
17 | })
|
18 | })
|
19 |
|
20 | test('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 |
|
43 | var ids = {}
|
44 | var versions = {}
|
45 | test('add docs to changeset upload', function (t) {
|
46 | t.plan(8)
|
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'])
|
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 old 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 | </create>
|
80 | </osmChange>`)
|
81 | })
|
82 |
|
83 | test('get osmchange doc from upload', function (t) {
|
84 | t.plan(7)
|
85 | var expected = [
|
86 | {
|
87 | name: 'node',
|
88 | attributes: {
|
89 | changeset: changeId,
|
90 | id: ids['1'],
|
91 | version: versions['1'],
|
92 | lat: '64.5',
|
93 | lon: '-121.5'
|
94 | },
|
95 | children: []
|
96 | },
|
97 | {
|
98 | name: 'node',
|
99 | attributes: {
|
100 | changeset: changeId,
|
101 | id: ids['2'],
|
102 | version: versions['2'],
|
103 | lat: '63.9',
|
104 | lon: '-120.9'
|
105 | },
|
106 | children: []
|
107 | },
|
108 | {
|
109 | name: 'way',
|
110 | attributes: {
|
111 | changeset: changeId,
|
112 | id: ids['3'],
|
113 | version: versions['3']
|
114 | },
|
115 | content: '',
|
116 | children: [
|
117 | {
|
118 | name: 'nd',
|
119 | attributes: { ref: ids['2'] },
|
120 | children: []
|
121 | },
|
122 | {
|
123 | name: 'nd',
|
124 | attributes: { ref: ids['1'] },
|
125 | children: []
|
126 | }
|
127 | ].sort(cmpref)
|
128 | }
|
129 | ].sort(cmpch)
|
130 | var href = base + 'changeset/' + changeId + '/download'
|
131 | var hq = hyperquest(href, {
|
132 | headers: { 'content-type': 'text/xml' }
|
133 | })
|
134 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
135 | var xml = parsexml(body)
|
136 | t.equal(xml.root.name, 'osmChange')
|
137 | t.equal(xml.root.children.length, 1)
|
138 | t.equal(xml.root.children[0].name, 'create')
|
139 | xml.root.children[0].children.sort(cmpch)
|
140 | xml.root.children[0].children.forEach(function (c) {
|
141 | c.children.sort(cmpref)
|
142 | })
|
143 | xml.root.children[0].children.forEach(function (c) {
|
144 | t.true(isISODate(c.attributes.timestamp))
|
145 | delete c.attributes.timestamp
|
146 | })
|
147 | t.deepEqual(xml.root.children[0].children, expected)
|
148 | }))
|
149 | })
|
150 |
|
151 | test('close changeset', function (t) {
|
152 | t.plan(2)
|
153 | var href = base + 'changeset/' + changeId + '/close'
|
154 | var hq = hyperquest.put(href)
|
155 | hq.once('response', function (res) {
|
156 | t.equal(res.statusCode, 200, 'create 200 ok')
|
157 | })
|
158 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
159 | t.equal(body.trim(), '', 'empty response')
|
160 | }))
|
161 | hq.end()
|
162 | })
|
163 |
|
164 | test('close already closed changeset', function (t) {
|
165 | t.plan(4)
|
166 | var href = base + 'changeset/' + changeId + '/close'
|
167 | var hq = hyperquest.put(href)
|
168 | hq.once('response', function (res) {
|
169 | t.equal(res.statusCode, 409, 'expected conflict code')
|
170 | var contentObj = contentType.parse(res)
|
171 | t.equal(contentObj.type, 'text/plain', 'media type correct')
|
172 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
173 | })
|
174 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
175 | t.equal(
|
176 | body.trim().replace(/closed at.*/, 'closed at'),
|
177 | 'The changeset ' + changeId + ' was closed at',
|
178 | 'already closed message'
|
179 | )
|
180 | }))
|
181 | hq.end()
|
182 | })
|
183 |
|
184 | test('upload to closed changeset', function (t) {
|
185 | t.plan(4)
|
186 | var href = base + 'changeset/' + changeId + '/upload'
|
187 | var hq = hyperquest.post(href, {
|
188 | headers: { 'content-type': 'text/xml' }
|
189 | })
|
190 | hq.on('response', function (res) {
|
191 | t.equal(res.statusCode, 409, 'expected conflict code')
|
192 | var contentObj = contentType.parse(res)
|
193 | t.equal(contentObj.type, 'text/plain', 'media type correct')
|
194 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
195 | })
|
196 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
197 | t.equal(
|
198 | body.trim().replace(/closed at.*/, 'closed at'),
|
199 | 'The changeset ' + changeId + ' was closed at',
|
200 | 'already closed message'
|
201 | )
|
202 | }))
|
203 | hq.end(`<osmChange version="1.0" generator="acme osm editor">
|
204 | <create>
|
205 | <node id="1" changeset="${changeId}" lat="64.5" lon="-121.5"/>
|
206 | <node id="2" changeset="${changeId}" lat="63.9" lon="-120.9"/>
|
207 | <way id="3" changeset="${changeId}">
|
208 | <nd ref="1"/>
|
209 | <nd ref="2"/>
|
210 | </way>
|
211 | </create>
|
212 | </osmChange>`)
|
213 | })
|
214 |
|
215 | var secondChangeId
|
216 | test('create second changeset', function (t) {
|
217 | t.plan(4)
|
218 | var href = base + 'changeset/create'
|
219 | var hq = hyperquest.put(href, {
|
220 | headers: { 'content-type': 'text/xml' }
|
221 | })
|
222 | hq.once('response', function (res) {
|
223 | t.equal(res.statusCode, 200, 'create 200 ok')
|
224 | var contentObj = contentType.parse(res)
|
225 | t.equal(contentObj.type, 'text/plain', 'media type correct')
|
226 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
227 | })
|
228 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
229 | secondChangeId = body.trim()
|
230 | t.ok(/^[0-9A-Fa-f]+$/.test(secondChangeId), 'expected changeset id response')
|
231 | }))
|
232 | hq.end(`<osm>
|
233 | <changeset>
|
234 | <tag k="comment" v="second"/>
|
235 | </changeset>
|
236 | </osm>`)
|
237 | })
|
238 |
|
239 | test('second changeset upload', function (t) {
|
240 | t.plan(13)
|
241 | var href = base + 'changeset/' + secondChangeId + '/upload'
|
242 | var hq = hyperquest.post(href, {
|
243 | headers: { 'content-type': 'text/xml' }
|
244 | })
|
245 | hq.on('response', function (res) {
|
246 | t.equal(res.statusCode, 200)
|
247 | var contentObj = contentType.parse(res)
|
248 | t.equal(contentObj.type, 'text/xml', 'media type correct')
|
249 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
250 | })
|
251 | var oldv, newv
|
252 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
253 | var xml = parsexml(body)
|
254 | t.equal(xml.root.name, 'diffResult')
|
255 | t.deepEqual(xml.root.children.map(function (c) {
|
256 | return c.attributes.old_id
|
257 | }).sort(), [ids['1']])
|
258 |
|
259 | oldv = versions['1']
|
260 | newv = xml.root.children[0].attributes.new_version
|
261 | hyperquest.get(base + 'node/' + ids['1'] + '/' + newv)
|
262 | .on('response', function (res) {
|
263 | t.equal(res.statusCode, 200)
|
264 | })
|
265 | .pipe(concat({ encoding: 'string' }, onnew))
|
266 | hyperquest.get(base + 'node/' + ids['1'] + '/' + oldv)
|
267 | .on('response', function (res) {
|
268 | t.equal(res.statusCode, 200)
|
269 | })
|
270 | .pipe(concat({ encoding: 'string' }, onold))
|
271 | }))
|
272 | hq.end(`<osmChange version="1.0" generator="acme osm editor">
|
273 | <modify>
|
274 | <node id="${ids['1']}" changeset="${secondChangeId}" lat="111" lon="222"/>
|
275 | </modify>
|
276 | </osmChange>`)
|
277 |
|
278 | function onnew (body) {
|
279 | var xml = parsexml(body)
|
280 | t.equal(xml.root.name, 'osm')
|
281 | t.true(isISODate(xml.root.children[0].attributes.timestamp))
|
282 | delete xml.root.children[0].attributes.timestamp
|
283 | t.deepEqual(xml.root.children, [
|
284 | {
|
285 | name: 'node',
|
286 | attributes: {
|
287 | changeset: secondChangeId,
|
288 | lat: '111',
|
289 | lon: '222',
|
290 | id: ids['1'],
|
291 | version: newv
|
292 | },
|
293 | children: []
|
294 | }
|
295 | ])
|
296 | }
|
297 | function onold (body) {
|
298 | var xml = parsexml(body)
|
299 | t.equal(xml.root.name, 'osm')
|
300 | t.true(isISODate(xml.root.children[0].attributes.timestamp))
|
301 | delete xml.root.children[0].attributes.timestamp
|
302 | t.deepEqual(xml.root.children, [
|
303 | {
|
304 | name: 'node',
|
305 | attributes: {
|
306 | changeset: changeId,
|
307 | lat: '64.5',
|
308 | lon: '-121.5',
|
309 | id: ids['1'],
|
310 | version: oldv
|
311 | },
|
312 | children: []
|
313 | }
|
314 | ])
|
315 | }
|
316 | })
|
317 |
|
318 | test('changeset_placeholder.js: setup server', function (t) {
|
319 | server.cleanup(function () {
|
320 | t.end()
|
321 | })
|
322 | })
|
323 |
|
324 | function cmpch (a, b) {
|
325 | return a.attributes.id < b.attributes.id ? -1 : 1
|
326 | }
|
327 |
|
328 | function cmpref (a, b) {
|
329 | return a.attributes.ref < b.attributes.ref ? -1 : 1
|
330 | }
|