4   class ChangesetsControllerTest < ActionDispatch::IntegrationTest
 
   6     # test all routes which lead to this controller
 
   9         { :path => "/api/0.6/changesets", :method => :get },
 
  10         { :controller => "api/changesets", :action => "index" }
 
  13         { :path => "/api/0.6/changesets.json", :method => :get },
 
  14         { :controller => "api/changesets", :action => "index", :format => "json" }
 
  17         { :path => "/api/0.6/changesets", :method => :post },
 
  18         { :controller => "api/changesets", :action => "create" }
 
  21         { :path => "/api/0.6/changeset/1", :method => :get },
 
  22         { :controller => "api/changesets", :action => "show", :id => "1" }
 
  25         { :path => "/api/0.6/changeset/1.json", :method => :get },
 
  26         { :controller => "api/changesets", :action => "show", :id => "1", :format => "json" }
 
  29         { :path => "/api/0.6/changeset/1", :method => :put },
 
  30         { :controller => "api/changesets", :action => "update", :id => "1" }
 
  33         { :path => "/api/0.6/changeset/1/upload", :method => :post },
 
  34         { :controller => "api/changesets", :action => "upload", :id => "1" }
 
  37         { :path => "/api/0.6/changeset/1/close", :method => :put },
 
  38         { :controller => "api/changesets", :action => "close", :id => "1" }
 
  42         { :controller => "api/changesets", :action => "create" },
 
  43         { :path => "/api/0.6/changeset/create", :method => :put }
 
  48     # test the query functionality of changesets
 
  50       private_user = create(:user, :data_public => false)
 
  51       private_user_changeset = create(:changeset, :user => private_user)
 
  52       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
 
  54       changeset = create(:changeset, :user => user)
 
  55       closed_changeset = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
 
  56       changeset2 = create(:changeset, :min_lat => (5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round, :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (15 * GeoRecord::SCALE).round)
 
  57       changeset3 = create(:changeset, :min_lat => (4.5 * GeoRecord::SCALE).round, :min_lon => (4.5 * GeoRecord::SCALE).round, :max_lat => (5 * GeoRecord::SCALE).round, :max_lon => (5 * GeoRecord::SCALE).round)
 
  59       get api_changesets_path(:bbox => "-10,-10, 10, 10")
 
  60       assert_response :success, "can't get changesets in bbox"
 
  61       assert_changesets_in_order [changeset3, changeset2]
 
  63       get api_changesets_path(:bbox => "4.5,4.5,4.6,4.6")
 
  64       assert_response :success, "can't get changesets in bbox"
 
  65       assert_changesets_in_order [changeset3]
 
  67       # not found when looking for changesets of non-existing users
 
  68       get api_changesets_path(:user => User.maximum(:id) + 1)
 
  69       assert_response :not_found
 
  70       assert_equal "text/plain", @response.media_type
 
  71       get api_changesets_path(:display_name => " ")
 
  72       assert_response :not_found
 
  73       assert_equal "text/plain", @response.media_type
 
  75       # can't get changesets of user 1 without authenticating
 
  76       get api_changesets_path(:user => private_user.id)
 
  77       assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
 
  78       get api_changesets_path(:display_name => private_user.display_name)
 
  79       assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
 
  81       # but this should work
 
  82       auth_header = bearer_authorization_header private_user
 
  83       get api_changesets_path(:user => private_user.id), :headers => auth_header
 
  84       assert_response :success, "can't get changesets by user ID"
 
  85       assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
 
  87       get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header
 
  88       assert_response :success, "can't get changesets by user name"
 
  89       assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
 
  92       get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
 
  93       assert_response :success, "can't get changesets by user name"
 
  95       js = ActiveSupport::JSON.decode(@response.body)
 
  98       assert_equal Settings.api_version, js["version"]
 
  99       assert_equal Settings.generator, js["generator"]
 
 100       assert_equal 2, js["changesets"].count
 
 102       # check that the correct error is given when we provide both UID and name
 
 103       get api_changesets_path(:user => private_user.id,
 
 104                               :display_name => private_user.display_name), :headers => auth_header
 
 105       assert_response :bad_request, "should be a bad request to have both ID and name specified"
 
 107       get api_changesets_path(:user => private_user.id, :open => true), :headers => auth_header
 
 108       assert_response :success, "can't get changesets by user and open"
 
 109       assert_changesets_in_order [private_user_changeset]
 
 111       get api_changesets_path(:time => "2007-12-31"), :headers => auth_header
 
 112       assert_response :success, "can't get changesets by time-since"
 
 113       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
 
 115       get api_changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
 
 116       assert_response :success, "can't get changesets by time-since with hour"
 
 117       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
 
 119       get api_changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
 
 120       assert_response :success, "can't get changesets by time-range"
 
 121       assert_changesets_in_order [closed_changeset]
 
 123       get api_changesets_path(:open => "true"), :headers => auth_header
 
 124       assert_response :success, "can't get changesets by open-ness"
 
 125       assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
 
 127       get api_changesets_path(:closed => "true"), :headers => auth_header
 
 128       assert_response :success, "can't get changesets by closed-ness"
 
 129       assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
 
 131       get api_changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
 
 132       assert_response :success, "can't get changesets by closed-ness and user"
 
 133       assert_changesets_in_order [private_user_closed_changeset]
 
 135       get api_changesets_path(:closed => "true", :user => user.id), :headers => auth_header
 
 136       assert_response :success, "can't get changesets by closed-ness and user"
 
 137       assert_changesets_in_order [closed_changeset]
 
 139       get api_changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
 
 140       assert_response :success, "can't get changesets by id (as comma-separated string)"
 
 141       assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
 
 143       get api_changesets_path(:changesets => ""), :headers => auth_header
 
 144       assert_response :bad_request, "should be a bad request since changesets is empty"
 
 148     # test the query functionality of changesets with the limit parameter
 
 151       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
 
 152       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
 
 153       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
 
 154       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
 
 155       changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
 
 157       get api_changesets_path
 
 158       assert_response :success
 
 159       assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
 
 161       get api_changesets_path(:limit => "3")
 
 162       assert_response :success
 
 163       assert_changesets_in_order [changeset5, changeset4, changeset3]
 
 165       get api_changesets_path(:limit => "0")
 
 166       assert_response :bad_request
 
 168       get api_changesets_path(:limit => Settings.max_changeset_query_limit)
 
 169       assert_response :success
 
 170       assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
 
 172       get api_changesets_path(:limit => Settings.max_changeset_query_limit + 1)
 
 173       assert_response :bad_request
 
 177     # test the query functionality of sequential changesets with order and time parameters
 
 180       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
 
 181       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
 
 182       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
 
 183       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
 
 184       changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
 
 185       changeset6 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 6, 1, 0, 0, 0), :closed_at => Time.utc(2008, 6, 2, 0, 0, 0))
 
 187       get api_changesets_path
 
 188       assert_response :success
 
 189       assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
 
 191       get api_changesets_path(:order => "oldest")
 
 192       assert_response :success
 
 193       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
 
 196         # lower time bound at the opening time of a changeset
 
 197         ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
 
 198         # lower time bound in the middle of a changeset
 
 199         ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
 
 200         # lower time bound at the closing time of a changeset
 
 201         ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
 
 202         # lower time bound after the closing time of a changeset
 
 203         ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
 
 204         # upper time bound in the middle of a changeset
 
 205         ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
 
 207         ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
 
 208       ].each do |from, to, interval_changesets, point_changesets|
 
 209         get api_changesets_path(:time => "#{from},#{to}")
 
 210         assert_response :success
 
 211         assert_changesets_in_order interval_changesets
 
 213         get api_changesets_path(:from => from, :to => to)
 
 214         assert_response :success
 
 215         assert_changesets_in_order point_changesets
 
 217         get api_changesets_path(:from => from, :to => to, :order => "oldest")
 
 218         assert_response :success
 
 219         assert_changesets_in_order point_changesets.reverse
 
 224     # test the query functionality of overlapping changesets with order and time parameters
 
 225     def test_index_order_overlapping
 
 227       changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 17, 0, 0), :closed_at => Time.utc(2015, 6, 4, 17, 0, 0))
 
 228       changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 16, 0, 0), :closed_at => Time.utc(2015, 6, 4, 18, 0, 0))
 
 229       changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 14, 0, 0), :closed_at => Time.utc(2015, 6, 4, 20, 0, 0))
 
 230       changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 3, 23, 0, 0), :closed_at => Time.utc(2015, 6, 4, 23, 0, 0))
 
 231       create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 2, 23, 0, 0), :closed_at => Time.utc(2015, 6, 3, 23, 0, 0))
 
 233       get api_changesets_path(:time => "2015-06-04T00:00:00Z")
 
 234       assert_response :success
 
 235       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
 
 237       get api_changesets_path(:from => "2015-06-04T00:00:00Z")
 
 238       assert_response :success
 
 239       assert_changesets_in_order [changeset1, changeset2, changeset3]
 
 241       get api_changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
 
 242       assert_response :success
 
 243       assert_changesets_in_order [changeset3, changeset2, changeset1]
 
 245       get api_changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
 
 246       assert_response :success
 
 247       assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
 
 249       get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
 
 250       assert_response :success
 
 251       assert_changesets_in_order [changeset1, changeset2]
 
 253       get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
 
 254       assert_response :success
 
 255       assert_changesets_in_order [changeset2, changeset1]
 
 259     # check that errors are returned if garbage is inserted
 
 261     def test_index_invalid
 
 264        ";drop table users;"].each do |bbox|
 
 265         get api_changesets_path(:bbox => bbox)
 
 266         assert_response :bad_request, "'#{bbox}' isn't a bbox"
 
 271        ";drop table users;",
 
 273        "-,-"].each do |time|
 
 274         get api_changesets_path(:time => time)
 
 275         assert_response :bad_request, "'#{time}' isn't a valid time range"
 
 282         get api_changesets_path(:user => uid)
 
 283         assert_response :bad_request, "'#{uid}' isn't a valid user ID"
 
 286       get api_changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
 
 287       assert_response :bad_request, "cannot use order=oldest with time"
 
 290     # -----------------------
 
 291     # Test simple changeset creation
 
 292     # -----------------------
 
 295       auth_header = bearer_authorization_header create(:user, :data_public => false)
 
 296       # Create the first user's changeset
 
 297       xml = "<osm><changeset>" \
 
 298             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
 300       post api_changesets_path, :params => xml, :headers => auth_header
 
 301       assert_require_public_data
 
 303       auth_header = bearer_authorization_header
 
 304       # Create the first user's changeset
 
 305       xml = "<osm><changeset>" \
 
 306             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
 308       post api_changesets_path, :params => xml, :headers => auth_header
 
 310       assert_response :success, "Creation of changeset did not return success status"
 
 311       newid = @response.body.to_i
 
 313       # check end time, should be an hour ahead of creation time
 
 314       cs = Changeset.find(newid)
 
 315       duration = cs.closed_at - cs.created_at
 
 316       # the difference can either be a rational, or a floating point number
 
 317       # of seconds, depending on the code path taken :-(
 
 318       if duration.instance_of?(Rational)
 
 319         assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
 
 321         # must be number of seconds...
 
 322         assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
 
 325       # checks if uploader was subscribed
 
 326       assert_equal 1, cs.subscribers.length
 
 329     def test_create_invalid
 
 330       auth_header = bearer_authorization_header create(:user, :data_public => false)
 
 331       xml = "<osm><changeset></osm>"
 
 332       post api_changesets_path, :params => xml, :headers => auth_header
 
 333       assert_require_public_data
 
 335       ## Try the public user
 
 336       auth_header = bearer_authorization_header
 
 337       xml = "<osm><changeset></osm>"
 
 338       post api_changesets_path, :params => xml, :headers => auth_header
 
 339       assert_response :bad_request, "creating a invalid changeset should fail"
 
 342     def test_create_invalid_no_content
 
 343       ## First check with no auth
 
 344       post api_changesets_path
 
 345       assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
 
 347       ## Now try to with a non-public user
 
 348       auth_header = bearer_authorization_header create(:user, :data_public => false)
 
 349       post api_changesets_path, :headers => auth_header
 
 350       assert_require_public_data
 
 352       ## Try an inactive user
 
 353       auth_header = bearer_authorization_header create(:user, :pending)
 
 354       post api_changesets_path, :headers => auth_header
 
 357       ## Now try to use a normal user
 
 358       auth_header = bearer_authorization_header
 
 359       post api_changesets_path, :headers => auth_header
 
 360       assert_response :bad_request, "creating a changeset with no content should fail"
 
 363     def test_create_wrong_method
 
 364       auth_header = bearer_authorization_header
 
 366       put api_changesets_path, :headers => auth_header
 
 367       assert_response :not_found
 
 368       assert_template "rescues/routing_error"
 
 371     def test_create_legacy_path
 
 372       auth_header = bearer_authorization_header
 
 373       xml = "<osm><changeset></changeset></osm>"
 
 375       assert_difference "Changeset.count", 1 do
 
 376         put "/api/0.6/changeset/create", :params => xml, :headers => auth_header
 
 379       assert_response :success, "Creation of changeset did not return success status"
 
 380       assert_equal Changeset.last.id, @response.body.to_i
 
 384     # check that the changeset can be shown and returns the correct
 
 385     # document structure.
 
 387       changeset = create(:changeset)
 
 389       get api_changeset_path(changeset)
 
 390       assert_response :success, "cannot get first changeset"
 
 392       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 393       assert_single_changeset changeset do
 
 394         assert_dom "> discussion", 0
 
 397       get api_changeset_path(changeset, :include_discussion => true)
 
 398       assert_response :success, "cannot get first changeset with comments"
 
 400       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 401       assert_single_changeset changeset do
 
 402         assert_dom "> discussion", 1
 
 403         assert_dom "> discussion > comment", 0
 
 407     def test_show_comments
 
 408       # all comments visible
 
 409       changeset = create(:changeset, :closed)
 
 410       comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
 
 412       get api_changeset_path(changeset, :include_discussion => true)
 
 413       assert_response :success, "cannot get closed changeset with comments"
 
 415       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 416         assert_single_changeset changeset do
 
 417           assert_dom "> discussion", 1 do
 
 418             assert_dom "> comment", 3 do |dom_comments|
 
 419               assert_dom dom_comments[0], "> @id", comment1.id.to_s
 
 420               assert_dom dom_comments[0], "> @visible", "true"
 
 421               assert_dom dom_comments[1], "> @id", comment2.id.to_s
 
 422               assert_dom dom_comments[1], "> @visible", "true"
 
 423               assert_dom dom_comments[2], "> @id", comment3.id.to_s
 
 424               assert_dom dom_comments[2], "> @visible", "true"
 
 430       # one hidden comment not included because not asked for
 
 431       comment2.update(:visible => false)
 
 434       get api_changeset_path(changeset, :include_discussion => true)
 
 435       assert_response :success, "cannot get closed changeset with comments"
 
 437       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 438       assert_single_changeset changeset do
 
 439         assert_dom "> discussion", 1 do
 
 440           assert_dom "> comment", 2 do |dom_comments|
 
 441             assert_dom dom_comments[0], "> @id", comment1.id.to_s
 
 442             assert_dom dom_comments[0], "> @visible", "true"
 
 443             assert_dom dom_comments[1], "> @id", comment3.id.to_s
 
 444             assert_dom dom_comments[1], "> @visible", "true"
 
 449       # one hidden comment not included because no permissions
 
 450       get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true)
 
 451       assert_response :success, "cannot get closed changeset with comments"
 
 453       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 454       assert_single_changeset changeset do
 
 455         assert_dom "> discussion", 1 do
 
 456           assert_dom "> comment", 2 do |dom_comments|
 
 457             assert_dom dom_comments[0], "> @id", comment1.id.to_s
 
 458             assert_dom dom_comments[0], "> @visible", "true"
 
 459             # maybe will show an empty comment element with visible=false in the future
 
 460             assert_dom dom_comments[1], "> @id", comment3.id.to_s
 
 461             assert_dom dom_comments[1], "> @visible", "true"
 
 466       # one hidden comment shown to moderators
 
 467       moderator_user = create(:moderator_user)
 
 468       auth_header = bearer_authorization_header moderator_user
 
 469       get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
 
 470       assert_response :success, "cannot get closed changeset with comments"
 
 472       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 473       assert_single_changeset changeset do
 
 474         assert_dom "> discussion", 1 do
 
 475           assert_dom "> comment", 3 do |dom_comments|
 
 476             assert_dom dom_comments[0], "> @id", comment1.id.to_s
 
 477             assert_dom dom_comments[0], "> @visible", "true"
 
 478             assert_dom dom_comments[1], "> @id", comment2.id.to_s
 
 479             assert_dom dom_comments[1], "> @visible", "false"
 
 480             assert_dom dom_comments[2], "> @id", comment3.id.to_s
 
 481             assert_dom dom_comments[2], "> @visible", "true"
 
 488       changeset = create(:changeset, :closed)
 
 489       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
 
 490       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
 
 492       get api_changeset_path(changeset)
 
 494       assert_response :success
 
 495       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 496       assert_single_changeset changeset do
 
 497         assert_dom "> tag", 2
 
 498         assert_dom "> tag[k='created_by'][v='JOSM/1.5 (18364)']", 1
 
 499         assert_dom "> tag[k='comment'][v='changeset comment']", 1
 
 504       changeset = create(:changeset)
 
 506       get api_changeset_path(changeset, :format => "json")
 
 507       assert_response :success, "cannot get first changeset"
 
 509       js = ActiveSupport::JSON.decode(@response.body)
 
 512       assert_equal Settings.api_version, js["version"]
 
 513       assert_equal Settings.generator, js["generator"]
 
 514       assert_single_changeset_json changeset, js
 
 515       assert_nil js["changeset"]["tags"]
 
 516       assert_nil js["changeset"]["comments"]
 
 517       assert_equal changeset.user.id, js["changeset"]["uid"]
 
 518       assert_equal changeset.user.display_name, js["changeset"]["user"]
 
 520       get api_changeset_path(changeset, :format => "json", :include_discussion => true)
 
 521       assert_response :success, "cannot get first changeset with comments"
 
 523       js = ActiveSupport::JSON.decode(@response.body)
 
 525       assert_equal Settings.api_version, js["version"]
 
 526       assert_equal Settings.generator, js["generator"]
 
 527       assert_single_changeset_json changeset, js
 
 528       assert_nil js["changeset"]["tags"]
 
 529       assert_nil js["changeset"]["min_lat"]
 
 530       assert_nil js["changeset"]["min_lon"]
 
 531       assert_nil js["changeset"]["max_lat"]
 
 532       assert_nil js["changeset"]["max_lon"]
 
 533       assert_equal 0, js["changeset"]["comments"].count
 
 536     def test_show_comments_json
 
 537       # all comments visible
 
 538       changeset = create(:changeset, :closed)
 
 539       comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
 
 541       get api_changeset_path(changeset, :format => "json", :include_discussion => true)
 
 542       assert_response :success, "cannot get closed changeset with comments"
 
 544       js = ActiveSupport::JSON.decode(@response.body)
 
 546       assert_equal Settings.api_version, js["version"]
 
 547       assert_equal Settings.generator, js["generator"]
 
 548       assert_single_changeset_json changeset, js
 
 549       assert_equal 3, js["changeset"]["comments"].count
 
 550       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
 
 551       assert js["changeset"]["comments"][0]["visible"]
 
 552       assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
 
 553       assert js["changeset"]["comments"][1]["visible"]
 
 554       assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
 
 555       assert js["changeset"]["comments"][2]["visible"]
 
 557       # one hidden comment not included because not asked for
 
 558       comment1.update(:visible => false)
 
 561       get api_changeset_path(changeset, :format => "json", :include_discussion => true)
 
 562       assert_response :success, "cannot get closed changeset with comments"
 
 564       js = ActiveSupport::JSON.decode(@response.body)
 
 566       assert_equal Settings.api_version, js["version"]
 
 567       assert_equal Settings.generator, js["generator"]
 
 568       assert_single_changeset_json changeset, js
 
 569       assert_equal 2, js["changeset"]["comments"].count
 
 570       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
 
 571       assert js["changeset"]["comments"][0]["visible"]
 
 572       assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
 
 573       assert js["changeset"]["comments"][1]["visible"]
 
 575       # one hidden comment not included because no permissions
 
 576       get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true)
 
 577       assert_response :success, "cannot get closed changeset with comments"
 
 579       js = ActiveSupport::JSON.decode(@response.body)
 
 581       assert_equal Settings.api_version, js["version"]
 
 582       assert_equal Settings.generator, js["generator"]
 
 583       assert_single_changeset_json changeset, js
 
 584       assert_equal 2, js["changeset"]["comments"].count
 
 585       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
 
 586       assert js["changeset"]["comments"][0]["visible"]
 
 587       # maybe will show an empty comment element with visible=false in the future
 
 588       assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
 
 589       assert js["changeset"]["comments"][1]["visible"]
 
 591       # one hidden comment shown to moderators
 
 592       moderator_user = create(:moderator_user)
 
 593       auth_header = bearer_authorization_header moderator_user
 
 594       get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
 
 595       assert_response :success, "cannot get closed changeset with comments"
 
 597       js = ActiveSupport::JSON.decode(@response.body)
 
 599       assert_equal Settings.api_version, js["version"]
 
 600       assert_equal Settings.generator, js["generator"]
 
 601       assert_single_changeset_json changeset, js
 
 602       assert_equal 3, js["changeset"]["comments"].count
 
 603       assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
 
 604       assert js["changeset"]["comments"][0]["visible"]
 
 605       assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
 
 606       assert_not js["changeset"]["comments"][1]["visible"]
 
 607       assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
 
 608       assert js["changeset"]["comments"][2]["visible"]
 
 611     def test_show_tags_json
 
 612       changeset = create(:changeset, :closed)
 
 613       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
 
 614       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
 
 616       get api_changeset_path(changeset, :format => "json")
 
 618       assert_response :success
 
 619       js = ActiveSupport::JSON.decode(@response.body)
 
 621       assert_equal Settings.api_version, js["version"]
 
 622       assert_equal Settings.generator, js["generator"]
 
 623       assert_single_changeset_json changeset, js
 
 624       assert_equal 2, js["changeset"]["tags"].count
 
 625       assert_equal "JOSM/1.5 (18364)", js["changeset"]["tags"]["created_by"]
 
 626       assert_equal "changeset comment", js["changeset"]["tags"]["comment"]
 
 629     def test_show_bbox_json
 
 630       # test bbox attribute
 
 631       changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
 
 632                                      :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
 
 634       get api_changeset_path(changeset, :format => "json")
 
 635       assert_response :success, "cannot get first changeset"
 
 637       js = ActiveSupport::JSON.decode(@response.body)
 
 639       assert_equal(-5, js["changeset"]["min_lat"])
 
 640       assert_equal 5, js["changeset"]["min_lon"]
 
 641       assert_equal 15, js["changeset"]["max_lat"]
 
 642       assert_equal 12, js["changeset"]["max_lon"]
 
 646     # check that a changeset that doesn't exist returns an appropriate message
 
 647     def test_show_not_found
 
 648       [0, -32, 233455644, "afg", "213"].each do |id|
 
 649         get api_changeset_path(id)
 
 650         assert_response :not_found, "should get a not found"
 
 651       rescue ActionController::UrlGenerationError => e
 
 652         assert_match(/No route matches/, e.to_s)
 
 657     # test that the user who opened a change can close it
 
 659       private_user = create(:user, :data_public => false)
 
 660       private_changeset = create(:changeset, :user => private_user)
 
 662       changeset = create(:changeset, :user => user)
 
 664       ## Try without authentication
 
 665       put changeset_close_path(changeset)
 
 666       assert_response :unauthorized
 
 668       ## Try using the non-public user
 
 669       auth_header = bearer_authorization_header private_user
 
 670       put changeset_close_path(private_changeset), :headers => auth_header
 
 671       assert_require_public_data
 
 673       ## The try with the public user
 
 674       auth_header = bearer_authorization_header user
 
 677       put changeset_close_path(cs_id), :headers => auth_header
 
 678       assert_response :success
 
 680       # test that it really is closed now
 
 681       cs = Changeset.find(changeset.id)
 
 683                  "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
 
 687     # test that a different user can't close another user's changeset
 
 688     def test_close_invalid
 
 690       changeset = create(:changeset)
 
 692       auth_header = bearer_authorization_header user
 
 694       put changeset_close_path(changeset), :headers => auth_header
 
 695       assert_response :conflict
 
 696       assert_equal "The user doesn't own that changeset", @response.body
 
 700     # test that you can't close using another method
 
 701     def test_close_method_invalid
 
 703       changeset = create(:changeset, :user => user)
 
 705       auth_header = bearer_authorization_header user
 
 707       get changeset_close_path(changeset), :headers => auth_header
 
 708       assert_response :not_found
 
 709       assert_template "rescues/routing_error"
 
 711       post changeset_close_path(changeset), :headers => auth_header
 
 712       assert_response :not_found
 
 713       assert_template "rescues/routing_error"
 
 717     # check that you can't close a changeset that isn't found
 
 718     def test_close_not_found
 
 719       cs_ids = [0, -132, "123"]
 
 721       # First try to do it with no auth
 
 723         put changeset_close_path(id)
 
 724         assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
 
 725       rescue ActionController::UrlGenerationError => e
 
 726         assert_match(/No route matches/, e.to_s)
 
 730       auth_header = bearer_authorization_header
 
 732         put changeset_close_path(id), :headers => auth_header
 
 733         assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
 
 734       rescue ActionController::UrlGenerationError => e
 
 735         assert_match(/No route matches/, e.to_s)
 
 740     # upload something simple, but valid and check that it can
 
 742     # Also try without auth and another user.
 
 743     def test_upload_simple_valid
 
 744       private_user = create(:user, :data_public => false)
 
 745       private_changeset = create(:changeset, :user => private_user)
 
 747       changeset = create(:changeset, :user => user)
 
 751       relation = create(:relation)
 
 752       other_relation = create(:relation)
 
 753       # create some tags, since we test that they are removed later
 
 754       create(:node_tag, :node => node)
 
 755       create(:way_tag, :way => way)
 
 756       create(:relation_tag, :relation => relation)
 
 759       changeset_id = changeset.id
 
 761       # simple diff to change a node, way and relation by removing
 
 766           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 767           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 768            <nd ref='#{node.id}'/>
 
 772           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 773            <member type='way' role='some' ref='#{way.id}'/>
 
 774            <member type='node' role='some' ref='#{node.id}'/>
 
 775            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 782       post changeset_upload_path(changeset), :params => diff
 
 783       assert_response :unauthorized,
 
 784                       "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
 
 786       ## Now try with a private user
 
 787       auth_header = bearer_authorization_header private_user
 
 788       changeset_id = private_changeset.id
 
 790       # simple diff to change a node, way and relation by removing
 
 795           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 796           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 797            <nd ref='#{node.id}'/>
 
 801           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 802            <member type='way' role='some' ref='#{way.id}'/>
 
 803            <member type='node' role='some' ref='#{node.id}'/>
 
 804            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 811       post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
 
 812       assert_response :forbidden,
 
 813                       "can't upload a simple valid diff to changeset: #{@response.body}"
 
 815       ## Now try with the public user
 
 816       auth_header = bearer_authorization_header user
 
 817       changeset_id = changeset.id
 
 819       # simple diff to change a node, way and relation by removing
 
 824           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 825           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 826            <nd ref='#{node.id}'/>
 
 830           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 831            <member type='way' role='some' ref='#{way.id}'/>
 
 832            <member type='node' role='some' ref='#{node.id}'/>
 
 833            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 840       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 841       assert_response :success,
 
 842                       "can't upload a simple valid diff to changeset: #{@response.body}"
 
 844       # check that the changes made it into the database
 
 845       assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
 
 846       assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
 
 847       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
 
 851     # upload something which creates new objects using placeholders
 
 852     def test_upload_create_valid
 
 854       changeset = create(:changeset, :user => user)
 
 856       way = create(:way_with_nodes, :nodes_count => 2)
 
 857       relation = create(:relation)
 
 859       auth_header = bearer_authorization_header user
 
 861       # simple diff to create a node way and relation using placeholders
 
 865           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 866            <tag k='foo' v='bar'/>
 
 867            <tag k='baz' v='bat'/>
 
 869           <way id='-1' changeset='#{changeset.id}'>
 
 870            <nd ref='#{node.id}'/>
 
 874           <relation id='-1' changeset='#{changeset.id}'>
 
 875            <member type='way' role='some' ref='#{way.id}'/>
 
 876            <member type='node' role='some' ref='#{node.id}'/>
 
 877            <member type='relation' role='some' ref='#{relation.id}'/>
 
 884       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 885       assert_response :success,
 
 886                       "can't upload a simple valid creation to changeset: #{@response.body}"
 
 888       # check the returned payload
 
 889       new_node_id, new_way_id, new_rel_id = nil
 
 890       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 891         # inspect the response to find out what the new element IDs are
 
 892         # check the old IDs are all present and negative one
 
 893         # check the versions are present and equal one
 
 894         assert_dom "> node", 1 do |(node_el)|
 
 895           new_node_id = node_el["new_id"].to_i
 
 896           assert_dom "> @old_id", "-1"
 
 897           assert_dom "> @new_version", "1"
 
 899         assert_dom "> way", 1 do |(way_el)|
 
 900           new_way_id = way_el["new_id"].to_i
 
 901           assert_dom "> @old_id", "-1"
 
 902           assert_dom "> @new_version", "1"
 
 904         assert_dom "> relation", 1 do |(rel_el)|
 
 905           new_rel_id = rel_el["new_id"].to_i
 
 906           assert_dom "> @old_id", "-1"
 
 907           assert_dom "> @new_version", "1"
 
 911       # check that the changes made it into the database
 
 912       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
 913       assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
 
 914       assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
 
 918     # test a complex delete where we delete elements which rely on eachother
 
 919     # in the same transaction.
 
 920     def test_upload_delete
 
 921       changeset = create(:changeset)
 
 922       super_relation = create(:relation)
 
 923       used_relation = create(:relation)
 
 924       used_way = create(:way)
 
 925       used_node = create(:node)
 
 926       create(:relation_member, :relation => super_relation, :member => used_relation)
 
 927       create(:relation_member, :relation => super_relation, :member => used_way)
 
 928       create(:relation_member, :relation => super_relation, :member => used_node)
 
 930       auth_header = bearer_authorization_header changeset.user
 
 932       diff = XML::Document.new
 
 933       diff.root = XML::Node.new "osmChange"
 
 934       delete = XML::Node.new "delete"
 
 936       delete << xml_node_for_relation(super_relation)
 
 937       delete << xml_node_for_relation(used_relation)
 
 938       delete << xml_node_for_way(used_way)
 
 939       delete << xml_node_for_node(used_node)
 
 941       # update the changeset to one that this user owns
 
 942       %w[node way relation].each do |type|
 
 943         delete.find("//osmChange/delete/#{type}").each do |n|
 
 944           n["changeset"] = changeset.id.to_s
 
 949       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 950       assert_response :success,
 
 951                       "can't upload a deletion diff to changeset: #{@response.body}"
 
 953       # check the response is well-formed
 
 954       assert_select "diffResult>node", 1
 
 955       assert_select "diffResult>way", 1
 
 956       assert_select "diffResult>relation", 2
 
 958       # check that everything was deleted
 
 959       assert_not Node.find(used_node.id).visible
 
 960       assert_not Way.find(used_way.id).visible
 
 961       assert_not Relation.find(super_relation.id).visible
 
 962       assert_not Relation.find(used_relation.id).visible
 
 966     # test uploading a delete with no lat/lon, as they are optional in
 
 967     # the osmChange spec.
 
 968     def test_upload_nolatlon_delete
 
 970       changeset = create(:changeset)
 
 972       auth_header = bearer_authorization_header changeset.user
 
 973       diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
 
 976       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 977       assert_response :success,
 
 978                       "can't upload a deletion diff to changeset: #{@response.body}"
 
 980       # check the response is well-formed
 
 981       assert_select "diffResult>node", 1
 
 983       # check that everything was deleted
 
 984       assert_not Node.find(node.id).visible
 
 987     def test_repeated_changeset_create
 
 989         auth_header = bearer_authorization_header
 
 991         # create a temporary changeset
 
 992         xml = "<osm><changeset>" \
 
 993               "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
 995         assert_difference "Changeset.count", 1 do
 
 996           post api_changesets_path, :params => xml, :headers => auth_header
 
 998         assert_response :success
 
