Merge remote-tracking branch 'upstream/pull/2134'
[rails.git] / test / controllers / amf_controller_test.rb
1 require "test_helper"
2
3 class AmfControllerTest < ActionController::TestCase
4   include Potlatch
5
6   ##
7   # test all routes which lead to this controller
8   def test_routes
9     assert_routing(
10       { :path => "/api/0.6/amf/read", :method => :post },
11       { :controller => "amf", :action => "amf_read" }
12     )
13     assert_routing(
14       { :path => "/api/0.6/amf/write", :method => :post },
15       { :controller => "amf", :action => "amf_write" }
16     )
17   end
18
19   def test_getpresets
20     user_en_de = create(:user, :languages => %w[en de])
21     user_de = create(:user, :languages => %w[de])
22     [user_en_de, user_de].each do |user|
23       post :amf_read, :body => amf_content("getpresets", "/1", ["#{user.email}:test", ""])
24       assert_response :success
25       amf_parse_response
26       presets = amf_result("/1")
27
28       assert_equal 15, presets.length
29       assert_equal POTLATCH_PRESETS[0], presets[0]
30       assert_equal POTLATCH_PRESETS[1], presets[1]
31       assert_equal POTLATCH_PRESETS[2], presets[2]
32       assert_equal POTLATCH_PRESETS[3], presets[3]
33       assert_equal POTLATCH_PRESETS[4], presets[4]
34       assert_equal POTLATCH_PRESETS[5], presets[5]
35       assert_equal POTLATCH_PRESETS[6], presets[6]
36       assert_equal POTLATCH_PRESETS[7], presets[7]
37       assert_equal POTLATCH_PRESETS[8], presets[8]
38       assert_equal POTLATCH_PRESETS[9], presets[9]
39       assert_equal POTLATCH_PRESETS[10], presets[10]
40       assert_equal POTLATCH_PRESETS[12], presets[12]
41       assert_equal user.languages.first, presets[13]["__potlatch_locale"]
42     end
43   end
44
45   def test_getway
46     # check a visible way
47     way = create(:way_with_nodes, :nodes_count => 1)
48     node = way.nodes.first
49     user = way.changeset.user
50
51     post :amf_read, :body => amf_content("getway", "/1", [way.id])
52     assert_response :success
53     amf_parse_response
54     result = amf_result("/1")
55     assert_equal 0, result[0]
56     assert_equal "", result[1]
57     assert_equal way.id, result[2]
58     assert_equal 1, result[3].length
59     assert_equal node.id, result[3][0][2]
60     assert_equal way.version, result[5]
61     assert_equal user.id, result[6]
62   end
63
64   def test_getway_invisible
65     # check an invisible way
66     id = create(:way, :deleted).id
67
68     post :amf_read, :body => amf_content("getway", "/1", [id])
69     assert_response :success
70     amf_parse_response
71     result = amf_result("/1")
72     assert_equal(-4, result[0])
73     assert_equal "way", result[1]
74     assert_equal id, result[2]
75     assert(result[3].nil? && result[4].nil? && result[5].nil? && result[6].nil?)
76   end
77
78   def test_getway_with_versions
79     # check a way with multiple versions
80     way = create(:way, :with_history, :version => 4)
81     create(:way_node, :way => way)
82     node = way.nodes.first
83     user = way.changeset.user
84
85     post :amf_read, :body => amf_content("getway", "/1", [way.id])
86     assert_response :success
87     amf_parse_response
88     result = amf_result("/1")
89     assert_equal 0, result[0]
90     assert_equal "", result[1]
91     assert_equal way.id, result[2]
92     assert_equal 1, result[3].length
93     assert_equal node.id, result[3][0][2]
94     assert_equal way.version, result[5]
95     assert_equal user.id, result[6]
96   end
97
98   def test_getway_with_duplicate_nodes
99     # check a way with duplicate nodes
100     way = create(:way)
101     node = create(:node)
102     create(:way_node, :way => way, :node => node, :sequence_id => 1)
103     create(:way_node, :way => way, :node => node, :sequence_id => 2)
104     user = way.changeset.user
105
106     post :amf_read, :body => amf_content("getway", "/1", [way.id])
107     assert_response :success
108     amf_parse_response
109     result = amf_result("/1")
110     assert_equal 0, result[0]
111     assert_equal "", result[1]
112     assert_equal way.id, result[2]
113     assert_equal 2, result[3].length
114     assert_equal node.id, result[3][0][2]
115     assert_equal node.id, result[3][1][2]
116     assert_equal way.version, result[5]
117     assert_equal user.id, result[6]
118   end
119
120   def test_getway_with_multiple_nodes
121     # check a way with multiple nodes
122     way = create(:way_with_nodes, :nodes_count => 3)
123     a = way.nodes[0].id
124     b = way.nodes[1].id
125     c = way.nodes[2].id
126     user = way.changeset.user
127
128     post :amf_read, :body => amf_content("getway", "/1", [way.id])
129     assert_response :success
130     amf_parse_response
131     result = amf_result("/1")
132     assert_equal 0, result[0]
133     assert_equal "", result[1]
134     assert_equal way.id, result[2]
135     assert_equal 3, result[3].length
136     assert_equal a, result[3][0][2]
137     assert_equal b, result[3][1][2]
138     assert_equal c, result[3][2][2]
139     assert_equal way.version, result[5]
140     assert_equal user.id, result[6]
141   end
142
143   def test_getway_nonexistent
144     # check chat a non-existent way is not returned
145     post :amf_read, :body => amf_content("getway", "/1", [0])
146     assert_response :success
147     amf_parse_response
148     way = amf_result("/1")
149     assert_equal(-4, way[0])
150     assert_equal "way", way[1]
151     assert_equal 0, way[2]
152     assert(way[3].nil?) && way[4].nil? && way[5].nil? && way[6].nil?
153   end
154
155   def test_whichways
156     node = create(:node, :lat => 3.0, :lon => 3.0)
157     way = create(:way)
158     deleted_way = create(:way, :deleted)
159     create(:way_node, :way => way, :node => node)
160     create(:way_node, :way => deleted_way, :node => node)
161     create(:way_tag, :way => way)
162
163     minlon = node.lon - 0.1
164     minlat = node.lat - 0.1
165     maxlon = node.lon + 0.1
166     maxlat = node.lat + 0.1
167     post :amf_read, :body => amf_content("whichways", "/1", [minlon, minlat, maxlon, maxlat])
168     assert_response :success
169     amf_parse_response
170
171     # check contents of message
172     map = amf_result "/1"
173     assert_equal 0, map[0], "map error code should be 0"
174     assert_equal "", map[1], "map error text should be empty"
175
176     # check the formatting of the message
177     assert_equal 5, map.length, "map should have length 5"
178     assert_equal Array, map[2].class, 'map "ways" element should be an array'
179     assert_equal Array, map[3].class, 'map "nodes" element should be an array'
180     assert_equal Array, map[4].class, 'map "relations" element should be an array'
181     map[2].each do |w|
182       assert_equal 2, w.length, "way should be (id, version) pair"
183       assert w[0] == w[0].floor, "way ID should be an integer"
184       assert w[1] == w[1].floor, "way version should be an integer"
185     end
186
187     map[3].each do |n|
188       assert_equal 5, w.length, "node should be (id, lat, lon, [tags], version) tuple"
189       assert n[0] == n[0].floor, "node ID should be an integer"
190       assert n[1] >= minlat - 0.01, "node lat should be greater than min"
191       assert n[1] <= maxlat - 0.01, "node lat should be less than max"
192       assert n[2] >= minlon - 0.01, "node lon should be greater than min"
193       assert n[2] <= maxlon - 0.01, "node lon should be less than max"
194       assert_equal Array, a[3].class, "node tags should be array"
195       assert n[4] == n[4].floor, "node version should be an integer"
196     end
197
198     map[4].each do |r|
199       assert_equal 2, r.length, "relation should be (id, version) pair"
200       assert r[0] == r[0].floor, "relation ID should be an integer"
201       assert r[1] == r[1].floor, "relation version should be an integer"
202     end
203
204     # TODO: looks like amf_controller changed since this test was written
205     # so someone who knows what they're doing should check this!
206     ways = map[2].collect { |x| x[0] }
207     assert ways.include?(way.id),
208            "map should include used way"
209     assert_not ways.include?(deleted_way.id),
210                "map should not include deleted way"
211   end
212
213   ##
214   # checks that too-large a bounding box will not be served.
215   def test_whichways_toobig
216     bbox = [-0.1, -0.1, 1.1, 1.1]
217     check_bboxes_are_bad [bbox] do |map, _bbox|
218       assert_boundary_error map, " The server said: The maximum bbox size is 0.25, and your request was too large. Either request a smaller area, or use planet.osm"
219     end
220   end
221
222   ##
223   # checks that an invalid bounding box will not be served. in this case
224   # one with max < min latitudes.
225   #
226   # NOTE: the controller expands the bbox by 0.01 in each direction!
227   def test_whichways_badlat
228     bboxes = [[0, 0.1, 0.1, 0], [-0.1, 80, 0.1, 70], [0.24, 54.35, 0.25, 54.33]]
229     check_bboxes_are_bad bboxes do |map, bbox|
230       assert_boundary_error map, " The server said: The minimum latitude must be less than the maximum latitude, but it wasn't", bbox.inspect
231     end
232   end
233
234   ##
235   # same as test_whichways_badlat, but for longitudes
236   #
237   # NOTE: the controller expands the bbox by 0.01 in each direction!
238   def test_whichways_badlon
239     bboxes = [[80, -0.1, 70, 0.1], [54.35, 0.24, 54.33, 0.25]]
240     check_bboxes_are_bad bboxes do |map, bbox|
241       assert_boundary_error map, " The server said: The minimum longitude must be less than the maximum longitude, but it wasn't", bbox.inspect
242     end
243   end
244
245   def test_whichways_deleted
246     node = create(:node, :with_history, :lat => 24.0, :lon => 24.0)
247     way = create(:way, :with_history)
248     way_v1 = way.old_ways.find_by(:version => 1)
249     deleted_way = create(:way, :with_history, :deleted)
250     deleted_way_v1 = deleted_way.old_ways.find_by(:version => 1)
251     create(:way_node, :way => way, :node => node)
252     create(:way_node, :way => deleted_way, :node => node)
253     create(:old_way_node, :old_way => way_v1, :node => node)
254     create(:old_way_node, :old_way => deleted_way_v1, :node => node)
255
256     minlon = node.lon - 0.1
257     minlat = node.lat - 0.1
258     maxlon = node.lon + 0.1
259     maxlat = node.lat + 0.1
260     post :amf_read, :body => amf_content("whichways_deleted", "/1", [minlon, minlat, maxlon, maxlat])
261     assert_response :success
262     amf_parse_response
263
264     # check contents of message
265     map = amf_result "/1"
266     assert_equal 0, map[0], "first map element should be 0"
267     assert_equal "", map[1], "second map element should be an empty string"
268     assert_equal Array, map[2].class, "third map element should be an array"
269     # TODO: looks like amf_controller changed since this test was written
270     # so someone who knows what they're doing should check this!
271     assert_not map[2].include?(way.id),
272                "map should not include visible way"
273     assert map[2].include?(deleted_way.id),
274            "map should include deleted way"
275   end
276
277   def test_whichways_deleted_toobig
278     bbox = [-0.1, -0.1, 1.1, 1.1]
279     post :amf_read, :body => amf_content("whichways_deleted", "/1", bbox)
280     assert_response :success
281     amf_parse_response
282
283     map = amf_result "/1"
284     assert_deleted_boundary_error map, " The server said: The maximum bbox size is 0.25, and your request was too large. Either request a smaller area, or use planet.osm"
285   end
286
287   def test_getrelation
288     id = create(:relation).id
289     post :amf_read, :body => amf_content("getrelation", "/1", [id])
290     assert_response :success
291     amf_parse_response
292     rel = amf_result("/1")
293     assert_equal rel[0], 0
294     assert_equal rel[2], id
295   end
296
297   def test_getrelation_invisible
298     id = create(:relation, :deleted).id
299     post :amf_read, :body => amf_content("getrelation", "/1", [id])
300     assert_response :success
301     amf_parse_response
302     rel = amf_result("/1")
303     assert_equal rel[0], -4
304     assert_equal rel[1], "relation"
305     assert_equal rel[2], id
306     assert(rel[3].nil?) && rel[4].nil?
307   end
308
309   def test_getrelation_nonexistent
310     id = 0
311     post :amf_read, :body => amf_content("getrelation", "/1", [id])
312     assert_response :success
313     amf_parse_response
314     rel = amf_result("/1")
315     assert_equal rel[0], -4
316     assert_equal rel[1], "relation"
317     assert_equal rel[2], id
318     assert(rel[3].nil?) && rel[4].nil?
319   end
320
321   def test_getway_old
322     latest = create(:way, :version => 2)
323     v1 = create(:old_way, :current_way => latest, :version => 1, :timestamp => Time.now.utc - 2.minutes)
324     _v2 = create(:old_way, :current_way => latest, :version => 2, :timestamp => Time.now.utc - 1.minute)
325
326     # try to get the last visible version (specified by <0) (should be current version)
327     # NOTE: looks from the API changes that this now expects a timestamp
328     # instead of a version number...
329     # try to get version 1
330     { latest.id => "",
331       v1.way_id => (v1.timestamp + 1).strftime("%d %b %Y, %H:%M:%S") }.each do |id, t|
332       post :amf_read, :body => amf_content("getway_old", "/1", [id, t])
333       assert_response :success
334       amf_parse_response
335       returned_way = amf_result("/1")
336       assert_equal 0, returned_way[0]
337       assert_equal id, returned_way[2]
338       # API returns the *latest* version, even for old ways...
339       assert_equal latest.version, returned_way[5]
340     end
341   end
342
343   ##
344   # test that the server doesn't fall over when rubbish is passed
345   # into the method args.
346   def test_getway_old_invalid
347     way_id = create(:way, :with_history, :version => 2).id
348     { "foo" => "bar",
349       way_id => "not a date",
350       way_id => "2009-03-25 00:00:00",                   # <- wrong format
351       way_id => "0 Jan 2009 00:00:00",                   # <- invalid date
352       -1 => "1 Jan 2009 00:00:00" }.each do |id, t| # <- invalid
353       post :amf_read, :body => amf_content("getway_old", "/1", [id, t])
354       assert_response :success
355       amf_parse_response
356       returned_way = amf_result("/1")
357       assert_equal(-1, returned_way[0])
358       assert returned_way[3].nil?
359       assert returned_way[4].nil?
360       assert returned_way[5].nil?
361     end
362   end
363
364   def test_getway_old_nonexistent
365     # try to get the last version-10 (shoudn't exist)
366     way = create(:way, :with_history, :version => 2)
367     v1 = way.old_ways.find_by(:version => 1)
368     # try to get last visible version of non-existent way
369     # try to get specific version of non-existent way
370     [[0, ""],
371      [0, "1 Jan 1970, 00:00:00"],
372      [v1.way_id, (v1.timestamp - 10).strftime("%d %b %Y, %H:%M:%S")]].each do |id, t|
373       post :amf_read, :body => amf_content("getway_old", "/1", [id, t])
374       assert_response :success
375       amf_parse_response
376       returned_way = amf_result("/1")
377       assert_equal(-1, returned_way[0])
378       assert returned_way[3].nil?
379       assert returned_way[4].nil?
380       assert returned_way[5].nil?
381     end
382   end
383
384   def test_getway_old_invisible
385     way = create(:way, :deleted, :with_history, :version => 1)
386     v1 = way.old_ways.find_by(:version => 1)
387     # try to get deleted version
388     [[v1.way_id, (v1.timestamp + 10).strftime("%d %b %Y, %H:%M:%S")]].each do |id, t|
389       post :amf_read, :body => amf_content("getway_old", "/1", [id, t])
390       assert_response :success
391       amf_parse_response
392       returned_way = amf_result("/1")
393       assert_equal(-1, returned_way[0])
394       assert returned_way[3].nil?
395       assert returned_way[4].nil?
396       assert returned_way[5].nil?
397     end
398   end
399
400   def test_getway_history
401     latest = create(:way, :version => 2)
402     oldest = create(:old_way, :current_way => latest, :version => 1, :timestamp => latest.timestamp - 2.minutes)
403     create(:old_way, :current_way => latest, :version => 2, :timestamp => latest.timestamp)
404
405     post :amf_read, :body => amf_content("getway_history", "/1", [latest.id])
406     assert_response :success
407     amf_parse_response
408     history = amf_result("/1")
409
410     # ['way',wayid,history]
411     assert_equal "way", history[0]
412     assert_equal latest.id, history[1]
413     # We use dates rather than version numbers here, because you might
414     # have moved a node within a way (i.e. way version not incremented).
415     # The timestamp is +1 because we say "give me the revision of 15:33:02",
416     # but that might actually include changes at 15:33:02.457.
417     assert_equal (latest.timestamp + 1).strftime("%d %b %Y, %H:%M:%S"), history[2].first[0]
418     assert_equal (oldest.timestamp + 1).strftime("%d %b %Y, %H:%M:%S"), history[2].last[0]
419   end
420
421   def test_getway_history_nonexistent
422     post :amf_read, :body => amf_content("getway_history", "/1", [0])
423     assert_response :success
424     amf_parse_response
425     history = amf_result("/1")
426
427     # ['way',wayid,history]
428     assert_equal history[0], "way"
429     assert_equal history[1], 0
430     assert history[2].empty?
431   end
432
433   def test_getnode_history
434     node = create(:node, :version => 2)
435     node_v1 = create(:old_node, :current_node => node, :version => 1, :timestamp => 3.days.ago)
436     _node_v2 = create(:old_node, :current_node => node, :version => 2, :timestamp => 2.days.ago)
437     node_v3 = create(:old_node, :current_node => node, :version => 3, :timestamp => 1.day.ago)
438
439     post :amf_read, :body => amf_content("getnode_history", "/1", [node.id])
440     assert_response :success
441     amf_parse_response
442     history = amf_result("/1")
443
444     # ['node',nodeid,history]
445     # note that (as per getway_history) we actually round up
446     # to the next second
447     assert_equal history[0], "node",
448                  'first element should be "node"'
449     assert_equal history[1], node.id,
450                  "second element should be the input node ID"
451     assert_equal history[2].first[0],
452                  (node_v3.timestamp + 1).strftime("%d %b %Y, %H:%M:%S"),
453                  "first element in third element (array) should be the latest version"
454     assert_equal history[2].last[0],
455                  (node_v1.timestamp + 1).strftime("%d %b %Y, %H:%M:%S"),
456                  "last element in third element (array) should be the initial version"
457   end
458
459   def test_getnode_history_nonexistent
460     post :amf_read, :body => amf_content("getnode_history", "/1", [0])
461     assert_response :success
462     amf_parse_response
463     history = amf_result("/1")
464
465     # ['node',nodeid,history]
466     assert_equal history[0], "node"
467     assert_equal history[1], 0
468     assert history[2].empty?
469   end
470
471   def test_findgpx_bad_user
472     post :amf_read, :body => amf_content("findgpx", "/1", [1, "test@example.com:wrong"])
473     assert_response :success
474     amf_parse_response
475     result = amf_result("/1")
476
477     assert_equal 2, result.length
478     assert_equal(-1, result[0])
479     assert_match(/must be logged in/, result[1])
480
481     blocked_user = create(:user)
482     create(:user_block, :user => blocked_user)
483     post :amf_read, :body => amf_content("findgpx", "/1", [1, "#{blocked_user.email}:test"])
484     assert_response :success
485     amf_parse_response
486     result = amf_result("/1")
487
488     assert_equal 2, result.length
489     assert_equal(-1, result[0])
490     assert_match(/access to the API has been blocked/, result[1])
491   end
492
493   def test_findgpx_by_id
494     user = create(:user)
495     trace = create(:trace, :visibility => "private", :user => user)
496
497     post :amf_read, :body => amf_content("findgpx", "/1", [trace.id, "#{user.email}:test"])
498     assert_response :success
499     amf_parse_response
500     result = amf_result("/1")
501
502     assert_equal 3, result.length
503     assert_equal 0, result[0]
504     assert_equal "", result[1]
505     traces = result[2]
506     assert_equal 1, traces.length
507     assert_equal 3, traces[0].length
508     assert_equal trace.id, traces[0][0]
509     assert_equal trace.name, traces[0][1]
510     assert_equal trace.description, traces[0][2]
511   end
512
513   def test_findgpx_by_name
514     user = create(:user)
515
516     post :amf_read, :body => amf_content("findgpx", "/1", ["Trace", "#{user.email}:test"])
517     assert_response :success
518     amf_parse_response
519     result = amf_result("/1")
520
521     # find by name fails as it uses mysql text search syntax...
522     assert_equal 2, result.length
523     assert_equal(-2, result[0])
524   end
525
526   def test_findrelations_by_id
527     relation = create(:relation, :version => 4)
528
529     post :amf_read, :body => amf_content("findrelations", "/1", [relation.id])
530     assert_response :success
531     amf_parse_response
532     result = amf_result("/1")
533
534     assert_equal 1, result.length
535     assert_equal 4, result[0].length
536     assert_equal relation.id, result[0][0]
537     assert_equal relation.tags, result[0][1]
538     assert_equal relation.members, result[0][2]
539     assert_equal relation.version, result[0][3]
540
541     post :amf_read, :body => amf_content("findrelations", "/1", [999999])
542     assert_response :success
543     amf_parse_response
544     result = amf_result("/1")
545
546     assert_equal 0, result.length
547   end
548
549   def test_findrelations_by_tags
550     visible_relation = create(:relation)
551     create(:relation_tag, :relation => visible_relation, :k => "test", :v => "yes")
552     used_relation = create(:relation)
553     super_relation = create(:relation)
554     create(:relation_member, :relation => super_relation, :member => used_relation)
555     create(:relation_tag, :relation => used_relation, :k => "test", :v => "yes")
556     create(:relation_tag, :relation => used_relation, :k => "name", :v => "Test Relation")
557
558     post :amf_read, :body => amf_content("findrelations", "/1", ["yes"])
559     assert_response :success
560     amf_parse_response
561     result = amf_result("/1").sort
562
563     assert_equal 2, result.length
564     assert_equal 4, result[0].length
565     assert_equal visible_relation.id, result[0][0]
566     assert_equal visible_relation.tags, result[0][1]
567     assert_equal visible_relation.members, result[0][2]
568     assert_equal visible_relation.version, result[0][3]
569     assert_equal 4, result[1].length
570     assert_equal used_relation.id, result[1][0]
571     assert_equal used_relation.tags, result[1][1]
572     assert_equal used_relation.members, result[1][2]
573     assert_equal used_relation.version, result[1][3]
574
575     post :amf_read, :body => amf_content("findrelations", "/1", ["no"])
576     assert_response :success
577     amf_parse_response
578     result = amf_result("/1").sort
579
580     assert_equal 0, result.length
581   end
582
583   def test_getpoi_without_timestamp
584     node = create(:node, :with_history, :version => 4)
585     create(:node_tag, :node => node)
586
587     post :amf_read, :body => amf_content("getpoi", "/1", [node.id, ""])
588     assert_response :success
589     amf_parse_response
590     result = amf_result("/1")
591
592     assert_equal 7, result.length
593     assert_equal 0, result[0]
594     assert_equal "", result[1]
595     assert_equal node.id, result[2]
596     assert_equal node.lon, result[3]
597     assert_equal node.lat, result[4]
598     assert_equal node.tags, result[5]
599     assert_equal node.version, result[6]
600
601     post :amf_read, :body => amf_content("getpoi", "/1", [999999, ""])
602     assert_response :success
603     amf_parse_response
604     result = amf_result("/1")
605
606     assert_equal 3, result.length
607     assert_equal(-4, result[0])
608     assert_equal "node", result[1]
609     assert_equal 999999, result[2]
610   end
611
612   def test_getpoi_with_timestamp
613     current_node = create(:node, :with_history, :version => 4)
614     node = current_node.old_nodes.find_by(:version => 2)
615
616     # Timestamps are stored with microseconds, but xmlschema truncates them to
617     # previous whole second, causing <= comparison to fail
618     timestamp = (node.timestamp + 1.second).xmlschema
619
620     post :amf_read, :body => amf_content("getpoi", "/1", [node.node_id, timestamp])
621     assert_response :success
622     amf_parse_response
623     result = amf_result("/1")
624
625     assert_equal 7, result.length
626     assert_equal 0, result[0]
627     assert_equal "", result[1]
628     assert_equal node.node_id, result[2]
629     assert_equal node.lon, result[3]
630     assert_equal node.lat, result[4]
631     assert_equal node.tags, result[5]
632     assert_equal current_node.version, result[6]
633
634     post :amf_read, :body => amf_content("getpoi", "/1", [node.node_id, "2000-01-01T00:00:00Z"])
635     assert_response :success
636     amf_parse_response
637     result = amf_result("/1")
638
639     assert_equal 3, result.length
640     assert_equal(-4, result[0])
641     assert_equal "node", result[1]
642     assert_equal node.node_id, result[2]
643
644     post :amf_read, :body => amf_content("getpoi", "/1", [999999, Time.now.xmlschema])
645     assert_response :success
646     amf_parse_response
647     result = amf_result("/1")
648
649     assert_equal 3, result.length
650     assert_equal(-4, result[0])
651     assert_equal "node", result[1]
652     assert_equal 999999, result[2]
653   end
654
655   # ************************************************************
656   # AMF Write tests
657
658   # check that we can update a poi
659   def test_putpoi_update_valid
660     nd = create(:node)
661     cs_id = nd.changeset.id
662     user = nd.changeset.user
663     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", cs_id, nd.version, nd.id, nd.lon, nd.lat, nd.tags, nd.visible])
664     assert_response :success
665     amf_parse_response
666     result = amf_result("/1")
667
668     assert_equal 5, result.size
669     assert_equal 0, result[0]
670     assert_equal "", result[1]
671     assert_equal nd.id, result[2]
672     assert_equal nd.id, result[3]
673     assert_equal nd.version + 1, result[4]
674
675     # Now try to update again, with a different lat/lon, using the updated version number
676     lat = nd.lat + 0.1
677     lon = nd.lon - 0.1
678     post :amf_write, :body => amf_content("putpoi", "/2", ["#{user.email}:test", cs_id, nd.version + 1, nd.id, lon, lat, nd.tags, nd.visible])
679     assert_response :success
680     amf_parse_response
681     result = amf_result("/2")
682
683     assert_equal 5, result.size
684     assert_equal 0, result[0]
685     assert_equal "", result[1]
686     assert_equal nd.id, result[2]
687     assert_equal nd.id, result[3]
688     assert_equal nd.version + 2, result[4]
689   end
690
691   # Check that we can create a no valid poi
692   # Using similar method for the node controller test
693   def test_putpoi_create_valid
694     # This node has no tags
695
696     # create a node with random lat/lon
697     lat = rand(-50..49) + rand
698     lon = rand(-50..49) + rand
699
700     changeset = create(:changeset)
701     user = changeset.user
702
703     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", changeset.id, nil, nil, lon, lat, {}, nil])
704     assert_response :success
705     amf_parse_response
706     result = amf_result("/1")
707
708     # check the array returned by the amf
709     assert_equal 5, result.size
710     assert_equal 0, result[0], "expected to get the status ok from the amf"
711     assert_equal 0, result[2], "The old id should be 0"
712     assert result[3].positive?, "The new id should be greater than 0"
713     assert_equal 1, result[4], "The new version should be 1"
714
715     # Finally check that the node that was saved has saved the data correctly
716     # in both the current and history tables
717     # First check the current table
718     current_node = Node.find(result[3].to_i)
719     assert_in_delta lat, current_node.lat, 0.00001, "The latitude was not retreieved correctly"
720     assert_in_delta lon, current_node.lon, 0.00001, "The longitude was not retreived correctly"
721     assert_equal 0, current_node.tags.size, "There seems to be a tag that has been added to the node"
722     assert_equal result[4], current_node.version, "The version returned, is different to the one returned by the amf"
723     # Now check the history table
724     historic_nodes = OldNode.where(:node_id => result[3])
725     assert_equal 1, historic_nodes.size, "There should only be one historic node created"
726     first_historic_node = historic_nodes.first
727     assert_in_delta lat, first_historic_node.lat, 0.00001, "The latitude was not retreived correctly"
728     assert_in_delta lon, first_historic_node.lon, 0.00001, "The longitude was not retreuved correctly"
729     assert_equal 0, first_historic_node.tags.size, "There seems to be a tag that have been attached to this node"
730     assert_equal result[4], first_historic_node.version, "The version returned, is different to the one returned by the amf"
731
732     ####
733     # This node has some tags
734
735     # create a node with random lat/lon
736     lat = rand(-50..49) + rand
737     lon = rand(-50..49) + rand
738
739     post :amf_write, :body => amf_content("putpoi", "/2", ["#{user.email}:test", changeset.id, nil, nil, lon, lat, { "key" => "value", "ping" => "pong" }, nil])
740     assert_response :success
741     amf_parse_response
742     result = amf_result("/2")
743
744     # check the array returned by the amf
745     assert_equal 5, result.size
746     assert_equal 0, result[0], "Expected to get the status ok in the amf"
747     assert_equal 0, result[2], "The old id should be 0"
748     assert result[3].positive?, "The new id should be greater than 0"
749     assert_equal 1, result[4], "The new version should be 1"
750
751     # Finally check that the node that was saved has saved the data correctly
752     # in both the current and history tables
753     # First check the current table
754     current_node = Node.find(result[3].to_i)
755     assert_in_delta lat, current_node.lat, 0.00001, "The latitude was not retreieved correctly"
756     assert_in_delta lon, current_node.lon, 0.00001, "The longitude was not retreived correctly"
757     assert_equal 2, current_node.tags.size, "There seems to be a tag that has been added to the node"
758     assert_equal({ "key" => "value", "ping" => "pong" }, current_node.tags, "tags are different")
759     assert_equal result[4], current_node.version, "The version returned, is different to the one returned by the amf"
760     # Now check the history table
761     historic_nodes = OldNode.where(:node_id => result[3])
762     assert_equal 1, historic_nodes.size, "There should only be one historic node created"
763     first_historic_node = historic_nodes.first
764     assert_in_delta lat, first_historic_node.lat, 0.00001, "The latitude was not retreived correctly"
765     assert_in_delta lon, first_historic_node.lon, 0.00001, "The longitude was not retreuved correctly"
766     assert_equal 2, first_historic_node.tags.size, "There seems to be a tag that have been attached to this node"
767     assert_equal({ "key" => "value", "ping" => "pong" }, first_historic_node.tags, "tags are different")
768     assert_equal result[4], first_historic_node.version, "The version returned, is different to the one returned by the amf"
769   end
770
771   # try creating a POI with rubbish in the tags
772   def test_putpoi_create_with_control_chars
773     # This node has no tags
774
775     # create a node with random lat/lon
776     lat = rand(-50..49) + rand
777     lon = rand(-50..49) + rand
778
779     changeset = create(:changeset)
780     user = changeset.user
781
782     mostly_invalid = (0..31).to_a.map(&:chr).join
783     tags = { "something" => "foo#{mostly_invalid}bar" }
784
785     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", changeset.id, nil, nil, lon, lat, tags, nil])
786     assert_response :success
787     amf_parse_response
788     result = amf_result("/1")
789
790     # check the array returned by the amf
791     assert_equal 5, result.size
792     assert_equal 0, result[0], "Expected to get the status ok in the amf"
793     assert_equal 0, result[2], "The old id should be 0"
794     assert result[3].positive?, "The new id should be greater than 0"
795     assert_equal 1, result[4], "The new version should be 1"
796
797     # Finally check that the node that was saved has saved the data correctly
798     # in both the current and history tables
799     # First check the current table
800     current_node = Node.find(result[3].to_i)
801     assert_equal 1, current_node.tags.size, "There seems to be a tag that has been added to the node"
802     assert_equal({ "something" => "foo\t\n\rbar" }, current_node.tags, "tags were not fixed correctly")
803     assert_equal result[4], current_node.version, "The version returned, is different to the one returned by the amf"
804   end
805
806   # try creating a POI with rubbish in the tags
807   def test_putpoi_create_with_invalid_utf8
808     # This node has no tags
809
810     # create a node with random lat/lon
811     lat = rand(-50..49) + rand
812     lon = rand(-50..49) + rand
813
814     changeset = create(:changeset)
815     user = changeset.user
816
817     invalid = "\xc0\xc0"
818     tags = { "something" => "foo#{invalid}bar" }
819
820     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", changeset.id, nil, nil, lon, lat, tags, nil])
821     assert_response :success
822     amf_parse_response
823     result = amf_result("/1")
824
825     assert_equal 2, result.size
826     assert_equal(-1, result[0], "Expected to get the status FAIL in the amf")
827     assert_equal "One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1.", result[1]
828   end
829
830   # try deleting a node
831   def test_putpoi_delete_valid
832     nd = create(:node)
833     cs_id = nd.changeset.id
834     user = nd.changeset.user
835
836     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", cs_id, nd.version, nd.id, nd.lon, nd.lat, nd.tags, false])
837     assert_response :success
838     amf_parse_response
839     result = amf_result("/1")
840
841     assert_equal 5, result.size
842     assert_equal 0, result[0]
843     assert_equal "", result[1]
844     assert_equal nd.id, result[2]
845     assert_equal nd.id, result[3]
846     assert_equal nd.version + 1, result[4]
847
848     current_node = Node.find(result[3].to_i)
849     assert_equal false, current_node.visible
850   end
851
852   # try deleting a node that is already deleted
853   def test_putpoi_delete_already_deleted
854     nd = create(:node, :deleted)
855     cs_id = nd.changeset.id
856     user = nd.changeset.user
857
858     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", cs_id, nd.version, nd.id, nd.lon, nd.lat, nd.tags, false])
859     assert_response :success
860     amf_parse_response
861     result = amf_result("/1")
862
863     assert_equal 3, result.size
864     assert_equal(-4, result[0])
865     assert_equal "node", result[1]
866     assert_equal nd.id, result[2]
867   end
868
869   # try deleting a node that has never existed
870   def test_putpoi_delete_not_found
871     changeset = create(:changeset)
872     cs_id = changeset.id
873     user = changeset.user
874
875     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", cs_id, 1, 999999, 0, 0, {}, false])
876     assert_response :success
877     amf_parse_response
878     result = amf_result("/1")
879
880     assert_equal 3, result.size
881     assert_equal(-4, result[0])
882     assert_equal "node", result[1]
883     assert_equal 999999, result[2]
884   end
885
886   # try setting an invalid location on a node
887   def test_putpoi_invalid_latlon
888     nd = create(:node)
889     cs_id = nd.changeset.id
890     user = nd.changeset.user
891
892     post :amf_write, :body => amf_content("putpoi", "/1", ["#{user.email}:test", cs_id, nd.version, nd.id, 200, 100, nd.tags, true])
893     assert_response :success
894     amf_parse_response
895     result = amf_result("/1")
896
897     assert_equal 2, result.size
898     assert_equal(-2, result[0])
899     assert_match(/Node is not in the world/, result[1])
900   end
901
902   # check that we can create a way
903   def test_putway_create_valid
904     changeset = create(:changeset)
905     cs_id = changeset.id
906     user = changeset.user
907
908     a = create(:node).id
909     b = create(:node).id
910     c = create(:node).id
911     d = create(:node).id
912     e = create(:node).id
913
914     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, 0, -1, [a, b, c], { "test" => "new" }, [], {}])
915     assert_response :success
916     amf_parse_response
917     result = amf_result("/1")
918     new_way_id = result[3].to_i
919
920     assert_equal 8, result.size
921     assert_equal 0, result[0]
922     assert_equal "", result[1]
923     assert_equal(-1, result[2])
924     assert_not_equal(-1, result[3])
925     assert_equal({}, result[4])
926     assert_equal 1, result[5]
927     assert_equal({}, result[6])
928     assert_equal({}, result[7])
929
930     new_way = Way.find(new_way_id)
931     assert_equal 1, new_way.version
932     assert_equal [a, b, c], new_way.nds
933     assert_equal({ "test" => "new" }, new_way.tags)
934
935     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, 0, -1, [b, d, e, a], { "test" => "newer" }, [], {}])
936     assert_response :success
937     amf_parse_response
938     result = amf_result("/1")
939     new_way_id = result[3].to_i
940
941     assert_equal 8, result.size
942     assert_equal 0, result[0]
943     assert_equal "", result[1]
944     assert_equal(-1, result[2])
945     assert_not_equal(-1, result[3])
946     assert_equal({}, result[4])
947     assert_equal 1, result[5]
948     assert_equal({}, result[6])
949     assert_equal({}, result[7])
950
951     new_way = Way.find(new_way_id)
952     assert_equal 1, new_way.version
953     assert_equal [b, d, e, a], new_way.nds
954     assert_equal({ "test" => "newer" }, new_way.tags)
955
956     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, 0, -1, [b, -1, d, e], { "test" => "newest" }, [[4.56, 12.34, -1, 0, { "test" => "new" }], [12.34, 4.56, d, 1, { "test" => "ok" }]], { a => 1 }])
957     assert_response :success
958     amf_parse_response
959     result = amf_result("/1")
960     new_way_id = result[3].to_i
961     new_node_id = result[4]["-1"].to_i
962
963     assert_equal 8, result.size
964     assert_equal 0, result[0]
965     assert_equal "", result[1]
966     assert_equal(-1, result[2])
967     assert_not_equal(-1, result[3])
968     assert_equal({ "-1" => new_node_id }, result[4])
969     assert_equal 1, result[5]
970     assert_equal({ new_node_id.to_s => 1, d.to_s => 2 }, result[6])
971     assert_equal({ a.to_s => 1 }, result[7])
972
973     new_way = Way.find(new_way_id)
974     assert_equal 1, new_way.version
975     assert_equal [b, new_node_id, d, e], new_way.nds
976     assert_equal({ "test" => "newest" }, new_way.tags)
977
978     new_node = Node.find(new_node_id)
979     assert_equal 1, new_node.version
980     assert_equal true, new_node.visible
981     assert_equal 4.56, new_node.lon
982     assert_equal 12.34, new_node.lat
983     assert_equal({ "test" => "new" }, new_node.tags)
984
985     changed_node = Node.find(d)
986     assert_equal 2, changed_node.version
987     assert_equal true, changed_node.visible
988     assert_equal 12.34, changed_node.lon
989     assert_equal 4.56, changed_node.lat
990     assert_equal({ "test" => "ok" }, changed_node.tags)
991
992     # node is not deleted because our other ways are using it
993     deleted_node = Node.find(a)
994     assert_equal 1, deleted_node.version
995     assert_equal true, deleted_node.visible
996   end
997
998   # check that we can update a way
999   def test_putway_update_valid
1000     way = create(:way_with_nodes, :nodes_count => 3)
1001     cs_id = way.changeset.id
1002     user = way.changeset.user
1003
1004     assert_not_equal({ "test" => "ok" }, way.tags)
1005     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, way.version, way.id, way.nds, { "test" => "ok" }, [], {}])
1006     assert_response :success
1007     amf_parse_response
1008     result = amf_result("/1")
1009
1010     assert_equal 8, result.size
1011     assert_equal 0, result[0]
1012     assert_equal "", result[1]
1013     assert_equal way.id, result[2]
1014     assert_equal way.id, result[3]
1015     assert_equal({}, result[4])
1016     assert_equal way.version + 1, result[5]
1017     assert_equal({}, result[6])
1018     assert_equal({}, result[7])
1019
1020     new_way = Way.find(way.id)
1021     assert_equal way.version + 1, new_way.version
1022     assert_equal way.nds, new_way.nds
1023     assert_equal({ "test" => "ok" }, new_way.tags)
1024
1025     # Test changing the nodes in the way
1026     a = create(:node).id
1027     b = create(:node).id
1028     c = create(:node).id
1029     d = create(:node).id
1030
1031     assert_not_equal [a, b, c, d], way.nds
1032     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, way.version + 1, way.id, [a, b, c, d], way.tags, [], {}])
1033     assert_response :success
1034     amf_parse_response
1035     result = amf_result("/1")
1036
1037     assert_equal 8, result.size
1038     assert_equal 0, result[0]
1039     assert_equal "", result[1]
1040     assert_equal way.id, result[2]
1041     assert_equal way.id, result[3]
1042     assert_equal({}, result[4])
1043     assert_equal way.version + 2, result[5]
1044     assert_equal({}, result[6])
1045     assert_equal({}, result[7])
1046
1047     new_way = Way.find(way.id)
1048     assert_equal way.version + 2, new_way.version
1049     assert_equal [a, b, c, d], new_way.nds
1050     assert_equal way.tags, new_way.tags
1051
1052     post :amf_write, :body => amf_content("putway", "/1", ["#{user.email}:test", cs_id, way.version + 2, way.id, [a, -1, b, c], way.tags, [[4.56, 12.34, -1, 0, { "test" => "new" }], [12.34, 4.56, b, 1, { "test" => "ok" }]], { d => 1 }])
1053     assert_response :success
1054     amf_parse_response
1055     result = amf_result("/1")
1056     new_node_id = result[4]["-1"].to_i
1057
1058     assert_equal 8, result.size
1059     assert_equal 0, result[0]
1060     assert_equal "", result[1]
1061     assert_equal way.id, result[2]
1062     assert_equal way.id, result[3]
1063     assert_equal({ "-1" => new_node_id }, result[4])
1064     assert_equal way.version + 3, result[5]
1065     assert_equal({ new_node_id.to_s => 1, b.to_s => 2 }, result[6])
1066     assert_equal({ d.to_s => 1 }, result[7])
1067
1068     new_way = Way.find(way.id)
1069     assert_equal way.version + 3, new_way.version
1070     assert_equal [a, new_node_id, b, c], new_way.nds
1071     assert_equal way.tags, new_way.tags
1072
1073     new_node = Node.find(new_node_id)
1074     assert_equal 1, new_node.version
1075     assert_equal true, new_node.visible
1076     assert_equal 4.56, new_node.lon
1077     assert_equal 12.34, new_node.lat
1078     assert_equal({ "test" => "new" }, new_node.tags)
1079
1080     changed_node = Node.find(b)
1081     assert_equal 2, changed_node.version
1082     assert_equal true, changed_node.visible
1083     assert_equal 12.34, changed_node.lon
1084     assert_equal 4.56, changed_node.lat
1085     assert_equal({ "test" => "ok" }, changed_node.tags)
1086
1087     deleted_node = Node.find(d)
1088     assert_equal 2, deleted_node.version
1089     assert_equal false, deleted_node.visible
1090   end
1091
1092   # check that we can delete a way
1093   def test_deleteway_valid
1094     way = create(:way_with_nodes, :nodes_count => 3)
1095     nodes = way.nodes.each_with_object({}) { |n, ns| ns[n.id] = n.version }
1096     cs_id = way.changeset.id
1097     user = way.changeset.user
1098
1099     # Of the three nodes, two should be kept since they are used in
1100     # a different way, and the third deleted since it's unused
1101
1102     a = way.nodes[0]
1103     create(:way_node, :node => a)
1104     b = way.nodes[1]
1105     create(:way_node, :node => b)
1106     c = way.nodes[2]
1107
1108     post :amf_write, :body => amf_content("deleteway", "/1", ["#{user.email}:test", cs_id, way.id, way.version, nodes])
1109     assert_response :success
1110     amf_parse_response
1111     result = amf_result("/1")
1112
1113     assert_equal 5, result.size
1114     assert_equal 0, result[0]
1115     assert_equal "", result[1]
1116     assert_equal way.id, result[2]
1117     assert_equal way.version + 1, result[3]
1118     assert_equal({ c.id.to_s => 2 }, result[4])
1119
1120     new_way = Way.find(way.id)
1121     assert_equal way.version + 1, new_way.version
1122     assert_equal false, new_way.visible
1123
1124     way.nds.each do |node_id|
1125       assert_equal result[4][node_id.to_s].nil?, Node.find(node_id).visible
1126     end
1127   end
1128
1129   # check that we can't delete a way that is in use
1130   def test_deleteway_inuse
1131     way = create(:way_with_nodes, :nodes_count => 4)
1132     create(:relation_member, :member => way)
1133     nodes = way.nodes.each_with_object({}) { |n, ns| ns[n.id] = n.version }
1134     cs_id = way.changeset.id
1135     user = way.changeset.user
1136
1137     post :amf_write, :body => amf_content("deleteway", "/1", ["#{user.email}:test", cs_id, way.id, way.version, nodes])
1138     assert_response :success
1139     amf_parse_response
1140     result = amf_result("/1")
1141
1142     assert_equal 2, result.size
1143     assert_equal(-1, result[0])
1144     assert_match(/Way #{way.id} is still used/, result[1])
1145
1146     new_way = Way.find(way.id)
1147     assert_equal way.version, new_way.version
1148     assert_equal true, new_way.visible
1149
1150     way.nds.each do |node_id|
1151       assert_equal true, Node.find(node_id).visible
1152     end
1153   end
1154
1155   # check that we can create a relation
1156   def test_putrelation_create_valid
1157     changeset = create(:changeset)
1158     user = changeset.user
1159     cs_id = changeset.id
1160
1161     node = create(:node)
1162     way = create(:way_with_nodes, :nodes_count => 2)
1163     relation = create(:relation)
1164
1165     post :amf_write, :body => amf_content("putrelation", "/1", ["#{user.email}:test", cs_id, 0, -1, { "test" => "new" }, [["Node", node.id, "node"], ["Way", way.id, "way"], ["Relation", relation.id, "relation"]], true])
1166     assert_response :success
1167     amf_parse_response
1168     result = amf_result("/1")
1169     new_relation_id = result[3].to_i
1170
1171     assert_equal 5, result.size
1172     assert_equal 0, result[0]
1173     assert_equal "", result[1]
1174     assert_equal(-1, result[2])
1175     assert_not_equal(-1, result[3])
1176     assert_equal 1, result[4]
1177
1178     new_relation = Relation.find(new_relation_id)
1179     assert_equal 1, new_relation.version
1180     assert_equal [["Node", node.id, "node"], ["Way", way.id, "way"], ["Relation", relation.id, "relation"]], new_relation.members
1181     assert_equal({ "test" => "new" }, new_relation.tags)
1182     assert_equal true, new_relation.visible
1183   end
1184
1185   # check that we can update a relation
1186   def test_putrelation_update_valid
1187     relation = create(:relation)
1188     create(:relation_member, :relation => relation)
1189     user = relation.changeset.user
1190     cs_id = relation.changeset.id
1191
1192     assert_not_equal({ "test" => "ok" }, relation.tags)
1193     post :amf_write, :body => amf_content("putrelation", "/1", ["#{user.email}:test", cs_id, relation.version, relation.id, { "test" => "ok" }, relation.members, true])
1194     assert_response :success
1195     amf_parse_response
1196     result = amf_result("/1")
1197
1198     assert_equal 5, result.size
1199     assert_equal 0, result[0]
1200     assert_equal "", result[1]
1201     assert_equal relation.id, result[2]
1202     assert_equal relation.id, result[3]
1203     assert_equal relation.version + 1, result[4]
1204
1205     new_relation = Relation.find(relation.id)
1206     assert_equal relation.version + 1, new_relation.version
1207     assert_equal relation.members, new_relation.members
1208     assert_equal({ "test" => "ok" }, new_relation.tags)
1209     assert_equal true, new_relation.visible
1210   end
1211
1212   # check that we can delete a relation
1213   def test_putrelation_delete_valid
1214     relation = create(:relation)
1215     create(:relation_member, :relation => relation)
1216     create(:relation_tag, :relation => relation)
1217     cs_id = relation.changeset.id
1218     user = relation.changeset.user
1219
1220     post :amf_write, :body => amf_content("putrelation", "/1", ["#{user.email}:test", cs_id, relation.version, relation.id, relation.tags, relation.members, false])
1221     assert_response :success
1222     amf_parse_response
1223     result = amf_result("/1")
1224
1225     assert_equal 5, result.size
1226     assert_equal 0, result[0]
1227     assert_equal "", result[1]
1228     assert_equal relation.id, result[2]
1229     assert_equal relation.id, result[3]
1230     assert_equal relation.version + 1, result[4]
1231
1232     new_relation = Relation.find(relation.id)
1233     assert_equal relation.version + 1, new_relation.version
1234     assert_equal [], new_relation.members
1235     assert_equal({}, new_relation.tags)
1236     assert_equal false, new_relation.visible
1237   end
1238
1239   # check that we can't delete a relation that is in use
1240   def test_putrelation_delete_inuse
1241     relation = create(:relation)
1242     super_relation = create(:relation)
1243     create(:relation_member, :relation => super_relation, :member => relation)
1244     cs_id = relation.changeset.id
1245     user = relation.changeset.user
1246
1247     post :amf_write, :body => amf_content("putrelation", "/1", ["#{user.email}:test", cs_id, relation.version, relation.id, relation.tags, relation.members, false])
1248     assert_response :success
1249     amf_parse_response
1250     result = amf_result("/1")
1251
1252     assert_equal 2, result.size
1253     assert_equal(-1, result[0])
1254     assert_match(/relation #{relation.id} is used in/, result[1])
1255
1256     new_relation = Relation.find(relation.id)
1257     assert_equal relation.version, new_relation.version
1258     assert_equal relation.members, new_relation.members
1259     assert_equal relation.tags, new_relation.tags
1260     assert_equal true, new_relation.visible
1261   end
1262
1263   # check that we can open a changeset
1264   def test_startchangeset_valid
1265     user = create(:user)
1266
1267     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user.email}:test", { "source" => "new" }, nil, "new", 1])
1268     assert_response :success
1269     amf_parse_response
1270     result = amf_result("/1")
1271     new_cs_id = result[2].to_i
1272
1273     assert_equal 3, result.size
1274     assert_equal 0, result[0]
1275     assert_equal "", result[1]
1276
1277     cs = Changeset.find(new_cs_id)
1278     assert_equal true, cs.is_open?
1279     assert_equal({ "comment" => "new", "source" => "new" }, cs.tags)
1280
1281     old_cs_id = new_cs_id
1282
1283     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user.email}:test", { "source" => "newer" }, old_cs_id, "newer", 1])
1284     assert_response :success
1285     amf_parse_response
1286     result = amf_result("/1")
1287     new_cs_id = result[2].to_i
1288
1289     assert_not_equal old_cs_id, new_cs_id
1290
1291     assert_equal 3, result.size
1292     assert_equal 0, result[0]
1293     assert_equal "", result[1]
1294
1295     cs = Changeset.find(old_cs_id)
1296     assert_equal false, cs.is_open?
1297     assert_equal({ "comment" => "newer", "source" => "new" }, cs.tags)
1298
1299     cs = Changeset.find(new_cs_id)
1300     assert_equal true, cs.is_open?
1301     assert_equal({ "comment" => "newer", "source" => "newer" }, cs.tags)
1302
1303     old_cs_id = new_cs_id
1304
1305     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user.email}:test", {}, old_cs_id, "", 0])
1306     assert_response :success
1307     amf_parse_response
1308     result = amf_result("/1")
1309
1310     assert_equal 3, result.size
1311     assert_equal 0, result[0]
1312     assert_equal "", result[1]
1313     assert_nil result[2]
1314
1315     cs = Changeset.find(old_cs_id)
1316     assert_equal false, cs.is_open?
1317     assert_equal({ "comment" => "newer", "source" => "newer" }, cs.tags)
1318   end
1319
1320   # check that we can't close somebody elses changeset
1321   def test_startchangeset_invalid_wrong_user
1322     user = create(:user)
1323     user2 = create(:user)
1324
1325     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user.email}:test", { "source" => "new" }, nil, "new", 1])
1326     assert_response :success
1327     amf_parse_response
1328     result = amf_result("/1")
1329     cs_id = result[2].to_i
1330
1331     assert_equal 3, result.size
1332     assert_equal 0, result[0]
1333     assert_equal "", result[1]
1334
1335     cs = Changeset.find(cs_id)
1336     assert_equal true, cs.is_open?
1337     assert_equal({ "comment" => "new", "source" => "new" }, cs.tags)
1338
1339     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user2.email}:test", {}, cs_id, "delete", 0])
1340     assert_response :success
1341     amf_parse_response
1342     result = amf_result("/1")
1343
1344     assert_equal 2, result.size
1345     assert_equal(-2, result[0])
1346     assert_equal "The user doesn't own that changeset", result[1]
1347
1348     cs = Changeset.find(cs_id)
1349     assert_equal true, cs.is_open?
1350     assert_equal({ "comment" => "new", "source" => "new" }, cs.tags)
1351   end
1352
1353   # check that invalid characters are stripped from changeset tags
1354   def test_startchangeset_invalid_xmlchar_comment
1355     user = create(:user)
1356
1357     invalid = "\035\022"
1358     comment = "foo#{invalid}bar"
1359
1360     post :amf_write, :body => amf_content("startchangeset", "/1", ["#{user.email}:test", {}, nil, comment, 1])
1361     assert_response :success
1362     amf_parse_response
1363     result = amf_result("/1")
1364     new_cs_id = result[2].to_i
1365
1366     assert_equal 3, result.size
1367     assert_equal 0, result[0]
1368     assert_equal "", result[1]
1369
1370     cs = Changeset.find(new_cs_id)
1371     assert_equal true, cs.is_open?
1372     assert_equal({ "comment" => "foobar" }, cs.tags)
1373   end
1374
1375   private
1376
1377   # ************************************************************
1378   # AMF Helper functions
1379
1380   # Get the result record for the specified ID
1381   # It's an assertion FAIL if the record does not exist
1382   def amf_result(ref)
1383     assert @amf_result.key?("#{ref}/onResult")
1384     @amf_result["#{ref}/onResult"]
1385   end
1386
1387   # Encode the AMF message to invoke "target" with parameters as
1388   # the passed data. The ref is used to retrieve the results.
1389   def amf_content(target, ref, data)
1390     a, b = 1.divmod(256)
1391     c = StringIO.new
1392     c.write 0.chr + 0.chr   # version 0
1393     c.write 0.chr + 0.chr   # n headers
1394     c.write a.chr + b.chr   # n bodies
1395     c.write AMF.encodestring(target)
1396     c.write AMF.encodestring(ref)
1397     c.write [-1].pack("N")
1398     c.write AMF.encodevalue(data)
1399
1400     c.string
1401   end
1402
1403   # Parses the @response object as an AMF messsage.
1404   # The result is a hash of message_ref => data.
1405   # The attribute @amf_result is initialised to this hash.
1406   def amf_parse_response
1407     req = StringIO.new(@response.body)
1408
1409     req.read(2) # version
1410
1411     # parse through any headers
1412     headers = AMF.getint(req)        # Read number of headers
1413     headers.times do                 # Read each header
1414       AMF.getstring(req)             #  |
1415       req.getc                       #  | skip boolean
1416       AMF.getvalue(req)              #  |
1417     end
1418
1419     # parse through responses
1420     results = {}
1421     bodies = AMF.getint(req)         # Read number of bodies
1422     bodies.times do                  # Read each body
1423       message = AMF.getstring(req)   #  | get message name
1424       AMF.getstring(req)             #  | get index in response sequence
1425       AMF.getlong(req)               #  | get total size in bytes
1426       args = AMF.getvalue(req)       #  | get response (probably an array)
1427       results[message] = args
1428     end
1429     @amf_result = results
1430     results
1431   end
1432
1433   ##
1434   # given an array of bounding boxes (each an array of 4 floats), call the
1435   # AMF "whichways" controller for each and pass the result back to the
1436   # caller's block for assertion testing.
1437   def check_bboxes_are_bad(bboxes)
1438     bboxes.each do |bbox|
1439       post :amf_read, :body => amf_content("whichways", "/1", bbox)
1440       assert_response :success
1441       amf_parse_response
1442
1443       # pass the response back to the caller's block to be tested
1444       # against what the caller expected.
1445       map = amf_result "/1"
1446       yield map, bbox
1447     end
1448   end
1449
1450   # this should be what AMF controller returns when the bbox of a
1451   # whichways request is invalid or too large.
1452   def assert_boundary_error(map, msg = nil, error_hint = nil)
1453     expected_map = [-2, "Sorry - I can't get the map for that area.#{msg}"]
1454     assert_equal expected_map, map, "AMF controller should have returned an error. (#{error_hint})"
1455   end
1456
1457   # this should be what AMF controller returns when the bbox of a
1458   # whichways_deleted request is invalid or too large.
1459   def assert_deleted_boundary_error(map, msg = nil, error_hint = nil)
1460     expected_map = [-2, "Sorry - I can't get the map for that area.#{msg}"]
1461     assert_equal expected_map, map, "AMF controller should have returned an error. (#{error_hint})"
1462   end
1463 end