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 base
|
9 | var server
|
10 |
|
11 | var createServer = require('./lib/test_server.js')
|
12 |
|
13 | test('history.js: setup server', function (t) {
|
14 | createServer(function (d) {
|
15 | base = d.base
|
16 | server = d.server
|
17 | t.end()
|
18 | })
|
19 | })
|
20 |
|
21 | var ids = {}
|
22 | var versions = {}
|
23 | var changesets = []
|
24 | test('create history', function (t) {
|
25 | var updates = [
|
26 | [
|
27 | { type: 'node', lat: 64.5, lon: -121.5, id: 'A' },
|
28 | { type: 'node', lat: 63.9, lon: -120.9, id: 'B' }
|
29 | ],
|
30 | [
|
31 | { type: 'node', lat: 64.3, lon: -121.4, id: 'A' }
|
32 | ],
|
33 | [
|
34 | { type: 'node', lat: 64.2, lon: -121.4, id: 'A', tags: { beep: 'boop' } }
|
35 | ],
|
36 | [
|
37 | { type: 'node', lat: 63.9, lon: -120.8, id: 'B' }
|
38 | ]
|
39 | ]
|
40 | t.plan(7 * updates.length)
|
41 | var exists = {}
|
42 | var versionId = {}
|
43 | ;(function next () {
|
44 | if (updates.length === 0) return
|
45 | var update = updates.shift()
|
46 | var hq = hyperquest.put(base + 'changeset/create', {
|
47 | headers: { 'content-type': 'text/xml' }
|
48 | })
|
49 | hq.once('response', function (res) {
|
50 | t.equal(res.statusCode, 200, 'create 200 ok')
|
51 | var contentObj = contentType.parse(res)
|
52 | t.equal(contentObj.type, 'text/plain', 'media type correct')
|
53 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
54 | })
|
55 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
56 | var changeId = body.trim()
|
57 | t.ok(/^[0-9A-Fa-f]+$/.test(changeId), 'expected changeset id response')
|
58 | changesets.push(changeId)
|
59 | upload(changeId, update, next)
|
60 | }))
|
61 | hq.end('<osm><changeset></changeset></osm>')
|
62 | })()
|
63 |
|
64 | function upload (changeId, update, next) {
|
65 | var hq = hyperquest.post(base + 'changeset/' + changeId + '/upload', {
|
66 | headers: { 'content-type': 'text/xml' }
|
67 | })
|
68 | hq.once('response', function (res) {
|
69 | t.equal(res.statusCode, 200, 'create 200 ok')
|
70 | t.equal(res.headers['content-type'], 'text/xml; charset=utf-8', 'upload content type')
|
71 | })
|
72 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
73 | var xml = parsexml(body)
|
74 | t.equal(xml.root.name, 'diffResult')
|
75 | xml.root.children.forEach(function (c) {
|
76 | var key
|
77 | if (/^-\d+/.test(c.attributes.old_id)) {
|
78 | key = update[-1 - Number(c.attributes.old_id)].id
|
79 | ids[key] = c.attributes.new_id
|
80 | versionId[c.attributes.new_id] = key
|
81 | } else {
|
82 | key = versionId[c.attributes.old_id]
|
83 | versionId[c.attributes.new_id] = key
|
84 | }
|
85 | if (!versions[key]) versions[key] = []
|
86 | versions[key].push(c.attributes.new_version)
|
87 | })
|
88 | next()
|
89 | }))
|
90 | hq.end(`<osmChange version="1.0" generator="acme osm editor">
|
91 | <create>
|
92 | ${update.filter(notExistFilter).map(createMap)}
|
93 | </create>
|
94 | <modify>
|
95 | ${update.filter(existFilter).map(modifyMap)}
|
96 | </modify>
|
97 | </osmChange>`)
|
98 | update.forEach(function (doc) { exists[doc.id] = true })
|
99 |
|
100 | function createMap (doc, i) {
|
101 | return `<node id="-${i + 1}"
|
102 | lat="${doc.lat}"
|
103 | lon="${doc.lon}"
|
104 | changeset="${changeId}"
|
105 | >${Object.keys(doc.tags || {}).map(function (key) {
|
106 | return `<tag k="${key}" v="${doc.tags[key]}"/>`
|
107 | }).join('')}
|
108 | </node>`
|
109 | }
|
110 | function modifyMap (doc) {
|
111 | return `<node id="${ids[doc.id]}"
|
112 | lat="${doc.lat}" lon="${doc.lon}"
|
113 | changeset="${changeId}"
|
114 | >${Object.keys(doc.tags || {}).map(function (key) {
|
115 | return `<tag k="${key}" v="${doc.tags[key]}"/>`
|
116 | }).join('')}
|
117 | </node>`
|
118 | }
|
119 | function existFilter (doc) { return exists[doc.id] }
|
120 | function notExistFilter (doc) { return !exists[doc.id] }
|
121 | }
|
122 | })
|
123 |
|
124 | test('history route', function (t) {
|
125 | t.plan(8)
|
126 | var expected = [
|
127 | {
|
128 | name: 'node',
|
129 | attributes: {
|
130 | changeset: changesets[2],
|
131 | id: ids.A,
|
132 | lat: '64.2',
|
133 | lon: '-121.4',
|
134 | version: versions.A[2]
|
135 | },
|
136 | children: [
|
137 | {
|
138 | name: 'tag',
|
139 | attributes: {
|
140 | k: 'beep',
|
141 | v: 'boop'
|
142 | },
|
143 | children: []
|
144 | }
|
145 | ],
|
146 | content: ''
|
147 | },
|
148 | {
|
149 | name: 'node',
|
150 | attributes: {
|
151 | changeset: changesets[1],
|
152 | id: ids.A,
|
153 | lat: '64.3',
|
154 | lon: '-121.4',
|
155 | version: versions.A[1]
|
156 | },
|
157 | children: []
|
158 | },
|
159 | {
|
160 | name: 'node',
|
161 | attributes: {
|
162 | changeset: changesets[0],
|
163 | id: ids.A,
|
164 | lat: '64.5',
|
165 | lon: '-121.5',
|
166 | version: versions.A[0]
|
167 | },
|
168 | children: []
|
169 | }
|
170 | ]
|
171 | var hq = hyperquest(base + 'node/' + ids.A + '/history', {
|
172 | headers: { 'content-type': 'text/xml' }
|
173 | })
|
174 | hq.on('response', function (res) {
|
175 | t.equal(res.statusCode, 200)
|
176 | var contentObj = contentType.parse(res)
|
177 | t.equal(contentObj.type, 'text/xml', 'media type correct')
|
178 | t.equal(contentObj.parameters.charset.toLowerCase(), 'utf-8', 'charset correct')
|
179 | })
|
180 | hq.pipe(concat({ encoding: 'string' }, function (body) {
|
181 | var xml = parsexml(body)
|
182 | t.equal(xml.root.name, 'osm')
|
183 | xml.root.children.forEach(function (c) {
|
184 | t.true(isISODate(c.attributes.timestamp))
|
185 | delete c.attributes.timestamp
|
186 | })
|
187 | t.deepEqual(xml.root.children, expected)
|
188 | }))
|
189 | })
|
190 |
|
191 | test('history.js: teardown server', function (t) {
|
192 | server.cleanup(function () {
|
193 | t.end()
|
194 | })
|
195 | })
|