1002     def test_upload_large_changeset
 
1003       user = create(:user)
 
1004       auth_header = bearer_authorization_header user
 
1006       # create an old changeset to ensure we have the maximum rate limit
 
1007       create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
 
1009       # create a changeset
 
1010       post api_changesets_path, :params => "<osm><changeset/></osm>", :headers => auth_header
 
1011       assert_response :success, "Should be able to create a changeset: #{@response.body}"
 
1012       changeset_id = @response.body.to_i
 
1014       # upload some widely-spaced nodes, spiralling positive and negative
 
1018           <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
 
1019           <node id='-10' lon='20'  lat='10' changeset='#{changeset_id}'/>
 
1020           <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
 
1021           <node id='-11' lon='40'  lat='20' changeset='#{changeset_id}'/>
 
1022           <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
 
1023           <node id='-12' lon='60'  lat='30' changeset='#{changeset_id}'/>
 
1024           <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
 
1025           <node id='-13' lon='80'  lat='40' changeset='#{changeset_id}'/>
 
1026           <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
 
1027           <node id='-14' lon='100'  lat='50' changeset='#{changeset_id}'/>
 
1028           <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
 
1029           <node id='-15' lon='120'  lat='60' changeset='#{changeset_id}'/>
 
1030           <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
 
1031           <node id='-16' lon='140'  lat='70' changeset='#{changeset_id}'/>
 
