]> git.openstreetmap.org Git - rails.git/blob - test/integration/changeset_bbox_test.rb
Merge remote-tracking branch 'upstream/pull/6243'
[rails.git] / test / integration / changeset_bbox_test.rb
1 require "test_helper"
2
3 class ChangesetBboxTest < ActionDispatch::IntegrationTest
4   ##
5   # check that the bounding box of a changeset gets updated correctly
6   def test_changeset_bbox
7     way = create(:way)
8     create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
9
10     auth_header = bearer_authorization_header
11
12     # create a new changeset
13     xml = "<osm><changeset/></osm>"
14     post api_changesets_path, :params => xml, :headers => auth_header
15     assert_response :success, "Creating of changeset failed."
16     changeset_id = @response.body.to_i
17
18     # add a single node to it
19     with_controller(NodesController.new) do
20       xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
21       post api_nodes_path, :params => xml, :headers => auth_header
22       assert_response :success, "Couldn't create node."
23     end
24
25     # get the bounding box back from the changeset
26     get api_changeset_path(changeset_id)
27     assert_response :success, "Couldn't read back changeset."
28     assert_dom "osm>changeset[min_lon='0.1000000']", 1
29     assert_dom "osm>changeset[max_lon='0.1000000']", 1
30     assert_dom "osm>changeset[min_lat='0.2000000']", 1
31     assert_dom "osm>changeset[max_lat='0.2000000']", 1
32
33     # add another node to it
34     with_controller(NodesController.new) do
35       xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
36       post api_nodes_path, :params => xml, :headers => auth_header
37       assert_response :success, "Couldn't create second node."
38     end
39
40     # get the bounding box back from the changeset
41     get api_changeset_path(changeset_id)
42     assert_response :success, "Couldn't read back changeset for the second time."
43     assert_dom "osm>changeset[min_lon='0.1000000']", 1
44     assert_dom "osm>changeset[max_lon='0.2000000']", 1
45     assert_dom "osm>changeset[min_lat='0.1000000']", 1
46     assert_dom "osm>changeset[max_lat='0.2000000']", 1
47
48     # add (delete) a way to it, which contains a point at (3,3)
49     with_controller(WaysController.new) do
50       xml = update_changeset(xml_for_way(way), changeset_id)
51       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
52       assert_response :success, "Couldn't delete a way."
53     end
54
55     # get the bounding box back from the changeset
56     get api_changeset_path(changeset_id)
57     assert_response :success, "Couldn't read back changeset for the third time."
58     assert_dom "osm>changeset[min_lon='0.1000000']", 1
59     assert_dom "osm>changeset[max_lon='0.3000000']", 1
60     assert_dom "osm>changeset[min_lat='0.1000000']", 1
61     assert_dom "osm>changeset[max_lat='0.3000000']", 1
62   end
63
64   ##
65   # when a relation's tag is modified then it should put the bounding
66   # box of all its members into the changeset.
67   def test_relation_tag_modify_bounding_box
68     relation = create(:relation)
69     node1 = create(:node, :lat => 0.3, :lon => 0.3)
70     node2 = create(:node, :lat => 0.5, :lon => 0.5)
71     way = create(:way)
72     create(:way_node, :way => way, :node => node1)
73     create(:relation_member, :relation => relation, :member => way)
74     create(:relation_member, :relation => relation, :member => node2)
75     # the relation contains nodes1 and node2 (node1
76     # indirectly via the way), so the bbox should be [0.3,0.3,0.5,0.5].
77     check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
78       # add a tag to an existing relation
79       relation_xml = xml_for_relation(relation)
80       relation_element = relation_xml.find("//osm/relation").first
81       new_tag = XML::Node.new("tag")
82       new_tag["k"] = "some_new_tag"
83       new_tag["v"] = "some_new_value"
84       relation_element << new_tag
85
86       # update changeset ID to point to new changeset
87       update_changeset(relation_xml, changeset_id)
88
89       # upload the change
90       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
91       assert_response :success, "can't update relation for tag/bbox test"
92     end
93   end
94
95   ##
96   # add a member to a relation and check the bounding box is only that
97   # element.
98   def test_relation_add_member_bounding_box
99     relation = create(:relation)
100     node1 = create(:node, :lat => 4, :lon => 4)
101     node2 = create(:node, :lat => 7, :lon => 7)
102     way1 = create(:way)
103     create(:way_node, :way => way1, :node => create(:node, :lat => 8, :lon => 8))
104     way2 = create(:way)
105     create(:way_node, :way => way2, :node => create(:node, :lat => 9, :lon => 9), :sequence_id => 1)
106     create(:way_node, :way => way2, :node => create(:node, :lat => 10, :lon => 10), :sequence_id => 2)
107
108     [node1, node2, way1, way2].each do |element|
109       bbox = element.bbox.to_unscaled
110       check_changeset_modify(bbox) do |changeset_id, auth_header|
111         relation_xml = xml_for_relation(Relation.find(relation.id))
112         relation_element = relation_xml.find("//osm/relation").first
113         new_member = XML::Node.new("member")
114         new_member["ref"] = element.id.to_s
115         new_member["type"] = element.class.to_s.downcase
116         new_member["role"] = "some_role"
117         relation_element << new_member
118
119         # update changeset ID to point to new changeset
120         update_changeset(relation_xml, changeset_id)
121
122         # upload the change
123         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
124         assert_response :success, "can't update relation for add #{element.class}/bbox test: #{@response.body}"
125
126         # get it back and check the ordering
127         get api_relation_path(relation)
128         assert_members_equal_response relation_xml
129       end
130     end
131   end
132
133   ##
134   # remove a member from a relation and check the bounding box is
135   # only that element.
136   def test_relation_remove_member_bounding_box
137     relation = create(:relation)
138     node1 = create(:node, :lat => 3, :lon => 3)
139     node2 = create(:node, :lat => 5, :lon => 5)
140     create(:relation_member, :relation => relation, :member => node1)
141     create(:relation_member, :relation => relation, :member => node2)
142
143     check_changeset_modify(BoundingBox.new(5, 5, 5, 5)) do |changeset_id, auth_header|
144       # remove node 5 (5,5) from an existing relation
145       relation_xml = xml_for_relation(relation)
146       relation_xml
147         .find("//osm/relation/member[@type='node'][@ref='#{node2.id}']")
148         .first.remove!
149
150       # update changeset ID to point to new changeset
151       update_changeset(relation_xml, changeset_id)
152
153       # upload the change
154       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
155       assert_response :success, "can't update relation for remove node/bbox test"
156     end
157   end
158
159   ##
160   # remove all the members from a relation. the result is pretty useless, but
161   # still technically valid.
162   def test_relation_remove_all_members
163     relation = create(:relation)
164     node1 = create(:node, :lat => 0.3, :lon => 0.3)
165     node2 = create(:node, :lat => 0.5, :lon => 0.5)
166     way = create(:way)
167     create(:way_node, :way => way, :node => node1)
168     create(:relation_member, :relation => relation, :member => way)
169     create(:relation_member, :relation => relation, :member => node2)
170
171     check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
172       relation_xml = xml_for_relation(relation)
173       relation_xml
174         .find("//osm/relation/member")
175         .each(&:remove!)
176
177       # update changeset ID to point to new changeset
178       update_changeset(relation_xml, changeset_id)
179
180       # upload the change
181       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
182       assert_response :success, "can't update relation for remove all members test"
183       checkrelation = Relation.find(relation.id)
184       assert_not_nil(checkrelation,
185                      "uploaded relation not found in database after upload")
186       assert_equal(0, checkrelation.members.length,
187                    "relation contains members but they should have all been deleted")
188     end
189   end
190
191   private
192
193   ##
194   # create a changeset and yield to the caller to set it up, then assert
195   # that the changeset bounding box is +bbox+.
196   def check_changeset_modify(bbox)
197     ## First test with the private user to check that you get a forbidden
198     auth_header = bearer_authorization_header create(:user, :data_public => false)
199
200     # create a new changeset for this operation, so we are assured
201     # that the bounding box will be newly-generated.
202     with_controller(Api::ChangesetsController.new) do
203       xml = "<osm><changeset/></osm>"
204       post api_changesets_path, :params => xml, :headers => auth_header
205       assert_response :forbidden, "shouldn't be able to create changeset for modify test, as should get forbidden"
206     end
207
208     ## Now do the whole thing with the public user
209     auth_header = bearer_authorization_header
210
211     # create a new changeset for this operation, so we are assured
212     # that the bounding box will be newly-generated.
213     changeset_id = with_controller(Api::ChangesetsController.new) do
214       xml = "<osm><changeset/></osm>"
215       post api_changesets_path, :params => xml, :headers => auth_header
216       assert_response :success, "couldn't create changeset for modify test"
217       @response.body.to_i
218     end
219
220     # go back to the block to do the actual modifies
221     yield changeset_id, auth_header
222
223     # now download the changeset to check its bounding box
224     with_controller(Api::ChangesetsController.new) do
225       get api_changeset_path(changeset_id)
226       assert_response :success, "can't re-read changeset for modify test"
227       assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
228       assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
229       assert_select "osm>changeset[min_lon='#{format('%<lon>.7f', :lon => bbox.min_lon)}']", 1, "Changeset min_lon wrong in #{@response.body}"
230       assert_select "osm>changeset[min_lat='#{format('%<lat>.7f', :lat => bbox.min_lat)}']", 1, "Changeset min_lat wrong in #{@response.body}"
231       assert_select "osm>changeset[max_lon='#{format('%<lon>.7f', :lon => bbox.max_lon)}']", 1, "Changeset max_lon wrong in #{@response.body}"
232       assert_select "osm>changeset[max_lat='#{format('%<lat>.7f', :lat => bbox.max_lat)}']", 1, "Changeset max_lat wrong in #{@response.body}"
233     end
234   end
235
236   ##
237   # checks that the XML document and the response have
238   # members in the same order.
239   def assert_members_equal_response(doc, response_message = "can't read back the relation")
240     assert_response :success, "#{response_message}: #{@response.body}"
241     new_doc = XML::Parser.string(@response.body).parse
242
243     doc_members = doc.find("//osm/relation/member").collect do |m|
244       [m["ref"].to_i, m["type"].to_sym, m["role"]]
245     end
246
247     new_members = new_doc.find("//osm/relation/member").collect do |m|
248       [m["ref"].to_i, m["type"].to_sym, m["role"]]
249     end
250
251     assert_equal doc_members, new_members, "members are not equal - ordering is wrong? (#{doc}, #{@response.body})"
252   end
253
254   ##
255   # update the changeset_id of an element
256   def update_changeset(xml, changeset_id)
257     xml_attr_rewrite(xml, "changeset", changeset_id)
258   end
259
260   ##
261   # update an attribute in an element
262   def xml_attr_rewrite(xml, name, value)
263     xml.find("//osm/*[self::node or self::way or self::relation]").first[name] = value.to_s
264     xml
265   end
266 end