source: subversion/sites/rails_port_branches/api06/test/functional/changeset_controller_test.rb @ 14478

Last change on this file since 14478 was 14478, checked in by Shaun McDonald, 10 years ago

remove the dup methods from the test helper. Add tests for no content and checking that a record has been created.

File size: 37.4 KB
Line 
1require File.dirname(__FILE__) + '/../test_helper'
2require 'changeset_controller'
3
4class ChangesetControllerTest < ActionController::TestCase
5  api_fixtures
6
7  # -----------------------
8  # Test simple changeset creation
9  # -----------------------
10 
11  def test_create
12    basic_authorization "test@openstreetmap.org", "test"
13   
14    # Create the first user's changeset
15    content "<osm><changeset>" +
16      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
17      "</changeset></osm>"
18    put :create
19   
20    assert_response :success, "Creation of changeset did not return sucess status"
21    newid = @response.body.to_i
22
23    # check end time, should be an hour ahead of creation time
24    cs = Changeset.find(newid)
25    duration = cs.closed_at - cs.created_at
26    # the difference can either be a rational, or a floating point number
27    # of seconds, depending on the code path taken :-(
28    if duration.class == Rational
29      assert_equal Rational(1,24), duration , "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
30    else
31      # must be number of seconds...
32      assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
33    end
34  end
35 
36  def test_create_invalid
37    basic_authorization "test@openstreetmap.org", "test"
38    content "<osm><changeset></osm>"
39    put :create
40    assert_response :bad_request, "creating a invalid changeset should fail"
41  end
42
43  def test_create_invalid_no_content
44    basic_authorization "test@openstreetmap.org", "test"
45    put :create
46    assert_response :bad_request, "creating a changeset with no content should fail"
47  end
48 
49  def test_create_wrong_method
50    basic_authorization "test@openstreetmap.org", "test"
51    get :create
52    assert_response :method_not_allowed
53  end
54   
55  ##
56  # check that the changeset can be read and returns the correct
57  # document structure.
58  def test_read
59    changeset_id = changesets(:normal_user_first_change).id
60    get :read, :id => changeset_id
61    assert_response :success, "cannot get first changeset"
62   
63    assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
64    assert_select "osm>changeset[id=#{changeset_id}]", 1
65  end
66 
67  ##
68  # test that the user who opened a change can close it
69  def test_close
70    basic_authorization "test@openstreetmap.org", "test"
71
72    cs_id = changesets(:normal_user_first_change).id
73    put :close, :id => cs_id
74    assert_response :success
75
76    # test that it really is closed now
77    cs = Changeset.find(cs_id)
78    assert(!cs.is_open?, 
79           "changeset should be closed now (#{cs.closed_at} > #{Time.now}.")
80  end
81
82  ##
83  # test that a different user can't close another user's changeset
84  def test_close_invalid
85    basic_authorization "test@example.com", "test"
86
87    put :close, :id => changesets(:normal_user_first_change).id
88    assert_response :conflict
89    assert_equal "The user doesn't own that changeset", @response.body
90  end
91
92  ##
93  # upload something simple, but valid and check that it can
94  # be read back ok.
95  def test_upload_simple_valid
96    basic_authorization "test@openstreetmap.org", "test"
97
98    # simple diff to change a node, way and relation by removing
99    # their tags
100    diff = <<EOF
101<osmChange>
102 <modify>
103  <node id='1' lon='0' lat='0' changeset='1' version='1'/>
104  <way id='1' changeset='1' version='1'>
105   <nd ref='3'/>
106  </way>
107 </modify>
108 <modify>
109  <relation id='1' changeset='1' version='1'>
110   <member type='way' role='some' ref='3'/>
111   <member type='node' role='some' ref='5'/>
112   <member type='relation' role='some' ref='3'/>
113  </relation>
114 </modify>
115</osmChange>
116EOF
117
118    # upload it
119    content diff
120    post :upload, :id => 1
121    assert_response :success, 
122      "can't upload a simple valid diff to changeset: #{@response.body}"
123
124    # check that the changes made it into the database
125    assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
126    assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
127    assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
128  end
129   
130  ##
131  # upload something which creates new objects using placeholders
132  def test_upload_create_valid
133    basic_authorization "test@openstreetmap.org", "test"
134
135    # simple diff to create a node way and relation using placeholders
136    diff = <<EOF
137<osmChange>
138 <create>
139  <node id='-1' lon='0' lat='0' changeset='1'>
140   <tag k='foo' v='bar'/>
141   <tag k='baz' v='bat'/>
142  </node>
143  <way id='-1' changeset='1'>
144   <nd ref='3'/>
145  </way>
146 </create>
147 <create>
148  <relation id='-1' changeset='1'>
149   <member type='way' role='some' ref='3'/>
150   <member type='node' role='some' ref='5'/>
151   <member type='relation' role='some' ref='3'/>
152  </relation>
153 </create>
154</osmChange>
155EOF
156
157    # upload it
158    content diff
159    post :upload, :id => 1
160    assert_response :success, 
161      "can't upload a simple valid creation to changeset: #{@response.body}"
162
163    # check the returned payload
164    assert_select "diffResult[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
165    assert_select "diffResult>node", 1
166    assert_select "diffresult>way", 1
167    assert_select "diffResult>relation", 1
168
169    # inspect the response to find out what the new element IDs are
170    doc = XML::Parser.string(@response.body).parse
171    new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
172    new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
173    new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
174
175    # check the old IDs are all present and negative one
176    assert_equal -1, doc.find("//diffResult/node").first["old_id"].to_i
177    assert_equal -1, doc.find("//diffResult/way").first["old_id"].to_i
178    assert_equal -1, doc.find("//diffResult/relation").first["old_id"].to_i
179
180    # check the versions are present and equal one
181    assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
182    assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
183    assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
184
185    # check that the changes made it into the database
186    assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
187    assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
188    assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
189  end
190   
191  ##
192  # test a complex delete where we delete elements which rely on eachother
193  # in the same transaction.
194  def test_upload_delete
195    basic_authorization "test@openstreetmap.org", "test"
196
197    diff = XML::Document.new
198    diff.root = XML::Node.new "osmChange"
199    delete = XML::Node.new "delete"
200    diff.root << delete
201    delete << current_relations(:visible_relation).to_xml_node
202    delete << current_relations(:used_relation).to_xml_node
203    delete << current_ways(:used_way).to_xml_node
204    delete << current_nodes(:node_used_by_relationship).to_xml_node
205
206    # upload it
207    content diff
208    post :upload, :id => 1
209    assert_response :success, 
210      "can't upload a deletion diff to changeset: #{@response.body}"
211
212    # check the response is well-formed
213    assert_select "diffResult>node", 1
214    assert_select "diffResult>way", 1
215    assert_select "diffResult>relation", 2
216
217    # check that everything was deleted
218    assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
219    assert_equal false, Way.find(current_ways(:used_way).id).visible
220    assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
221    assert_equal false, Relation.find(current_relations(:used_relation).id).visible
222  end
223
224  ##
225  # test uploading a delete with no lat/lon, as they are optional in
226  # the osmChange spec.
227  def test_upload_nolatlon_delete
228    basic_authorization "test@openstreetmap.org", "test"
229
230    node = current_nodes(:visible_node)
231    cs = changesets(:normal_user_first_change)
232    diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{cs.id}'/></delete></osmChange>"
233
234    # upload it
235    content diff
236    post :upload, :id => cs.id
237    assert_response :success, 
238      "can't upload a deletion diff to changeset: #{@response.body}"
239
240    # check the response is well-formed
241    assert_select "diffResult>node", 1
242
243    # check that everything was deleted
244    assert_equal false, Node.find(node.id).visible
245  end
246
247  def test_repeated_changeset_create
248    30.times do
249      basic_authorization "test@openstreetmap.org", "test"
250   
251      # create a temporary changeset
252      content "<osm><changeset>" +
253        "<tag k='created_by' v='osm test suite checking changesets'/>" + 
254        "</changeset></osm>"
255      assert_difference('Changeset.count', 1) do
256        put :create
257      end
258      assert_response :success
259      changeset_id = @response.body.to_i
260    end
261  end
262
263  ##
264  # test that deleting stuff in a transaction doesn't bypass the checks
265  # to ensure that used elements are not deleted.
266  def test_upload_delete_invalid
267    basic_authorization "test@openstreetmap.org", "test"
268
269    diff = XML::Document.new
270    diff.root = XML::Node.new "osmChange"
271    delete = XML::Node.new "delete"
272    diff.root << delete
273    delete << current_relations(:visible_relation).to_xml_node
274    delete << current_ways(:used_way).to_xml_node
275    delete << current_nodes(:node_used_by_relationship).to_xml_node
276
277    # upload it
278    content diff
279    post :upload, :id => 1
280    assert_response :precondition_failed, 
281      "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
282
283    # check that nothing was, in fact, deleted
284    assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
285    assert_equal true, Way.find(current_ways(:used_way).id).visible
286    assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
287  end
288
289  ##
290  # upload something which creates new objects and inserts them into
291  # existing containers using placeholders.
292  def test_upload_complex
293    basic_authorization "test@openstreetmap.org", "test"
294
295    # simple diff to create a node way and relation using placeholders
296    diff = <<EOF
297<osmChange>
298 <create>
299  <node id='-1' lon='0' lat='0' changeset='1'>
300   <tag k='foo' v='bar'/>
301   <tag k='baz' v='bat'/>
302  </node>
303 </create>
304 <modify>
305  <way id='1' changeset='1' version='1'>
306   <nd ref='-1'/>
307   <nd ref='3'/>
308  </way>
309  <relation id='1' changeset='1' version='1'>
310   <member type='way' role='some' ref='3'/>
311   <member type='node' role='some' ref='-1'/>
312   <member type='relation' role='some' ref='3'/>
313  </relation>
314 </modify>
315</osmChange>
316EOF
317
318    # upload it
319    content diff
320    post :upload, :id => 1
321    assert_response :success, 
322      "can't upload a complex diff to changeset: #{@response.body}"
323
324    # check the returned payload
325    assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
326    assert_select "diffResult>node", 1
327    assert_select "diffResult>way", 1
328    assert_select "diffResult>relation", 1
329
330    # inspect the response to find out what the new element IDs are
331    doc = XML::Parser.string(@response.body).parse
332    new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
333
334    # check that the changes made it into the database
335    assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
336    assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
337    Relation.find(1).members.each do |type,id,role|
338      if type == 'node'
339        assert_equal new_node_id, id, "relation should contain new node"
340      end
341    end
342  end
343   
344  ##
345  # create a diff which references several changesets, which should cause
346  # a rollback and none of the diff gets committed
347  def test_upload_invalid_changesets
348    basic_authorization "test@openstreetmap.org", "test"
349
350    # simple diff to create a node way and relation using placeholders
351    diff = <<EOF
352<osmChange>
353 <modify>
354  <node id='1' lon='0' lat='0' changeset='1' version='1'/>
355  <way id='1' changeset='1' version='1'>
356   <nd ref='3'/>
357  </way>
358 </modify>
359 <modify>
360  <relation id='1' changeset='1' version='1'>
361   <member type='way' role='some' ref='3'/>
362   <member type='node' role='some' ref='5'/>
363   <member type='relation' role='some' ref='3'/>
364  </relation>
365 </modify>
366 <create>
367  <node id='-1' lon='0' lat='0' changeset='4'>
368   <tag k='foo' v='bar'/>
369   <tag k='baz' v='bat'/>
370  </node>
371 </create>
372</osmChange>
373EOF
374    # cache the objects before uploading them
375    node = current_nodes(:visible_node)
376    way = current_ways(:visible_way)
377    rel = current_relations(:visible_relation)
378
379    # upload it
380    content diff
381    post :upload, :id => 1
382    assert_response :conflict, 
383      "uploading a diff with multiple changsets should have failed"
384
385    # check that objects are unmodified
386    assert_nodes_are_equal(node, Node.find(1))
387    assert_ways_are_equal(way, Way.find(1))
388  end
389   
390  ##
391  # upload multiple versions of the same element in the same diff.
392  def test_upload_multiple_valid
393    basic_authorization "test@openstreetmap.org", "test"
394
395    # change the location of a node multiple times, each time referencing
396    # the last version. doesn't this depend on version numbers being
397    # sequential?
398    diff = <<EOF
399<osmChange>
400 <modify>
401  <node id='1' lon='0' lat='0' changeset='1' version='1'/>
402  <node id='1' lon='1' lat='0' changeset='1' version='2'/>
403  <node id='1' lon='1' lat='1' changeset='1' version='3'/>
404  <node id='1' lon='1' lat='2' changeset='1' version='4'/>
405  <node id='1' lon='2' lat='2' changeset='1' version='5'/>
406  <node id='1' lon='3' lat='2' changeset='1' version='6'/>
407  <node id='1' lon='3' lat='3' changeset='1' version='7'/>
408  <node id='1' lon='9' lat='9' changeset='1' version='8'/>
409 </modify>
410</osmChange>
411EOF
412
413    # upload it
414    content diff
415    post :upload, :id => 1
416    assert_response :success, 
417      "can't upload multiple versions of an element in a diff: #{@response.body}"
418   
419    # check the response is well-formed. its counter-intuitive, but the
420    # API will return multiple elements with the same ID and different
421    # version numbers for each change we made.
422    assert_select "diffResult>node", 8
423  end
424
425  ##
426  # upload multiple versions of the same element in the same diff, but
427  # keep the version numbers the same.
428  def test_upload_multiple_duplicate
429    basic_authorization "test@openstreetmap.org", "test"
430
431    diff = <<EOF
432<osmChange>
433 <modify>
434  <node id='1' lon='0' lat='0' changeset='1' version='1'/>
435  <node id='1' lon='1' lat='1' changeset='1' version='1'/>
436 </modify>
437</osmChange>
438EOF
439
440    # upload it
441    content diff
442    post :upload, :id => 1
443    assert_response :conflict, 
444      "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
445  end
446
447  ##
448  # try to upload some elements without specifying the version
449  def test_upload_missing_version
450    basic_authorization "test@openstreetmap.org", "test"
451
452    diff = <<EOF
453<osmChange>
454 <modify>
455  <node id='1' lon='1' lat='1' changeset='1'/>
456 </modify>
457</osmChange>
458EOF
459
460    # upload it
461    content diff
462    post :upload, :id => 1
463    assert_response :bad_request, 
464      "shouldn't be able to upload an element without version: #{@response.body}"
465  end
466 
467  ##
468  # try to upload with commands other than create, modify, or delete
469  def test_action_upload_invalid
470    basic_authorization "test@openstreetmap.org", "test"
471   
472    diff = <<EOF
473<osmChange>
474  <ping>
475    <node id='1' lon='1' lat='1' changeset='1' />
476  </ping>
477</osmChange>
478EOF
479  content diff
480  post :upload, :id => 1
481  assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
482  assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
483  end
484
485  ##
486  # upload a valid changeset which has a mixture of whitespace
487  # to check a bug reported by ivansanchez (#1565).
488  def test_upload_whitespace_valid
489    basic_authorization "test@openstreetmap.org", "test"
490
491    diff = <<EOF
492<osmChange>
493 <modify><node id='1' lon='0' lat='0' changeset='1'
494  version='1'></node>
495  <node id='1' lon='1' lat='1' changeset='1' version='2'><tag k='k' v='v'/></node></modify>
496 <modify>
497  <relation id='1' changeset='1' version='1'><member
498   type='way' role='some' ref='3'/><member
499    type='node' role='some' ref='5'/>
500   <member type='relation' role='some' ref='3'/>
501  </relation>
502 </modify></osmChange>
503EOF
504
505    # upload it
506    content diff
507    post :upload, :id => 1
508    assert_response :success, 
509      "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
510
511    # check the response is well-formed
512    assert_select "diffResult>node", 2
513    assert_select "diffResult>relation", 1
514
515    # check that the changes made it into the database
516    assert_equal 1, Node.find(1).tags.size, "node 1 should now have one tag"
517    assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
518  end
519
520  ##
521  # upload a valid changeset which has a mixture of whitespace
522  # to check a bug reported by ivansanchez.
523  def test_upload_reuse_placeholder_valid
524    basic_authorization "test@openstreetmap.org", "test"
525
526    diff = <<EOF
527<osmChange>
528 <create>
529  <node id='-1' lon='0' lat='0' changeset='1'>
530   <tag k="foo" v="bar"/>
531  </node>
532 </create>
533 <modify>
534  <node id='-1' lon='1' lat='1' changeset='1' version='1'/>
535 </modify>
536 <delete>
537  <node id='-1' lon='2' lat='2' changeset='1' version='2'/>
538 </delete>
539</osmChange>
540EOF
541
542    # upload it
543    content diff
544    post :upload, :id => 1
545    assert_response :success, 
546      "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
547
548    # check the response is well-formed
549    assert_select "diffResult>node", 3
550    assert_select "diffResult>node[old_id=-1]", 3
551  end
552
553  ##
554  # test what happens if a diff upload re-uses placeholder IDs in an
555  # illegal way.
556  def test_upload_placeholder_invalid
557    basic_authorization "test@openstreetmap.org", "test"
558
559    diff = <<EOF
560<osmChange>
561 <create>
562  <node id='-1' lon='0' lat='0' changeset='1' version='1'/>
563  <node id='-1' lon='1' lat='1' changeset='1' version='1'/>
564  <node id='-1' lon='2' lat='2' changeset='1' version='2'/>
565 </create>
566</osmChange>
567EOF
568
569    # upload it
570    content diff
571    post :upload, :id => 1
572    assert_response :bad_request, 
573      "shouldn't be able to re-use placeholder IDs"
574  end
575
576  ##
577  # test what happens if a diff is uploaded containing only a node
578  # move.
579  def test_upload_node_move
580    basic_authorization "test@openstreetmap.org", "test"
581
582    content "<osm><changeset>" +
583      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
584      "</changeset></osm>"
585    put :create
586    assert_response :success
587    changeset_id = @response.body.to_i
588
589    old_node = current_nodes(:visible_node)
590
591    diff = XML::Document.new
592    diff.root = XML::Node.new "osmChange"
593    modify = XML::Node.new "modify"
594    xml_old_node = old_node.to_xml_node
595    xml_old_node["lat"] = (2.0).to_s
596    xml_old_node["lon"] = (2.0).to_s
597    xml_old_node["changeset"] = changeset_id.to_s
598    modify << xml_old_node
599    diff.root << modify
600
601    # upload it
602    content diff
603    post :upload, :id => changeset_id
604    assert_response :success, 
605      "diff should have uploaded OK"
606
607    # check the bbox
608    changeset = Changeset.find(changeset_id)
609    assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
610    assert_equal 2*SCALE, changeset.max_lon, "max_lon should be 2 degrees"
611    assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
612    assert_equal 2*SCALE, changeset.max_lat, "max_lat should be 2 degrees"
613  end
614
615  ##
616  # test what happens if a diff is uploaded adding a node to a way.
617  def test_upload_way_extend
618    basic_authorization "test@openstreetmap.org", "test"
619
620    content "<osm><changeset>" +
621      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
622      "</changeset></osm>"
623    put :create
624    assert_response :success
625    changeset_id = @response.body.to_i
626
627    old_way = current_ways(:visible_way)
628
629    diff = XML::Document.new
630    diff.root = XML::Node.new "osmChange"
631    modify = XML::Node.new "modify"
632    xml_old_way = old_way.to_xml_node
633    nd_ref = XML::Node.new "nd"
634    nd_ref["ref"] = current_nodes(:visible_node).id.to_s
635    xml_old_way << nd_ref
636    xml_old_way["changeset"] = changeset_id.to_s
637    modify << xml_old_way
638    diff.root << modify
639
640    # upload it
641    content diff
642    post :upload, :id => changeset_id
643    assert_response :success, 
644      "diff should have uploaded OK"
645
646    # check the bbox
647    changeset = Changeset.find(changeset_id)
648    assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
649    assert_equal 3*SCALE, changeset.max_lon, "max_lon should be 3 degrees"
650    assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
651    assert_equal 3*SCALE, changeset.max_lat, "max_lat should be 3 degrees"
652  end
653
654  ##
655  # test for more issues in #1568
656  def test_upload_empty_invalid
657    basic_authorization "test@openstreetmap.org", "test"
658
659    [ "<osmChange/>",
660      "<osmChange></osmChange>",
661      "<osmChange><modify/></osmChange>",
662      "<osmChange><modify></modify></osmChange>"
663    ].each do |diff|
664      # upload it
665      content diff
666      post :upload, :id => 1
667      assert_response(:success, "should be able to upload " +
668                      "empty changeset: " + diff)
669    end
670  end
671
672  ##
673  # when we make some simple changes we get the same changes back from the
674  # diff download.
675  def test_diff_download_simple
676    basic_authorization(users(:normal_user).email, "test")
677
678    # create a temporary changeset
679    content "<osm><changeset>" +
680      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
681      "</changeset></osm>"
682    put :create
683    assert_response :success
684    changeset_id = @response.body.to_i
685
686    # add a diff to it
687    diff = <<EOF
688<osmChange>
689 <modify>
690  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
691  <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
692  <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
693  <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
694  <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
695  <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
696  <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
697  <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
698 </modify>
699</osmChange>
700EOF
701
702    # upload it
703    content diff
704    post :upload, :id => changeset_id
705    assert_response :success, 
706      "can't upload multiple versions of an element in a diff: #{@response.body}"
707   
708    get :download, :id => changeset_id
709    assert_response :success
710
711    assert_select "osmChange", 1
712    assert_select "osmChange>modify", 8
713    assert_select "osmChange>modify>node", 8
714  end
715 
716  ##
717  # culled this from josm to ensure that nothing in the way that josm
718  # is formatting the request is causing it to fail.
719  #
720  # NOTE: the error turned out to be something else completely!
721  def test_josm_upload
722    basic_authorization(users(:normal_user).email, "test")
723
724    # create a temporary changeset
725    content "<osm><changeset>" +
726      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
727      "</changeset></osm>"
728    put :create
729    assert_response :success
730    changeset_id = @response.body.to_i
731
732    diff = <<OSM
733<osmChange version="0.6" generator="JOSM">
734<create version="0.6" generator="JOSM">
735  <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
736  <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
737  <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
738  <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
739  <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
740  <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
741  <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
742  <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
743  <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
744  <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
745    <nd ref='-1' />
746    <nd ref='-2' />
747    <nd ref='-3' />
748    <nd ref='-4' />
749    <nd ref='-5' />
750    <nd ref='-6' />
751    <nd ref='-7' />
752    <nd ref='-8' />
753    <nd ref='-9' />
754    <tag k='highway' v='residential' />
755    <tag k='name' v='Foobar Street' />
756  </way>
757</create>
758</osmChange>
759OSM
760
761    # upload it
762    content diff
763    post :upload, :id => changeset_id
764    assert_response :success, 
765      "can't upload a diff from JOSM: #{@response.body}"
766   
767    get :download, :id => changeset_id
768    assert_response :success
769
770    assert_select "osmChange", 1
771    assert_select "osmChange>create>node", 9
772    assert_select "osmChange>create>way", 1
773    assert_select "osmChange>create>way>nd", 9
774    assert_select "osmChange>create>way>tag", 2
775  end
776
777  ##
778  # when we make some complex changes we get the same changes back from the
779  # diff download.
780  def test_diff_download_complex
781    basic_authorization(users(:normal_user).email, "test")
782
783    # create a temporary changeset
784    content "<osm><changeset>" +
785      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
786      "</changeset></osm>"
787    put :create
788    assert_response :success
789    changeset_id = @response.body.to_i
790
791    # add a diff to it
792    diff = <<EOF
793<osmChange>
794 <delete>
795  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
796 </delete>
797 <create>
798  <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
799  <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
800  <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
801 </create>
802 <modify>
803  <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
804  <way id='1' changeset='#{changeset_id}' version='1'>
805   <nd ref='3'/>
806   <nd ref='-1'/>
807   <nd ref='-2'/>
808   <nd ref='-3'/>
809  </way>
810 </modify>
811</osmChange>
812EOF
813
814    # upload it
815    content diff
816    post :upload, :id => changeset_id
817    assert_response :success, 
818      "can't upload multiple versions of an element in a diff: #{@response.body}"
819   
820    get :download, :id => changeset_id
821    assert_response :success
822
823    assert_select "osmChange", 1
824    assert_select "osmChange>create", 3
825    assert_select "osmChange>delete", 1
826    assert_select "osmChange>modify", 2
827    assert_select "osmChange>create>node", 3
828    assert_select "osmChange>delete>node", 1 
829    assert_select "osmChange>modify>node", 1
830    assert_select "osmChange>modify>way", 1
831  end
832
833  ##
834  # check that the bounding box of a changeset gets updated correctly
835  def test_changeset_bbox
836    basic_authorization "test@openstreetmap.org", "test"
837
838    # create a new changeset
839    content "<osm><changeset/></osm>"
840    put :create
841    assert_response :success, "Creating of changeset failed."
842    changeset_id = @response.body.to_i
843   
844    # add a single node to it
845    with_controller(NodeController.new) do
846      content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
847      put :create
848      assert_response :success, "Couldn't create node."
849    end
850
851    # get the bounding box back from the changeset
852    get :read, :id => changeset_id
853    assert_response :success, "Couldn't read back changeset."
854    assert_select "osm>changeset[min_lon=1.0]", 1
855    assert_select "osm>changeset[max_lon=1.0]", 1
856    assert_select "osm>changeset[min_lat=2.0]", 1
857    assert_select "osm>changeset[max_lat=2.0]", 1
858
859    # add another node to it
860    with_controller(NodeController.new) do
861      content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
862      put :create
863      assert_response :success, "Couldn't create second node."
864    end
865
866    # get the bounding box back from the changeset
867    get :read, :id => changeset_id
868    assert_response :success, "Couldn't read back changeset for the second time."
869    assert_select "osm>changeset[min_lon=1.0]", 1
870    assert_select "osm>changeset[max_lon=2.0]", 1
871    assert_select "osm>changeset[min_lat=1.0]", 1
872    assert_select "osm>changeset[max_lat=2.0]", 1
873
874    # add (delete) a way to it, which contains a point at (3,3)
875    with_controller(WayController.new) do
876      content update_changeset(current_ways(:visible_way).to_xml,
877                               changeset_id)
878      put :delete, :id => current_ways(:visible_way).id
879      assert_response :success, "Couldn't delete a way."
880    end
881
882    # get the bounding box back from the changeset
883    get :read, :id => changeset_id
884    assert_response :success, "Couldn't read back changeset for the third time."
885    # note that the 3.1 here is because of the bbox overexpansion
886    assert_select "osm>changeset[min_lon=1.0]", 1
887    assert_select "osm>changeset[max_lon=3.1]", 1
888    assert_select "osm>changeset[min_lat=1.0]", 1
889    assert_select "osm>changeset[max_lat=3.1]", 1   
890  end
891
892  ##
893  # test that the changeset :include method works as it should
894  def test_changeset_include
895    basic_authorization "test@openstreetmap.org", "test"
896
897    # create a new changeset
898    content "<osm><changeset/></osm>"
899    put :create
900    assert_response :success, "Creating of changeset failed."
901    changeset_id = @response.body.to_i
902
903    # NOTE: the include method doesn't over-expand, like inserting
904    # a real method does. this is because we expect the client to
905    # know what it is doing!
906    check_after_include(changeset_id,  1,  1, [ 1,  1,  1,  1])
907    check_after_include(changeset_id,  3,  3, [ 1,  1,  3,  3])
908    check_after_include(changeset_id,  4,  2, [ 1,  1,  4,  3])
909    check_after_include(changeset_id,  2,  2, [ 1,  1,  4,  3])
910    check_after_include(changeset_id, -1, -1, [-1, -1,  4,  3])
911    check_after_include(changeset_id, -2,  5, [-2, -1,  4,  5])
912  end
913
914  ##
915  # test the query functionality of changesets
916  def test_query
917    get :query, :bbox => "-10,-10, 10, 10"
918    assert_response :success, "can't get changesets in bbox"
919    assert_changesets [1,4,6]
920
921    get :query, :bbox => "4.5,4.5,4.6,4.6"
922    assert_response :success, "can't get changesets in bbox"
923    assert_changesets [1]
924
925    # can't get changesets of user 1 without authenticating
926    get :query, :user => users(:normal_user).id
927    assert_response :not_found, "shouldn't be able to get changesets by non-public user"
928
929    # but this should work
930    basic_authorization "test@openstreetmap.org", "test"
931    get :query, :user => users(:normal_user).id
932    assert_response :success, "can't get changesets by user"
933    assert_changesets [1,3,4,6]
934
935    get :query, :user => users(:normal_user).id, :open => true
936    assert_response :success, "can't get changesets by user and open"
937    assert_changesets [1,4]
938
939    get :query, :time => '2007-12-31'
940    assert_response :success, "can't get changesets by time-since"
941    assert_changesets [1,2,4,5,6]
942
943    get :query, :time => '2008-01-01T12:34Z'
944    assert_response :success, "can't get changesets by time-since with hour"
945    assert_changesets [1,2,4,5,6]
946
947    get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
948    assert_response :success, "can't get changesets by time-range"
949    assert_changesets [1,4,5,6]
950
951    get :query, :open => 'true'
952    assert_response :success, "can't get changesets by open-ness"
953    assert_changesets [1,2,4]
954  end
955
956  ##
957  # check that errors are returned if garbage is inserted
958  # into query strings
959  def test_query_invalid
960    [ "abracadabra!",
961      "1,2,3,F",
962      ";drop table users;"
963      ].each do |bbox|
964      get :query, :bbox => bbox
965      assert_response :bad_request, "'#{bbox}' isn't a bbox"
966    end
967
968    [ "now()",
969      "00-00-00",
970      ";drop table users;",
971      ",",
972      "-,-"
973      ].each do |time|
974      get :query, :time => time
975      assert_response :bad_request, "'#{time}' isn't a valid time range"
976    end
977
978    [ "me",
979      "foobar",
980      "-1",
981      "0"
982      ].each do |uid|
983      get :query, :user => uid
984      assert_response :bad_request, "'#{uid}' isn't a valid user ID"
985    end
986  end
987
988  ##
989  # check updating tags on a changeset
990  def test_changeset_update
991    changeset = changesets(:normal_user_first_change)
992    new_changeset = changeset.to_xml
993    new_tag = XML::Node.new "tag"
994    new_tag['k'] = "tagtesting"
995    new_tag['v'] = "valuetesting"
996    new_changeset.find("//osm/changeset").first << new_tag
997    content new_changeset
998
999    # try without any authorization
1000    put :update, :id => changeset.id
1001    assert_response :unauthorized
1002
1003    # try with the wrong authorization
1004    basic_authorization "test@example.com", "test"
1005    put :update, :id => changeset.id
1006    assert_response :conflict
1007
1008    # now this should work...
1009    basic_authorization "test@openstreetmap.org", "test"
1010    put :update, :id => changeset.id
1011    assert_response :success
1012
1013    assert_select "osm>changeset[id=#{changeset.id}]", 1
1014    assert_select "osm>changeset>tag", 2
1015    assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
1016  end
1017 
1018  ##
1019  # check that a user different from the one who opened the changeset
1020  # can't modify it.
1021  def test_changeset_update_invalid
1022    basic_authorization "test@example.com", "test"
1023
1024    changeset = changesets(:normal_user_first_change)
1025    new_changeset = changeset.to_xml
1026    new_tag = XML::Node.new "tag"
1027    new_tag['k'] = "testing"
1028    new_tag['v'] = "testing"
1029    new_changeset.find("//osm/changeset").first << new_tag
1030
1031    content new_changeset
1032    put :update, :id => changeset.id
1033    assert_response :conflict
1034  end
1035
1036  ##
1037  # check that a changeset can contain a certain max number of changes.
1038  def test_changeset_limits
1039    basic_authorization "test@openstreetmap.org", "test"
1040
1041    # open a new changeset
1042    content "<osm><changeset/></osm>"
1043    put :create
1044    assert_response :success, "can't create a new changeset"
1045    cs_id = @response.body.to_i
1046
1047    # start the counter just short of where the changeset should finish.
1048    offset = 10
1049    # alter the database to set the counter on the changeset directly,
1050    # otherwise it takes about 6 minutes to fill all of them.
1051    changeset = Changeset.find(cs_id)
1052    changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1053    changeset.save!
1054
1055    with_controller(NodeController.new) do
1056      # create a new node
1057      content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1058      put :create
1059      assert_response :success, "can't create a new node"
1060      node_id = @response.body.to_i
1061
1062      get :read, :id => node_id
1063      assert_response :success, "can't read back new node"
1064      node_doc = XML::Parser.string(@response.body).parse
1065      node_xml = node_doc.find("//osm/node").first
1066
1067      # loop until we fill the changeset with nodes
1068      offset.times do |i|
1069        node_xml['lat'] = rand.to_s
1070        node_xml['lon'] = rand.to_s
1071        node_xml['version'] = (i+1).to_s
1072
1073        content node_doc
1074        put :update, :id => node_id
1075        assert_response :success, "attempt #{i} should have succeeded"
1076      end
1077
1078      # trying again should fail
1079      node_xml['lat'] = rand.to_s
1080      node_xml['lon'] = rand.to_s
1081      node_xml['version'] = offset.to_s
1082     
1083      content node_doc
1084      put :update, :id => node_id
1085      assert_response :conflict, "final attempt should have failed"
1086    end
1087
1088    changeset = Changeset.find(cs_id)
1089    assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1090
1091    # check that the changeset is now closed as well
1092    assert(!changeset.is_open?, 
1093           "changeset should have been auto-closed by exceeding " + 
1094           "element limit.")
1095  end
1096 
1097  # This should display the last 20 changesets closed.
1098  def test_list
1099    @changesets = Changeset.find(:all, :order => "created_at DESC", :conditions => ['min_lat IS NOT NULL'], :limit=> 20)
1100    assert @changesets.size <= 20
1101    get :list
1102    assert_response :success
1103    assert_template "list"
1104    # Now check that all 20 (or however many were returned) changesets are in the html
1105    assert_select "h1", :text => "Recent Changes", :count => 1
1106    assert_select "table[id='keyvalue'] tr", :count => @changesets.size + 1
1107    @changesets.each do |changeset|
1108      # FIXME this test needs rewriting - test for table contents
1109    end
1110  end
1111 
1112  #------------------------------------------------------------
1113  # utility functions
1114  #------------------------------------------------------------
1115
1116  ##
1117  # boilerplate for checking that certain changesets exist in the
1118  # output.
1119  def assert_changesets(ids)
1120    assert_select "osm>changeset", ids.size
1121    ids.each do |id|
1122      assert_select "osm>changeset[id=#{id}]", 1
1123    end
1124  end
1125
1126  ##
1127  # call the include method and assert properties of the bbox
1128  def check_after_include(changeset_id, lon, lat, bbox)
1129    content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1130    post :expand_bbox, :id => changeset_id
1131    assert_response :success, "Setting include of changeset failed: #{@response.body}"
1132
1133    # check exactly one changeset
1134    assert_select "osm>changeset", 1
1135    assert_select "osm>changeset[id=#{changeset_id}]", 1
1136
1137    # check the bbox
1138    doc = XML::Parser.string(@response.body).parse
1139    changeset = doc.find("//osm/changeset").first
1140    assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
1141    assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
1142    assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
1143    assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
1144  end
1145
1146  ##
1147  # update the changeset_id of a way element
1148  def update_changeset(xml, changeset_id)
1149    xml_attr_rewrite(xml, 'changeset', changeset_id)
1150  end
1151
1152  ##
1153  # update an attribute in a way element
1154  def xml_attr_rewrite(xml, name, value)
1155    xml.find("//osm/way").first[name] = value.to_s
1156    return xml
1157  end
1158
1159end
Note: See TracBrowser for help on using the repository browser.