1032           <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
 
1033           <node id='-17' lon='160'  lat='80' changeset='#{changeset_id}'/>
 
1034           <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
 
1035           <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset_id}'/>
 
1040       # upload it, which used to cause an error like "PGError: ERROR:
 
1041       # integer out of range" (bug #2152). but shouldn't any more.
 
1042       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
 
1043       assert_response :success,
 
1044                       "can't upload a spatially-large diff to changeset: #{@response.body}"
 
1046       # check that the changeset bbox is within bounds
 
1047       cs = Changeset.find(changeset_id)
 
1048       assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
 
1049       assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
 
1050       assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
 
1051       assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
 
1055     # test that deleting stuff in a transaction doesn't bypass the checks
 
1056     # to ensure that used elements are not deleted.
 
1057     def test_upload_delete_invalid
 
1058       changeset = create(:changeset)
 
1059       relation = create(:relation)
 
1060       other_relation = create(:relation)
 
1061       used_way = create(:way)
 
1062       used_node = create(:node)
 
1063       create(:relation_member, :relation => relation, :member => used_way)
 
1064       create(:relation_member, :relation => relation, :member => used_node)
 
1066       auth_header = bearer_authorization_header changeset.user
 
1068       diff = XML::Document.new
 
1069       diff.root = XML::Node.new "osmChange"
 
1070       delete = XML::Node.new "delete"
 
1072       delete << xml_node_for_relation(other_relation)
 
1073       delete << xml_node_for_way(used_way)
 
1074       delete << xml_node_for_node(used_node)
 
1076       # update the changeset to one that this user owns
 
1077       %w[node way relation].each do |type|
 
1078         delete.find("//osmChange/delete/#{type}").each do |n|
 
1079           n["changeset"] = changeset.id.to_s
 
1084       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
1085       assert_response :precondition_failed,
 
1086                       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
 
1087       assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
 
1089       # check that nothing was, in fact, deleted
 
1090       assert Node.find(used_node.id).visible
 
1091       assert Way.find(used_way.id).visible
 
1092       assert Relation.find(relation.id).visible
 
1093       assert Relation.find(other_relation.id).visible
 
1097     # test that a conditional delete of an in use object works.
 
1098     def test_upload_delete_if_unused
 
1099       changeset = create(:changeset)
 
1100       super_relation = create(:relation)
 
1101       used_relation = create(:relation)
 
1102       used_way = create(:way)
 
1103       used_node = create(:node)
 
1104       create(:relation_member, :relation => super_relation, :member => used_relation)
 
1105       create(:relation_member, :relation => super_relation, :member => used_way)
 
1106       create(:relation_member, :relation => super_relation, :member => used_node)
 
1108       auth_header = bearer_authorization_header changeset.user
 
1110       diff = XML::Document.new
 
1111       diff.root = XML::Node.new "osmChange"
 
1112       delete = XML::Node.new "delete"
 
1114       delete["if-unused"] = ""
 
1115       delete << xml_node_for_relation(used_relation)
 
1116       delete << xml_node_for_way(used_way)
 
1117       delete << xml_node_for_node(used_node)
 
1119       # update the changeset to one that this user owns
 
1120       %w[node way relation].each do |type|
 
1121         delete.find("//osmChange/delete/#{type}").each do |n|
 
1122           n["changeset"] = changeset.id.to_s
 
1127       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
1128       assert_response :success,
 
1129                       "can't do a conditional delete of in use objects: #{@response.body}"
 
1131       # check the returned payload
 
1132       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
1133         # check the old IDs are all present and what we expect
 
1134         # check the new IDs are all present and unchanged
 
1135         # check the new versions are all present and unchanged
 
1136         assert_dom "> node", 1 do
 
1137           assert_dom "> @old_id", used_node.id.to_s
 
1138           assert_dom "> @new_id", used_node.id.to_s
 
1139           assert_dom "> @new_version", used_node.version.to_s
 
1141         assert_dom "> way", 1 do
 
1142           assert_dom "> @old_id", used_way.id.to_s
 
1143           assert_dom "> @new_id", used_way.id.to_s
 
1144           assert_dom "> @new_version", used_way.version.to_s
 
1146         assert_dom "> relation", 1 do
 
1147           assert_dom "> @old_id", used_relation.id.to_s
 
1148           assert_dom "> @new_id", used_relation.id.to_s
 
1149           assert_dom "> @new_version", used_relation.version.to_s
 
1153       # check that nothing was, in fact, deleted
 
1154       assert Node.find(used_node.id).visible
 
1155       assert Way.find(used_way.id).visible
 
1156       assert Relation.find(used_relation.id).visible
 
1160     # upload an element with a really long tag value
 
1161     def test_upload_invalid_too_long_tag
 
1162       changeset = create(:changeset)
 
1164       auth_header = bearer_authorization_header changeset.user
 
1166       # simple diff to create a node way and relation using placeholders
 
1170           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1171            <tag k='foo' v='#{'x' * 256}'/>
 
1178       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1179       assert_response :bad_request,
 
1180                       "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
 
1184     # upload something which creates new objects and inserts them into
 
1185     # existing containers using placeholders.
 
1186     def test_upload_complex
 
1188       node = create(:node)
 
1189       relation = create(:relation)
 
1190       create(:way_node, :way => way, :node => node)
 
1192       changeset = create(:changeset)
 
1194       auth_header = bearer_authorization_header changeset.user
 
1196       # simple diff to create a node way and relation using placeholders
 
1200           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1201            <tag k='foo' v='bar'/>
 
1202            <tag k='baz' v='bat'/>
 
1206           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
1208            <nd ref='#{node.id}'/>
 
1210           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
1211            <member type='way' role='some' ref='#{way.id}'/>
 
1212            <member type='node' role='some' ref='-1'/>
 
1213            <member type='relation' role='some' ref='#{relation.id}'/>
 
1220       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1221       assert_response :success,
 
1222                       "can't upload a complex diff to changeset: #{@response.body}"
 
1224       # check the returned payload
 
1226       assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
1227         assert_dom "> node", 1 do |(node_el)|
 
1228           new_node_id = node_el["new_id"].to_i
 
1230         assert_dom "> way", 1
 
1231         assert_dom "> relation", 1
 
1234       # check that the changes made it into the database
 
1235       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
1236       assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
 
1237       Relation.find(relation.id).members.each do |type, id, _role|
 
1238         assert_equal new_node_id, id, "relation should contain new node" if type == "node"
 
1243     # create a diff which references several changesets, which should cause
 
1244     # a rollback and none of the diff gets committed
 
1245     def test_upload_invalid_changesets
 
1246       changeset = create(:changeset)
 
1247       other_changeset = create(:changeset, :user => changeset.user)
 
1248       node = create(:node)
 
1250       relation = create(:relation)
 
1251       other_relation = create(:relation)
 
1253       auth_header = bearer_authorization_header changeset.user
 
1255       # simple diff to create a node way and relation using placeholders
 
1259           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1260           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
1261            <nd ref='#{node.id}'/>
 
1265           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
1266            <member type='way' role='some' ref='#{way.id}'/>
 
1267            <member type='node' role='some' ref='#{node.id}'/>
 
1268            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
1272           <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
 
1273            <tag k='foo' v='bar'/>
 
1274            <tag k='baz' v='bat'/>
 
1281       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1282       assert_response :conflict,
 
1283                       "uploading a diff with multiple changesets should have failed"
 
1285       # check that objects are unmodified
 
1286       assert_nodes_are_equal(node, Node.find(node.id))
 
1287       assert_ways_are_equal(way, Way.find(way.id))
 
1288       assert_relations_are_equal(relation, Relation.find(relation.id))
 
1292     # upload multiple versions of the same element in the same diff.
 
1293     def test_upload_multiple_valid
 
1294       node = create(:node)
 
1295       changeset = create(:changeset)
 
1296       auth_header = bearer_authorization_header changeset.user
 
1298       # change the location of a node multiple times, each time referencing
 
1299       # the last version. doesn't this depend on version numbers being
 
1304           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
 
1305           <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
 
1306           <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
 
1307           <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
 
1308           <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
 
1309           <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
 
1310           <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
 
1311           <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
 
1317       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1318       assert_response :success,
 
1319                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
1321       # check the response is well-formed. its counter-intuitive, but the
 
1322       # API will return multiple elements with the same ID and different
 
1323       # version numbers for each change we made.
 
1324       assert_select "diffResult>node", 8
 
1328     # upload multiple versions of the same element in the same diff, but
 
1329     # keep the version numbers the same.
 
1330     def test_upload_multiple_duplicate
 
1331       node = create(:node)
 
1332       changeset = create(:changeset)
 
1334       auth_header = bearer_authorization_header changeset.user
 
1339           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1340           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1346       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1347       assert_response :conflict,
 
1348                       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
 
1352     # try to upload some elements without specifying the version
 
1353     def test_upload_missing_version
 
1354       changeset = create(:changeset)
 
1356       auth_header = bearer_authorization_header changeset.user
 
1361          <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
 
1367       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1368       assert_response :bad_request,
 
1369                       "shouldn't be able to upload an element without version: #{@response.body}"
 
1373     # try to upload with commands other than create, modify, or delete
 
1374     def test_action_upload_invalid
 
1375       changeset = create(:changeset)
 
1377       auth_header = bearer_authorization_header changeset.user
 
1382            <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
 
1386       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1387       assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
 
1388       assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
 
1392     # upload a valid changeset which has a mixture of whitespace
 
1393     # to check a bug reported by ivansanchez (#1565).
 
1394     def test_upload_whitespace_valid
 
1395       changeset = create(:changeset)
 
1396       node = create(:node)
 
1397       way = create(:way_with_nodes, :nodes_count => 2)
 
1398       relation = create(:relation)
 
1399       other_relation = create(:relation)
 
1400       create(:relation_tag, :relation => relation)
 
1402       auth_header = bearer_authorization_header changeset.user
 
1406          <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
 
1408           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
 
1410          <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
 
1411            type='way' role='some' ref='#{way.id}'/><member
 
1412             type='node' role='some' ref='#{node.id}'/>
 
1413            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
1415          </modify></osmChange>
 
1419       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1420       assert_response :success,
 
1421                       "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
 
1423       # check the response is well-formed
 
1424       assert_select "diffResult>node", 2
 
1425       assert_select "diffResult>relation", 1
 
1427       # check that the changes made it into the database
 
1428       assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
 
1429       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
 
1433     # test that a placeholder can be reused within the same upload.
 
1434     def test_upload_reuse_placeholder_valid
 
1435       changeset = create(:changeset)
 
1437       auth_header = bearer_authorization_header changeset.user
 
1442           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1443            <tag k="foo" v="bar"/>
 
1447           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1450           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
 
1456       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1457       assert_response :success,
 
1458                       "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
 
1460       # check the response is well-formed
 
1461       assert_select "diffResult>node", 3
 
1462       assert_select "diffResult>node[old_id='-1']", 3
 
1466     # test what happens if a diff upload re-uses placeholder IDs in an
 
1468     def test_upload_placeholder_invalid
 
1469       changeset = create(:changeset)
 
1471       auth_header = bearer_authorization_header changeset.user
 
1476           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1477           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1478           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
 
1484       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1485       assert_response :bad_request,
 
1486                       "shouldn't be able to re-use placeholder IDs"
 
1488       # placeholder_ids must be unique across all action blocks
 
1492           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1495           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1501       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1502       assert_response :bad_request,
 
1503                       "shouldn't be able to re-use placeholder IDs"
 
1506     def test_upload_process_order
 
1507       changeset = create(:changeset)
 
1509       auth_header = bearer_authorization_header changeset.user
 
1514           <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
 
1515           <way id="-1" changeset="#{changeset.id}">
 
1519           <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
 
1525       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1526       assert_response :bad_request,
 
1527                       "shouldn't refer elements behind it"
 
1530     def test_upload_duplicate_delete
 
1531       changeset = create(:changeset)
 
1533       auth_header = bearer_authorization_header changeset.user
 
1538             <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
 
1541             <node id="-1" version="1" changeset="#{changeset.id}" />
 
1542             <node id="-1" version="1" changeset="#{changeset.id}" />
 
1548       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1549       assert_response :gone,
 
1550                       "transaction should be cancelled by second deletion"
 
1555             <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
 
1557           <delete if-unused="true">
 
1558             <node id="-1" version="1" changeset="#{changeset.id}" />
 
1559             <node id="-1" version="1" changeset="#{changeset.id}" />
 
1565       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1567       assert_select "diffResult>node", 3
 
1568       assert_select "diffResult>node[old_id='-1']", 3
 
1569       assert_select "diffResult>node[new_version='1']", 1
 
1570       assert_select "diffResult>node[new_version='2']", 1
 
1574     # test that uploading a way referencing invalid placeholders gives a
 
1575     # proper error, not a 500.
 
1576     def test_upload_placeholder_invalid_way
 
1577       changeset = create(:changeset)
 
1580       auth_header = bearer_authorization_header changeset.user
 
1585           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
 
1586           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
 
1587           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
 
1588           <way id="-1" changeset="#{changeset.id}" version="1">
 
1599       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1600       assert_response :bad_request,
 
1601                       "shouldn't be able to use invalid placeholder IDs"
 
1602       assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
 
1604       # the same again, but this time use an existing way
 
1608           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
 
1609           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
 
1610           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
 
1611           <way id="#{way.id}" changeset="#{changeset.id}" version="1">
 
1622       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1623       assert_response :bad_request,
 
1624                       "shouldn't be able to use invalid placeholder IDs"
 
1625       assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
 
1629     # test that uploading a relation referencing invalid placeholders gives a
 
1630     # proper error, not a 500.
 
1631     def test_upload_placeholder_invalid_relation
 
1632       changeset = create(:changeset)
 
1633       relation = create(:relation)
 
1635       auth_header = bearer_authorization_header changeset.user
 
1640           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
 
1641           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
 
1642           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
 
1643           <relation id="-1" changeset="#{changeset.id}" version="1">
 
1644            <member type="node" role="foo" ref="-1"/>
 
1645            <member type="node" role="foo" ref="-2"/>
 
1646            <member type="node" role="foo" ref="-3"/>
 
1647            <member type="node" role="foo" ref="-4"/>
 
1654       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1655       assert_response :bad_request,
 
1656                       "shouldn't be able to use invalid placeholder IDs"
 
1657       assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
 
1659       # the same again, but this time use an existing relation
 
1663           <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
 
1664           <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
 
1665           <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
 
1666           <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
 
1667            <member type="node" role="foo" ref="-1"/>
 
1668            <member type="node" role="foo" ref="-2"/>
 
1669            <member type="node" role="foo" ref="-3"/>
 
1670            <member type="way" role="bar" ref="-1"/>
 
1677       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1678       assert_response :bad_request,
 
1679                       "shouldn't be able to use invalid placeholder IDs"
 
1680       assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
 
1684     # test what happens if a diff is uploaded containing only a node
 
1686     def test_upload_node_move
 
1687       auth_header = bearer_authorization_header
 
1689       xml = "<osm><changeset>" \
 
1690             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1691             "</changeset></osm>"
 
1692       post api_changesets_path, :params => xml, :headers => auth_header
 
1693       assert_response :success
 
1694       changeset_id = @response.body.to_i
 
1696       old_node = create(:node, :lat => 1, :lon => 1)
 
1698       diff = XML::Document.new
 
1699       diff.root = XML::Node.new "osmChange"
 
1700       modify = XML::Node.new "modify"
 
1701       xml_old_node = xml_node_for_node(old_node)
 
1702       xml_old_node["lat"] = 2.0.to_s
 
1703       xml_old_node["lon"] = 2.0.to_s
 
1704       xml_old_node["changeset"] = changeset_id.to_s
 
1705       modify << xml_old_node
 
1709       post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
 
1710       assert_response :success,
 
1711                       "diff should have uploaded OK"
 
1714       changeset = Changeset.find(changeset_id)
 
1715       assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
 
1716       assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
 
1717       assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
 
1718       assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
 
1722     # test what happens if a diff is uploaded adding a node to a way.
 
1723     def test_upload_way_extend
 
1724       auth_header = bearer_authorization_header
 
1726       xml = "<osm><changeset>" \
 
1727             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1728             "</changeset></osm>"
 
1729       post api_changesets_path, :params => xml, :headers => auth_header
 
1730       assert_response :success
 
1731       changeset_id = @response.body.to_i
 
1733       old_way = create(:way)
 
1734       create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
 
1736       diff = XML::Document.new
 
1737       diff.root = XML::Node.new "osmChange"
 
1738       modify = XML::Node.new "modify"
 
1739       xml_old_way = xml_node_for_way(old_way)
 
1740       nd_ref = XML::Node.new "nd"
 
1741       nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
 
1742       xml_old_way << nd_ref
 
1743       xml_old_way["changeset"] = changeset_id.to_s
 
1744       modify << xml_old_way
 
1748       post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
 
1749       assert_response :success,
 
1750                       "diff should have uploaded OK"
 
1753       changeset = Changeset.find(changeset_id)
 
1754       assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
 
1755       assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
 
1756       assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
 
1757       assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
 
1761     # test for more issues in #1568
 
1762     def test_upload_empty_invalid
 
1763       changeset = create(:changeset)
 
1765       auth_header = bearer_authorization_header changeset.user
 
1768        "<osmChange></osmChange>",
 
1769        "<osmChange><modify/></osmChange>",
 
1770        "<osmChange><modify></modify></osmChange>"].each do |diff|
 
1772         post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1773         assert_response(:success, "should be able to upload " \
 
1774                                   "empty changeset: " + diff)
 
1779     # test that the X-Error-Format header works to request XML errors
 
1780     def test_upload_xml_errors
 
1781       changeset = create(:changeset)
 
1782       node = create(:node)
 
1783       create(:relation_member, :member => node)
 
1785       auth_header = bearer_authorization_header changeset.user
 
1787       # try and delete a node that is in use
 
1788       diff = XML::Document.new
 
1789       diff.root = XML::Node.new "osmChange"
 
1790       delete = XML::Node.new "delete"
 
1792       delete << xml_node_for_node(node)
 
1795       error_header = error_format_header "xml"
 
1796       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
 
1797       assert_response :success,
 
1798                       "failed to return error in XML format"
 
1800       # check the returned payload
 
1801       assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
1802       assert_select "osmError>status", 1
 
1803       assert_select "osmError>message", 1
 
1806     def test_upload_not_found
 
1807       changeset = create(:changeset)
 
1809       auth_header = bearer_authorization_header changeset.user
 
1815           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1821       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1822       assert_response :not_found, "Node should not be found"
 
1828           <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1834       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1835       assert_response :not_found, "Way should not be found"
 
1841           <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1847       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1848       assert_response :not_found, "Relation should not be found"
 
1854           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1860       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1861       assert_response :not_found, "Node should not be deleted"
 
1867           <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1873       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1874       assert_response :not_found, "Way should not be deleted"
 
1880           <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1886       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1887       assert_response :not_found, "Relation should not be deleted"
 
1890     def test_upload_relation_placeholder_not_fix
 
1891       changeset = create(:changeset)
 
1893       auth_header = bearer_authorization_header changeset.user
 
1897         <osmChange version='0.6'>
 
1899             <relation id='-2' version='0' changeset='#{changeset.id}'>
 
1900               <member type='relation' role='' ref='-4' />
 
1901               <tag k='type' v='route' />
 
1902               <tag k='name' v='AtoB' />
 
1904             <relation id='-3' version='0' changeset='#{changeset.id}'>
 
1905               <tag k='type' v='route' />
 
1906               <tag k='name' v='BtoA' />
 
1908             <relation id='-4' version='0' changeset='#{changeset.id}'>
 
1909               <member type='relation' role='' ref='-2' />
 
1910               <member type='relation' role='' ref='-3' />
 
1911               <tag k='type' v='route_master' />
 
1912               <tag k='name' v='master' />
 
1919       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
1920       assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
 
1921       assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
 
1924     def test_upload_multiple_delete_block
 
1925       changeset = create(:changeset)
 
1927       auth_header = bearer_authorization_header changeset.user
 
1929       node = create(:node)
 
1931       create(:way_node, :way => way, :node => node)
 
1932       alone_node = create(:node)
 
1936         <osmChange version='0.6'>
 
1937           <delete version="0.6">
 
1938             <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
 
1940           <delete version="0.6" if-unused="true">
 
1941             <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
 
1947       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
1948       assert_response :precondition_failed,
 
1949                       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
 
1950       assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
 
1954     # test initial rate limit
 
1955     def test_upload_initial_rate_limit
 
1957       user = create(:user)
 
1959       # create some objects to use
 
1960       node = create(:node)
 
1961       way = create(:way_with_nodes, :nodes_count => 2)
 
1962       relation = create(:relation)
 
1964       # create a changeset that puts us near the initial rate limit
 
1965       changeset = create(:changeset, :user => user,
 
1966                                      :created_at => Time.now.utc - 5.minutes,
 
1967                                      :num_changes => Settings.initial_changes_per_hour - 2)
 
1969       # create authentication header
 
1970       auth_header = bearer_authorization_header user
 
1972       # simple diff to create a node way and relation using placeholders
 
1976           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1977            <tag k='foo' v='bar'/>
 
1978            <tag k='baz' v='bat'/>
 
1980           <way id='-1' changeset='#{changeset.id}'>
 
1981            <nd ref='#{node.id}'/>
 
1985           <relation id='-1' changeset='#{changeset.id}'>
 
1986            <member type='way' role='some' ref='#{way.id}'/>
 
1987            <member type='node' role='some' ref='#{node.id}'/>
 
1988            <member type='relation' role='some' ref='#{relation.id}'/>
 
1995       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1996       assert_response :too_many_requests, "upload did not hit rate limit"
 
2000     # test maximum rate limit
 
2001     def test_upload_maximum_rate_limit
 
2003       user = create(:user)
 
2005       # create some objects to use
 
2006       node = create(:node)
 
2007       way = create(:way_with_nodes, :nodes_count => 2)
 
2008       relation = create(:relation)
 
2010       # create a changeset to establish our initial edit time
 
2011       changeset = create(:changeset, :user => user,
 
2012                                      :created_at => Time.now.utc - 28.days)
 
2014       # create changeset to put us near the maximum rate limit
 
2015       total_changes = Settings.max_changes_per_hour - 2
 
2016       while total_changes.positive?
 
2017         changes = [total_changes, Changeset::MAX_ELEMENTS].min
 
2018         changeset = create(:changeset, :user => user,
 
2019                                        :created_at => Time.now.utc - 5.minutes,
 
2020                                        :num_changes => changes)
 
2021         total_changes -= changes
 
2024       # create authentication header
 
2025       auth_header = bearer_authorization_header user
 
2027       # simple diff to create a node way and relation using placeholders
 
2031           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
2032            <tag k='foo' v='bar'/>
 
2033            <tag k='baz' v='bat'/>
 
2035           <way id='-1' changeset='#{changeset.id}'>
 
2036            <nd ref='#{node.id}'/>
 
2040           <relation id='-1' changeset='#{changeset.id}'>
 
2041            <member type='way' role='some' ref='#{way.id}'/>
 
2042            <member type='node' role='some' ref='#{node.id}'/>
 
2043            <member type='relation' role='some' ref='#{relation.id}'/>
 
2050       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
2051       assert_response :too_many_requests, "upload did not hit rate limit"
 
2055     # test initial size limit
 
2056     def test_upload_initial_size_limit
 
2058       user = create(:user)
 
2060       # create a changeset that puts us near the initial size limit
 
2061       changeset = create(:changeset, :user => user,
 
2062                                      :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
 
2063                                      :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
 
2065       # create authentication header
 
2066       auth_header = bearer_authorization_header user
 
2068       # simple diff to create a node
 
2072           <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
 
2073            <tag k='foo' v='bar'/>
 
2074            <tag k='baz' v='bat'/>
 
2081       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
2082       assert_response :payload_too_large, "upload did not hit size limit"
 
2086     # test size limit after one week
 
2087     def test_upload_week_size_limit
 
2089       user = create(:user)
 
2091       # create a changeset to establish our initial edit time
 
2092       create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
 
2094       # create a changeset that puts us near the initial size limit
 
2095       changeset = create(:changeset, :user => user,
 
2096                                      :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
 
2097                                      :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
 
2099       # create authentication header
 
2100       auth_header = bearer_authorization_header user
 
2102       # simple diff to create a node way and relation using placeholders
 
2106           <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
 
2107            <tag k='foo' v='bar'/>
 
2108            <tag k='baz' v='bat'/>
 
2115       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
2116       assert_response :payload_too_large, "upload did not hit size limit"
 
2120     # when we make some simple changes we get the same changes back from the
 
2122     def test_diff_download_simple
 
2123       node = create(:node)
 
2125       ## First try with a non-public user, which should get a forbidden
 
2126       auth_header = bearer_authorization_header create(:user, :data_public => false)
 
2128       # create a temporary changeset
 
2129       xml = "<osm><changeset>" \
 
2130             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
2131             "</changeset></osm>"
 
2132       post api_changesets_path, :params => xml, :headers => auth_header
 
2133       assert_response :forbidden
 
2135       ## Now try with a normal user
 
2136       auth_header = bearer_authorization_header
 
2138       # create a temporary changeset
 
2139       xml = "<osm><changeset>" \
 
2140             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
2141             "</changeset></osm>"
 
2142       post api_changesets_path, :params => xml, :headers => auth_header
 
2143       assert_response :success
 
2144       changeset_id = @response.body.to_i
 
2150           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
 
2151           <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
 
2152           <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
 
2153           <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
 
2154           <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
 
2155           <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
 
2156           <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
 
2157           <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
 
2163       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
 
2164       assert_response :success,
 
2165                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
2167       get api_changeset_download_path(changeset_id)
 
2168       assert_response :success
 
2170       assert_select "osmChange", 1
 
2171       assert_select "osmChange>modify", 8
 
2172       assert_select "osmChange>modify>node", 8
 
2176     # culled this from josm to ensure that nothing in the way that josm
 
2177     # is formatting the request is causing it to fail.
 
2179     # NOTE: the error turned out to be something else completely!
 
2180     def test_josm_upload
 
2181       auth_header = bearer_authorization_header
 
2183       # create a temporary changeset
 
2184       xml = "<osm><changeset>" \
 
2185             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
2186             "</changeset></osm>"
 
2187       post api_changesets_path, :params => xml, :headers => auth_header
 
2188       assert_response :success
 
2189       changeset_id = @response.body.to_i
 
2192         <osmChange version="0.6" generator="JOSM">
 
2193         <create version="0.6" generator="JOSM">
 
2194           <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
 
2195           <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
 
2196           <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
 
2197           <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
 
2198           <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
 
2199           <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
 
2200           <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
 
2201           <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
 
2202           <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
 
2203           <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
 
2213             <tag k='highway' v='residential' />
 
2214             <tag k='name' v='Foobar Street' />
 
2221       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
 
2222       assert_response :success,
 
2223                       "can't upload a diff from JOSM: #{@response.body}"
 
2225       get api_changeset_download_path(changeset_id)
 
2226       assert_response :success
 
2228       assert_select "osmChange", 1
 
2229       assert_select "osmChange>create>node", 9
 
2230       assert_select "osmChange>create>way", 1
 
2231       assert_select "osmChange>create>way>nd", 9
 
2232       assert_select "osmChange>create>way>tag", 2
 
2236     # when we make some complex changes we get the same changes back from the
 
2238     def test_diff_download_complex
 
2239       node = create(:node)
 
2240       node2 = create(:node)
 
2242       auth_header = bearer_authorization_header
 
2244       # create a temporary changeset
 
2245       xml = "<osm><changeset>" \
 
2246             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
2247             "</changeset></osm>"
 
2248       post api_changesets_path, :params => xml, :headers => auth_header
 
2249       assert_response :success
 
2250       changeset_id = @response.body.to_i
 
2256           <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
 
2259           <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
 
2260           <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
 
2261           <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
 
2264           <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
 
2265           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
2266            <nd ref='#{node2.id}'/>
 
2276       post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
 
2277       assert_response :success,
 
2278                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
2280       get api_changeset_download_path(changeset_id)
 
2281       assert_response :success
 
2283       assert_select "osmChange", 1
 
2284       assert_select "osmChange>create", 3
 
2285       assert_select "osmChange>delete", 1
 
2286       assert_select "osmChange>modify", 2
 
2287       assert_select "osmChange>create>node", 3
 
2288       assert_select "osmChange>delete>node", 1
 
2289       assert_select "osmChange>modify>node", 1
 
2290       assert_select "osmChange>modify>way", 1
 
2294     # check that the bounding box of a changeset gets updated correctly
 
2295     # FIXME: This should really be moded to a integration test due to the with_controller
 
2296     def test_changeset_bbox
 
2298       create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
 
2300       auth_header = bearer_authorization_header
 
2302       # create a new changeset
 
2303       xml = "<osm><changeset/></osm>"
 
2304       post api_changesets_path, :params => xml, :headers => auth_header
 
2305       assert_response :success, "Creating of changeset failed."
 
2306       changeset_id = @response.body.to_i
 
2308       # add a single node to it
 
2309       with_controller(NodesController.new) do
 
2310         xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
 
2311         post api_nodes_path, :params => xml, :headers => auth_header
 
2312         assert_response :success, "Couldn't create node."
 
2315       # get the bounding box back from the changeset
 
2316       get api_changeset_path(changeset_id)
 
2317       assert_response :success, "Couldn't read back changeset."
 
2318       assert_select "osm>changeset[min_lon='0.1000000']", 1
 
2319       assert_select "osm>changeset[max_lon='0.1000000']", 1
 
2320       assert_select "osm>changeset[min_lat='0.2000000']", 1
 
2321       assert_select "osm>changeset[max_lat='0.2000000']", 1
 
2323       # add another node to it
 
2324       with_controller(NodesController.new) do
 
2325         xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
 
2326         post api_nodes_path, :params => xml, :headers => auth_header
 
2327         assert_response :success, "Couldn't create second node."
 
2330       # get the bounding box back from the changeset
 
2331       get api_changeset_path(changeset_id)
 
2332       assert_response :success, "Couldn't read back changeset for the second time."
 
2333       assert_select "osm>changeset[min_lon='0.1000000']", 1
 
2334       assert_select "osm>changeset[max_lon='0.2000000']", 1
 
2335       assert_select "osm>changeset[min_lat='0.1000000']", 1
 
2336       assert_select "osm>changeset[max_lat='0.2000000']", 1
 
2338       # add (delete) a way to it, which contains a point at (3,3)
 
2339       with_controller(WaysController.new) do
 
2340         xml = update_changeset(xml_for_way(way), changeset_id)
 
2341         delete api_way_path(way), :params => xml.to_s, :headers => auth_header
 
2342         assert_response :success, "Couldn't delete a way."
 
2345       # get the bounding box back from the changeset
 
2346       get api_changeset_path(changeset_id)
 
2347       assert_response :success, "Couldn't read back changeset for the third time."
 
2348       assert_select "osm>changeset[min_lon='0.1000000']", 1
 
2349       assert_select "osm>changeset[max_lon='0.3000000']", 1
 
2350       assert_select "osm>changeset[min_lat='0.1000000']", 1
 
2351       assert_select "osm>changeset[max_lat='0.3000000']", 1
 
2355     # check updating tags on a changeset
 
2356     def test_changeset_update
 
2357       private_user = create(:user, :data_public => false)
 
2358       private_changeset = create(:changeset, :user => private_user)
 
2359       user = create(:user)
 
2360       changeset = create(:changeset, :user => user)
 
2362       ## First try with a non-public user
 
2363       new_changeset = create_changeset_xml(:user => private_user)
 
2364       new_tag = XML::Node.new "tag"
 
2365       new_tag["k"] = "tagtesting"
 
2366       new_tag["v"] = "valuetesting"
 
2367       new_changeset.find("//osm/changeset").first << new_tag
 
2369       # try without any authorization
 
2370       put api_changeset_path(private_changeset), :params => new_changeset.to_s
 
2371       assert_response :unauthorized
 
2373       # try with the wrong authorization
 
2374       auth_header = bearer_authorization_header
 
2375       put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
 
2376       assert_response :conflict
 
2378       # now this should get an unauthorized
 
2379       auth_header = bearer_authorization_header private_user
 
2380       put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
 
2381       assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
 
2383       ## Now try with the public user
 
2384       new_changeset = create_changeset_xml(:id => 1)
 
2385       new_tag = XML::Node.new "tag"
 
2386       new_tag["k"] = "tagtesting"
 
2387       new_tag["v"] = "valuetesting"
 
2388       new_changeset.find("//osm/changeset").first << new_tag
 
2390       # try without any authorization
 
2391       put api_changeset_path(changeset), :params => new_changeset.to_s
 
2392       assert_response :unauthorized
 
2394       # try with the wrong authorization
 
2395       auth_header = bearer_authorization_header
 
2396       put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
2397       assert_response :conflict
 
2399       # now this should work...
 
2400       auth_header = bearer_authorization_header user
 
2401       put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
2402       assert_response :success
 
2404       assert_select "osm>changeset[id='#{changeset.id}']", 1
 
2405       assert_select "osm>changeset>tag", 1
 
2406       assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
 
2410     # check that a user different from the one who opened the changeset
 
2412     def test_changeset_update_invalid
 
2413       auth_header = bearer_authorization_header
 
2415       changeset = create(:changeset)
 
2416       new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
 
2417       new_tag = XML::Node.new "tag"
 
2418       new_tag["k"] = "testing"
 
2419       new_tag["v"] = "testing"
 
2420       new_changeset.find("//osm/changeset").first << new_tag
 
2422       put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
2423       assert_response :conflict
 
2427     # check that a changeset can contain a certain max number of changes.
 
2428     ## FIXME should be changed to an integration test due to the with_controller
 
2429     def test_changeset_limits
 
2430       user = create(:user)
 
2431       auth_header = bearer_authorization_header user
 
2433       # create an old changeset to ensure we have the maximum rate limit
 
2434       create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
 
2436       # open a new changeset
 
2437       xml = "<osm><changeset/></osm>"
 
2438       post api_changesets_path, :params => xml, :headers => auth_header
 
2439       assert_response :success, "can't create a new changeset"
 
2440       cs_id = @response.body.to_i
 
2442       # start the counter just short of where the changeset should finish.
 
2444       # alter the database to set the counter on the changeset directly,
 
2445       # otherwise it takes about 6 minutes to fill all of them.
 
2446       changeset = Changeset.find(cs_id)
 
2447       changeset.num_changes = Changeset::MAX_ELEMENTS - offset
 
2450       with_controller(NodesController.new) do
 
2452         xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
 
2453         post api_nodes_path, :params => xml, :headers => auth_header
 
2454         assert_response :success, "can't create a new node"
 
2455         node_id = @response.body.to_i
 
2457         get api_node_path(node_id)
 
2458         assert_response :success, "can't read back new node"
 
2459         node_doc = XML::Parser.string(@response.body).parse
 
2460         node_xml = node_doc.find("//osm/node").first
 
2462         # loop until we fill the changeset with nodes
 
2464           node_xml["lat"] = rand.to_s
 
2465           node_xml["lon"] = rand.to_s
 
2466           node_xml["version"] = (i + 1).to_s
 
2468           put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
 
2469           assert_response :success, "attempt #{i} should have succeeded"
 
2472         # trying again should fail
 
2473         node_xml["lat"] = rand.to_s
 
2474         node_xml["lon"] = rand.to_s
 
2475         node_xml["version"] = offset.to_s
 
2477         put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
 
2478         assert_response :conflict, "final attempt should have failed"
 
2481       changeset = Changeset.find(cs_id)
 
2482       assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
 
2484       # check that the changeset is now closed as well
 
2485       assert_not(changeset.open?,
 
2486                  "changeset should have been auto-closed by exceeding " \
 
2493     # check that the output consists of one specific changeset
 
2494     def assert_single_changeset(changeset, &)
 
2495       assert_dom "> changeset", 1 do
 
2496         assert_dom "> @id", changeset.id.to_s
 
2497         assert_dom "> @created_at", changeset.created_at.xmlschema
 
2499           assert_dom "> @open", "true"
 
2500           assert_dom "> @closed_at", 0
 
2502           assert_dom "> @open", "false"
 
2503           assert_dom "> @closed_at", changeset.closed_at.xmlschema
 
2505         assert_dom "> @comments_count", changeset.comments.length.to_s
 
2506         assert_dom "> @changes_count", changeset.num_changes.to_s
 
2507         yield if block_given?
 
2511     def assert_single_changeset_json(changeset, js)
 
2512       assert_equal changeset.id, js["changeset"]["id"]
 
2513       assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
 
2515         assert js["changeset"]["open"]
 
2516         assert_nil js["changeset"]["closed_at"]
 
2518         assert_not js["changeset"]["open"]
 
2519         assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
 
2521       assert_equal changeset.comments.length, js["changeset"]["comments_count"]
 
2522       assert_equal changeset.num_changes, js["changeset"]["changes_count"]
 
2526     # check that certain changesets exist in the output in the specified order
 
2527     def assert_changesets_in_order(changesets)
 
2528       assert_select "osm>changeset", changesets.size
 
2529       changesets.each_with_index do |changeset, index|
 
2530         assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
 
2535     # update the changeset_id of a way element
 
2536     def update_changeset(xml, changeset_id)
 
2537       xml_attr_rewrite(xml, "changeset", changeset_id)
 
2541     # update an attribute in a way element
 
2542     def xml_attr_rewrite(xml, name, value)
 
2543       xml.find("//osm/way").first[name] = value.to_s
 
2548     # build XML for changesets
 
2549     def create_changeset_xml(user: nil, id: nil)
 
2550       root = XML::Document.new
 
2551       root.root = XML::Node.new "osm"
 
2552       cs = XML::Node.new "changeset"
 
2554         cs["user"] = user.display_name
 
2555         cs["uid"] = user.id.to_s
 
2557       cs["id"] = id.to_s if id