]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/notes_controller_test.rb
Require user names to be unique after unicode normalisation
[rails.git] / test / controllers / api / notes_controller_test.rb
1 require "test_helper"
2
3 module Api
4   class NotesControllerTest < ActionDispatch::IntegrationTest
5     def setup
6       super
7       # Stub nominatim response for note locations
8       stub_request(:get, %r{^https://nominatim\.openstreetmap\.org/reverse\?})
9         .to_return(:status => 404)
10     end
11
12     ##
13     # test all routes which lead to this controller
14     def test_routes
15       assert_routing(
16         { :path => "/api/0.6/notes", :method => :post },
17         { :controller => "api/notes", :action => "create" }
18       )
19       assert_routing(
20         { :path => "/api/0.6/notes/1", :method => :get },
21         { :controller => "api/notes", :action => "show", :id => "1" }
22       )
23       assert_recognizes(
24         { :controller => "api/notes", :action => "show", :id => "1", :format => "xml" },
25         { :path => "/api/0.6/notes/1.xml", :method => :get }
26       )
27       assert_routing(
28         { :path => "/api/0.6/notes/1.rss", :method => :get },
29         { :controller => "api/notes", :action => "show", :id => "1", :format => "rss" }
30       )
31       assert_routing(
32         { :path => "/api/0.6/notes/1.json", :method => :get },
33         { :controller => "api/notes", :action => "show", :id => "1", :format => "json" }
34       )
35       assert_routing(
36         { :path => "/api/0.6/notes/1.gpx", :method => :get },
37         { :controller => "api/notes", :action => "show", :id => "1", :format => "gpx" }
38       )
39       assert_routing(
40         { :path => "/api/0.6/notes/1/comment", :method => :post },
41         { :controller => "api/notes", :action => "comment", :id => "1" }
42       )
43       assert_routing(
44         { :path => "/api/0.6/notes/1/close", :method => :post },
45         { :controller => "api/notes", :action => "close", :id => "1" }
46       )
47       assert_routing(
48         { :path => "/api/0.6/notes/1/reopen", :method => :post },
49         { :controller => "api/notes", :action => "reopen", :id => "1" }
50       )
51       assert_routing(
52         { :path => "/api/0.6/notes/1", :method => :delete },
53         { :controller => "api/notes", :action => "destroy", :id => "1" }
54       )
55
56       assert_routing(
57         { :path => "/api/0.6/notes", :method => :get },
58         { :controller => "api/notes", :action => "index" }
59       )
60       assert_recognizes(
61         { :controller => "api/notes", :action => "index", :format => "xml" },
62         { :path => "/api/0.6/notes.xml", :method => :get }
63       )
64       assert_routing(
65         { :path => "/api/0.6/notes.rss", :method => :get },
66         { :controller => "api/notes", :action => "index", :format => "rss" }
67       )
68       assert_routing(
69         { :path => "/api/0.6/notes.json", :method => :get },
70         { :controller => "api/notes", :action => "index", :format => "json" }
71       )
72       assert_routing(
73         { :path => "/api/0.6/notes.gpx", :method => :get },
74         { :controller => "api/notes", :action => "index", :format => "gpx" }
75       )
76
77       assert_routing(
78         { :path => "/api/0.6/notes/search", :method => :get },
79         { :controller => "api/notes", :action => "search" }
80       )
81       assert_recognizes(
82         { :controller => "api/notes", :action => "search", :format => "xml" },
83         { :path => "/api/0.6/notes/search.xml", :method => :get }
84       )
85       assert_routing(
86         { :path => "/api/0.6/notes/search.rss", :method => :get },
87         { :controller => "api/notes", :action => "search", :format => "rss" }
88       )
89       assert_routing(
90         { :path => "/api/0.6/notes/search.json", :method => :get },
91         { :controller => "api/notes", :action => "search", :format => "json" }
92       )
93       assert_routing(
94         { :path => "/api/0.6/notes/search.gpx", :method => :get },
95         { :controller => "api/notes", :action => "search", :format => "gpx" }
96       )
97
98       assert_routing(
99         { :path => "/api/0.6/notes/feed", :method => :get },
100         { :controller => "api/notes", :action => "feed", :format => "rss" }
101       )
102
103       assert_recognizes(
104         { :controller => "api/notes", :action => "create" },
105         { :path => "/api/0.6/notes/addPOIexec", :method => :post }
106       )
107       assert_recognizes(
108         { :controller => "api/notes", :action => "close" },
109         { :path => "/api/0.6/notes/closePOIexec", :method => :post }
110       )
111       assert_recognizes(
112         { :controller => "api/notes", :action => "comment" },
113         { :path => "/api/0.6/notes/editPOIexec", :method => :post }
114       )
115       assert_recognizes(
116         { :controller => "api/notes", :action => "index", :format => "gpx" },
117         { :path => "/api/0.6/notes/getGPX", :method => :get }
118       )
119       assert_recognizes(
120         { :controller => "api/notes", :action => "feed", :format => "rss" },
121         { :path => "/api/0.6/notes/getRSSfeed", :method => :get }
122       )
123     end
124
125     def test_create_success
126       assert_difference "Note.count", 1 do
127         assert_difference "NoteComment.count", 1 do
128           post api_notes_path(:lat => -1.0, :lon => -1.0, :text => "This is a comment", :format => "json")
129         end
130       end
131       assert_response :success
132       js = ActiveSupport::JSON.decode(@response.body)
133       assert_not_nil js
134       assert_equal "Feature", js["type"]
135       assert_equal "Point", js["geometry"]["type"]
136       assert_equal [-1.0, -1.0], js["geometry"]["coordinates"]
137       assert_equal "open", js["properties"]["status"]
138       assert_equal 1, js["properties"]["comments"].count
139       assert_equal "opened", js["properties"]["comments"].last["action"]
140       assert_equal "This is a comment", js["properties"]["comments"].last["text"]
141       assert_nil js["properties"]["comments"].last["user"]
142       id = js["properties"]["id"]
143
144       get api_note_path(:id => id, :format => "json")
145       assert_response :success
146       js = ActiveSupport::JSON.decode(@response.body)
147       assert_not_nil js
148       assert_equal "Feature", js["type"]
149       assert_equal "Point", js["geometry"]["type"]
150       assert_equal [-1.0, -1.0], js["geometry"]["coordinates"]
151       assert_equal id, js["properties"]["id"]
152       assert_equal "open", js["properties"]["status"]
153       assert_equal 1, js["properties"]["comments"].count
154       assert_equal "opened", js["properties"]["comments"].last["action"]
155       assert_equal "This is a comment", js["properties"]["comments"].last["text"]
156       assert_nil js["properties"]["comments"].last["user"]
157     end
158
159     def test_create_fail
160       assert_no_difference "Note.count" do
161         assert_no_difference "NoteComment.count" do
162           post api_notes_path(:lon => -1.0, :text => "This is a comment")
163         end
164       end
165       assert_response :bad_request
166
167       assert_no_difference "Note.count" do
168         assert_no_difference "NoteComment.count" do
169           post api_notes_path(:lat => -1.0, :text => "This is a comment")
170         end
171       end
172       assert_response :bad_request
173
174       assert_no_difference "Note.count" do
175         assert_no_difference "NoteComment.count" do
176           post api_notes_path(:lat => -1.0, :lon => -1.0)
177         end
178       end
179       assert_response :bad_request
180
181       assert_no_difference "Note.count" do
182         assert_no_difference "NoteComment.count" do
183           post api_notes_path(:lat => -1.0, :lon => -1.0, :text => "")
184         end
185       end
186       assert_response :bad_request
187
188       assert_no_difference "Note.count" do
189         assert_no_difference "NoteComment.count" do
190           post api_notes_path(:lat => -100.0, :lon => -1.0, :text => "This is a comment")
191         end
192       end
193       assert_response :bad_request
194
195       assert_no_difference "Note.count" do
196         assert_no_difference "NoteComment.count" do
197           post api_notes_path(:lat => -1.0, :lon => -200.0, :text => "This is a comment")
198         end
199       end
200       assert_response :bad_request
201
202       assert_no_difference "Note.count" do
203         assert_no_difference "NoteComment.count" do
204           post api_notes_path(:lat => "abc", :lon => -1.0, :text => "This is a comment")
205         end
206       end
207       assert_response :bad_request
208
209       assert_no_difference "Note.count" do
210         assert_no_difference "NoteComment.count" do
211           post api_notes_path(:lat => -1.0, :lon => "abc", :text => "This is a comment")
212         end
213       end
214       assert_response :bad_request
215
216       assert_no_difference "Note.count" do
217         assert_no_difference "NoteComment.count" do
218           post api_notes_path(:lat => -1.0, :lon => -1.0, :text => "x\u0000y")
219         end
220       end
221       assert_response :bad_request
222     end
223
224     def test_comment_success
225       open_note_with_comment = create(:note_with_comments)
226       user = create(:user)
227       auth_header = basic_authorization_header user.email, "test"
228       assert_difference "NoteComment.count", 1 do
229         assert_no_difference "ActionMailer::Base.deliveries.size" do
230           perform_enqueued_jobs do
231             post comment_api_note_path(:id => open_note_with_comment, :text => "This is an additional comment", :format => "json"), :headers => auth_header
232           end
233         end
234       end
235       assert_response :success
236       js = ActiveSupport::JSON.decode(@response.body)
237       assert_not_nil js
238       assert_equal "Feature", js["type"]
239       assert_equal open_note_with_comment.id, js["properties"]["id"]
240       assert_equal "open", js["properties"]["status"]
241       assert_equal 2, js["properties"]["comments"].count
242       assert_equal "commented", js["properties"]["comments"].last["action"]
243       assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
244       assert_equal user.display_name, js["properties"]["comments"].last["user"]
245
246       get api_note_path(:id => open_note_with_comment, :format => "json")
247       assert_response :success
248       js = ActiveSupport::JSON.decode(@response.body)
249       assert_not_nil js
250       assert_equal "Feature", js["type"]
251       assert_equal open_note_with_comment.id, js["properties"]["id"]
252       assert_equal "open", js["properties"]["status"]
253       assert_equal 2, js["properties"]["comments"].count
254       assert_equal "commented", js["properties"]["comments"].last["action"]
255       assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
256       assert_equal user.display_name, js["properties"]["comments"].last["user"]
257
258       # Ensure that emails are sent to users
259       first_user = create(:user)
260       second_user = create(:user)
261       third_user = create(:user)
262
263       note_with_comments_by_users = create(:note) do |note|
264         create(:note_comment, :note => note, :author => first_user)
265         create(:note_comment, :note => note, :author => second_user)
266       end
267
268       auth_header = basic_authorization_header third_user.email, "test"
269
270       assert_difference "NoteComment.count", 1 do
271         assert_difference "ActionMailer::Base.deliveries.size", 2 do
272           perform_enqueued_jobs do
273             post comment_api_note_path(:id => note_with_comments_by_users, :text => "This is an additional comment", :format => "json"), :headers => auth_header
274           end
275         end
276       end
277       assert_response :success
278       js = ActiveSupport::JSON.decode(@response.body)
279       assert_not_nil js
280       assert_equal "Feature", js["type"]
281       assert_equal note_with_comments_by_users.id, js["properties"]["id"]
282       assert_equal "open", js["properties"]["status"]
283       assert_equal 3, js["properties"]["comments"].count
284       assert_equal "commented", js["properties"]["comments"].last["action"]
285       assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
286       assert_equal third_user.display_name, js["properties"]["comments"].last["user"]
287
288       email = ActionMailer::Base.deliveries.find { |e| e.to.first == first_user.email }
289       assert_not_nil email
290       assert_equal 1, email.to.length
291       assert_equal "[OpenStreetMap] #{third_user.display_name} has commented on one of your notes", email.subject
292       assert_equal first_user.email, email.to.first
293
294       email = ActionMailer::Base.deliveries.find { |e| e.to.first == second_user.email }
295       assert_not_nil email
296       assert_equal 1, email.to.length
297       assert_equal "[OpenStreetMap] #{third_user.display_name} has commented on a note you are interested in", email.subject
298
299       get api_note_path(:id => note_with_comments_by_users, :format => "json")
300       assert_response :success
301       js = ActiveSupport::JSON.decode(@response.body)
302       assert_not_nil js
303       assert_equal "Feature", js["type"]
304       assert_equal note_with_comments_by_users.id, js["properties"]["id"]
305       assert_equal "open", js["properties"]["status"]
306       assert_equal 3, js["properties"]["comments"].count
307       assert_equal "commented", js["properties"]["comments"].last["action"]
308       assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
309       assert_equal third_user.display_name, js["properties"]["comments"].last["user"]
310
311       ActionMailer::Base.deliveries.clear
312     end
313
314     def test_comment_fail
315       open_note_with_comment = create(:note_with_comments)
316
317       user = create(:user)
318
319       assert_no_difference "NoteComment.count" do
320         post comment_api_note_path(:id => open_note_with_comment)
321         assert_response :unauthorized
322       end
323
324       auth_header = basic_authorization_header user.email, "test"
325
326       assert_no_difference "NoteComment.count" do
327         post comment_api_note_path(:id => open_note_with_comment), :headers => auth_header
328       end
329       assert_response :bad_request
330
331       assert_no_difference "NoteComment.count" do
332         post comment_api_note_path(:id => open_note_with_comment, :text => ""), :headers => auth_header
333       end
334       assert_response :bad_request
335
336       assert_no_difference "NoteComment.count" do
337         post comment_api_note_path(:id => 12345, :text => "This is an additional comment"), :headers => auth_header
338       end
339       assert_response :not_found
340
341       hidden_note_with_comment = create(:note_with_comments, :status => "hidden")
342
343       assert_no_difference "NoteComment.count" do
344         post comment_api_note_path(:id => hidden_note_with_comment, :text => "This is an additional comment"), :headers => auth_header
345       end
346       assert_response :gone
347
348       closed_note_with_comment = create(:note_with_comments, :status => "closed", :closed_at => Time.now.utc)
349
350       assert_no_difference "NoteComment.count" do
351         post comment_api_note_path(:id => closed_note_with_comment, :text => "This is an additional comment"), :headers => auth_header
352       end
353       assert_response :conflict
354
355       assert_no_difference "NoteComment.count" do
356         post comment_api_note_path(:id => open_note_with_comment, :text => "x\u0000y"), :headers => auth_header
357       end
358       assert_response :bad_request
359     end
360
361     def test_close_success
362       open_note_with_comment = create(:note_with_comments)
363       user = create(:user)
364
365       post close_api_note_path(:id => open_note_with_comment, :text => "This is a close comment", :format => "json")
366       assert_response :unauthorized
367
368       auth_header = basic_authorization_header user.email, "test"
369
370       post close_api_note_path(:id => open_note_with_comment, :text => "This is a close comment", :format => "json"), :headers => auth_header
371       assert_response :success
372       js = ActiveSupport::JSON.decode(@response.body)
373       assert_not_nil js
374       assert_equal "Feature", js["type"]
375       assert_equal open_note_with_comment.id, js["properties"]["id"]
376       assert_equal "closed", js["properties"]["status"]
377       assert_equal 2, js["properties"]["comments"].count
378       assert_equal "closed", js["properties"]["comments"].last["action"]
379       assert_equal "This is a close comment", js["properties"]["comments"].last["text"]
380       assert_equal user.display_name, js["properties"]["comments"].last["user"]
381
382       get api_note_path(:id => open_note_with_comment.id, :format => "json")
383       assert_response :success
384       js = ActiveSupport::JSON.decode(@response.body)
385       assert_not_nil js
386       assert_equal "Feature", js["type"]
387       assert_equal open_note_with_comment.id, js["properties"]["id"]
388       assert_equal "closed", js["properties"]["status"]
389       assert_equal 2, js["properties"]["comments"].count
390       assert_equal "closed", js["properties"]["comments"].last["action"]
391       assert_equal "This is a close comment", js["properties"]["comments"].last["text"]
392       assert_equal user.display_name, js["properties"]["comments"].last["user"]
393     end
394
395     def test_close_fail
396       post close_api_note_path(:id => 12345)
397       assert_response :unauthorized
398
399       auth_header = basic_authorization_header create(:user).email, "test"
400
401       post close_api_note_path(:id => 12345), :headers => auth_header
402       assert_response :not_found
403
404       hidden_note_with_comment = create(:note_with_comments, :status => "hidden")
405
406       post close_api_note_path(:id => hidden_note_with_comment), :headers => auth_header
407       assert_response :gone
408
409       closed_note_with_comment = create(:note_with_comments, :status => "closed", :closed_at => Time.now.utc)
410
411       post close_api_note_path(:id => closed_note_with_comment), :headers => auth_header
412       assert_response :conflict
413     end
414
415     def test_reopen_success
416       closed_note_with_comment = create(:note_with_comments, :status => "closed", :closed_at => Time.now.utc)
417       user = create(:user)
418
419       post reopen_api_note_path(:id => closed_note_with_comment, :text => "This is a reopen comment", :format => "json")
420       assert_response :unauthorized
421
422       auth_header = basic_authorization_header user.email, "test"
423
424       post reopen_api_note_path(:id => closed_note_with_comment, :text => "This is a reopen comment", :format => "json"), :headers => auth_header
425       assert_response :success
426       js = ActiveSupport::JSON.decode(@response.body)
427       assert_not_nil js
428       assert_equal "Feature", js["type"]
429       assert_equal closed_note_with_comment.id, js["properties"]["id"]
430       assert_equal "open", js["properties"]["status"]
431       assert_equal 2, js["properties"]["comments"].count
432       assert_equal "reopened", js["properties"]["comments"].last["action"]
433       assert_equal "This is a reopen comment", js["properties"]["comments"].last["text"]
434       assert_equal user.display_name, js["properties"]["comments"].last["user"]
435
436       get api_note_path(:id => closed_note_with_comment, :format => "json")
437       assert_response :success
438       js = ActiveSupport::JSON.decode(@response.body)
439       assert_not_nil js
440       assert_equal "Feature", js["type"]
441       assert_equal closed_note_with_comment.id, js["properties"]["id"]
442       assert_equal "open", js["properties"]["status"]
443       assert_equal 2, js["properties"]["comments"].count
444       assert_equal "reopened", js["properties"]["comments"].last["action"]
445       assert_equal "This is a reopen comment", js["properties"]["comments"].last["text"]
446       assert_equal user.display_name, js["properties"]["comments"].last["user"]
447     end
448
449     def test_reopen_fail
450       hidden_note_with_comment = create(:note_with_comments, :status => "hidden")
451
452       post reopen_api_note_path(:id => hidden_note_with_comment)
453       assert_response :unauthorized
454
455       auth_header = basic_authorization_header create(:user).email, "test"
456
457       post reopen_api_note_path(:id => 12345), :headers => auth_header
458       assert_response :not_found
459
460       post reopen_api_note_path(:id => hidden_note_with_comment), :headers => auth_header
461       assert_response :gone
462
463       open_note_with_comment = create(:note_with_comments)
464
465       post reopen_api_note_path(:id => open_note_with_comment), :headers => auth_header
466       assert_response :conflict
467     end
468
469     def test_show_success
470       open_note = create(:note_with_comments)
471
472       get api_note_path(:id => open_note, :format => "xml")
473       assert_response :success
474       assert_equal "application/xml", @response.media_type
475       assert_select "osm", :count => 1 do
476         assert_select "note[lat='#{open_note.lat}'][lon='#{open_note.lon}']", :count => 1 do
477           assert_select "id", open_note.id.to_s
478           assert_select "url", api_note_url(open_note, :format => "xml")
479           assert_select "comment_url", comment_api_note_url(open_note, :format => "xml")
480           assert_select "close_url", close_api_note_url(open_note, :format => "xml")
481           assert_select "date_created", open_note.created_at.to_s
482           assert_select "status", open_note.status
483           assert_select "comments", :count => 1 do
484             assert_select "comment", :count => 1
485           end
486         end
487       end
488
489       get api_note_path(:id => open_note, :format => "rss")
490       assert_response :success
491       assert_equal "application/rss+xml", @response.media_type
492       assert_select "rss", :count => 1 do
493         assert_select "channel", :count => 1 do
494           assert_select "item", :count => 1 do
495             assert_select "link", note_url(open_note)
496             assert_select "guid", api_note_url(open_note)
497             assert_select "pubDate", open_note.created_at.to_fs(:rfc822)
498             assert_select "geo|lat", open_note.lat.to_s
499             assert_select "geo|long", open_note.lon.to_s
500             assert_select "georss|point", "#{open_note.lon} #{open_note.lon}"
501           end
502         end
503       end
504
505       get api_note_path(:id => open_note, :format => "json")
506       assert_response :success
507       assert_equal "application/json", @response.media_type
508       js = ActiveSupport::JSON.decode(@response.body)
509       assert_not_nil js
510       assert_equal "Feature", js["type"]
511       assert_equal "Point", js["geometry"]["type"]
512       assert_equal open_note.lat, js["geometry"]["coordinates"][0]
513       assert_equal open_note.lon, js["geometry"]["coordinates"][1]
514       assert_equal open_note.id, js["properties"]["id"]
515       assert_equal api_note_url(open_note, :format => "json"), js["properties"]["url"]
516       assert_equal comment_api_note_url(open_note, :format => "json"), js["properties"]["comment_url"]
517       assert_equal close_api_note_url(open_note, :format => "json"), js["properties"]["close_url"]
518       assert_equal open_note.created_at.to_s, js["properties"]["date_created"]
519       assert_equal open_note.status, js["properties"]["status"]
520
521       get api_note_path(:id => open_note, :format => "gpx")
522       assert_response :success
523       assert_equal "application/gpx+xml", @response.media_type
524       assert_select "gpx", :count => 1 do
525         assert_select "wpt[lat='#{open_note.lat}'][lon='#{open_note.lon}']", :count => 1 do
526           assert_select "time", :count => 1
527           assert_select "name", "Note: #{open_note.id}"
528           assert_select "desc", :count => 1
529           assert_select "link[href='http://www.example.com/note/#{open_note.id}']", :count => 1
530           assert_select "extensions", :count => 1 do
531             assert_select "id", open_note.id.to_s
532             assert_select "url", api_note_url(open_note, :format => "gpx")
533             assert_select "comment_url", comment_api_note_url(open_note, :format => "gpx")
534             assert_select "close_url", close_api_note_url(open_note, :format => "gpx")
535           end
536         end
537       end
538     end
539
540     def test_show_hidden_comment
541       note_with_hidden_comment = create(:note) do |note|
542         create(:note_comment, :note => note, :body => "Valid comment for hidden note")
543         create(:note_comment, :note => note, :visible => false)
544         create(:note_comment, :note => note, :body => "Another valid comment for hidden note")
545       end
546
547       get api_note_path(:id => note_with_hidden_comment, :format => "json")
548       assert_response :success
549       js = ActiveSupport::JSON.decode(@response.body)
550       assert_not_nil js
551       assert_equal "Feature", js["type"]
552       assert_equal note_with_hidden_comment.id, js["properties"]["id"]
553       assert_equal 2, js["properties"]["comments"].count
554       assert_equal "Valid comment for hidden note", js["properties"]["comments"][0]["text"]
555       assert_equal "Another valid comment for hidden note", js["properties"]["comments"][1]["text"]
556     end
557
558     def test_show_fail
559       get api_note_path(:id => 12345)
560       assert_response :not_found
561
562       get api_note_path(:id => create(:note, :status => "hidden"))
563       assert_response :gone
564     end
565
566     def test_destroy_success
567       open_note_with_comment = create(:note_with_comments)
568       user = create(:user)
569       moderator_user = create(:moderator_user)
570
571       delete api_note_path(:id => open_note_with_comment, :text => "This is a hide comment", :format => "json")
572       assert_response :unauthorized
573
574       auth_header = basic_authorization_header user.email, "test"
575
576       delete api_note_path(:id => open_note_with_comment, :text => "This is a hide comment", :format => "json"), :headers => auth_header
577       assert_response :forbidden
578
579       auth_header = basic_authorization_header moderator_user.email, "test"
580
581       delete api_note_path(:id => open_note_with_comment, :text => "This is a hide comment", :format => "json"), :headers => auth_header
582       assert_response :success
583       js = ActiveSupport::JSON.decode(@response.body)
584       assert_not_nil js
585       assert_equal "Feature", js["type"]
586       assert_equal open_note_with_comment.id, js["properties"]["id"]
587       assert_equal "hidden", js["properties"]["status"]
588       assert_equal 2, js["properties"]["comments"].count
589       assert_equal "hidden", js["properties"]["comments"].last["action"]
590       assert_equal "This is a hide comment", js["properties"]["comments"].last["text"]
591       assert_equal moderator_user.display_name, js["properties"]["comments"].last["user"]
592
593       get api_note_path(:id => open_note_with_comment, :format => "json"), :headers => auth_header
594       assert_response :success
595
596       auth_header = basic_authorization_header user.email, "test"
597
598       get api_note_path(:id => open_note_with_comment, :format => "json"), :headers => auth_header
599       assert_response :gone
600     end
601
602     def test_destroy_fail
603       user = create(:user)
604       moderator_user = create(:moderator_user)
605
606       delete api_note_path(:id => 12345, :format => "json")
607       assert_response :unauthorized
608
609       auth_header = basic_authorization_header user.email, "test"
610
611       delete api_note_path(:id => 12345, :format => "json"), :headers => auth_header
612       assert_response :forbidden
613
614       auth_header = basic_authorization_header moderator_user.email, "test"
615
616       delete api_note_path(:id => 12345, :format => "json"), :headers => auth_header
617       assert_response :not_found
618
619       hidden_note_with_comment = create(:note_with_comments, :status => "hidden")
620
621       delete api_note_path(:id => hidden_note_with_comment, :format => "json"), :headers => auth_header
622       assert_response :gone
623     end
624
625     def test_index_success
626       position = (1.1 * GeoRecord::SCALE).to_i
627       create(:note_with_comments, :latitude => position, :longitude => position)
628       create(:note_with_comments, :latitude => position, :longitude => position)
629
630       get api_notes_path(:bbox => "1,1,1.2,1.2", :format => "rss")
631       assert_response :success
632       assert_equal "application/rss+xml", @response.media_type
633       assert_select "rss", :count => 1 do
634         assert_select "channel", :count => 1 do
635           assert_select "description", :text => /1\.2/, :count => 1
636           assert_select "item", :count => 2
637         end
638       end
639
640       get api_notes_path(:bbox => "1,1,1.2,1.2", :format => "json")
641       assert_response :success
642       assert_equal "application/json", @response.media_type
643       js = ActiveSupport::JSON.decode(@response.body)
644       assert_not_nil js
645       assert_equal "FeatureCollection", js["type"]
646       assert_equal 2, js["features"].count
647
648       get api_notes_path(:bbox => "1,1,1.2,1.2", :format => "xml")
649       assert_response :success
650       assert_equal "application/xml", @response.media_type
651       assert_select "osm", :count => 1 do
652         assert_select "note", :count => 2
653       end
654
655       get api_notes_path(:bbox => "1,1,1.2,1.2", :format => "gpx")
656       assert_response :success
657       assert_equal "application/gpx+xml", @response.media_type
658       assert_select "gpx", :count => 1 do
659         assert_select "wpt", :count => 2
660       end
661     end
662
663     def test_index_limit
664       position = (1.1 * GeoRecord::SCALE).to_i
665       create(:note_with_comments, :latitude => position, :longitude => position)
666       create(:note_with_comments, :latitude => position, :longitude => position)
667
668       get api_notes_path(:bbox => "1,1,1.2,1.2", :limit => 1, :format => "rss")
669       assert_response :success
670       assert_equal "application/rss+xml", @response.media_type
671       assert_select "rss", :count => 1 do
672         assert_select "channel", :count => 1 do
673           assert_select "item", :count => 1
674         end
675       end
676
677       get api_notes_path(:bbox => "1,1,1.2,1.2", :limit => 1, :format => "json")
678       assert_response :success
679       assert_equal "application/json", @response.media_type
680       js = ActiveSupport::JSON.decode(@response.body)
681       assert_not_nil js
682       assert_equal "FeatureCollection", js["type"]
683       assert_equal 1, js["features"].count
684
685       get api_notes_path(:bbox => "1,1,1.2,1.2", :limit => 1, :format => "xml")
686       assert_response :success
687       assert_equal "application/xml", @response.media_type
688       assert_select "osm", :count => 1 do
689         assert_select "note", :count => 1
690       end
691
692       get api_notes_path(:bbox => "1,1,1.2,1.2", :limit => 1, :format => "gpx")
693       assert_response :success
694       assert_equal "application/gpx+xml", @response.media_type
695       assert_select "gpx", :count => 1 do
696         assert_select "wpt", :count => 1
697       end
698
699       get api_notes_path(:bbox => "1,1,1.2,1.2", :limit => Settings.max_note_query_limit, :format => "rss")
700       assert_response :success
701     end
702
703     def test_index_empty_area
704       get api_notes_path(:bbox => "5,5,5.1,5.1", :format => "rss")
705       assert_response :success
706       assert_equal "application/rss+xml", @response.media_type
707       assert_select "rss", :count => 1 do
708         assert_select "channel", :count => 1 do
709           assert_select "item", :count => 0
710         end
711       end
712
713       get api_notes_path(:bbox => "5,5,5.1,5.1", :format => "json")
714       assert_response :success
715       assert_equal "application/json", @response.media_type
716       js = ActiveSupport::JSON.decode(@response.body)
717       assert_not_nil js
718       assert_equal "FeatureCollection", js["type"]
719       assert_equal 0, js["features"].count
720
721       get api_notes_path(:bbox => "5,5,5.1,5.1", :format => "xml")
722       assert_response :success
723       assert_equal "application/xml", @response.media_type
724       assert_select "osm", :count => 1 do
725         assert_select "note", :count => 0
726       end
727
728       get api_notes_path(:bbox => "5,5,5.1,5.1", :format => "gpx")
729       assert_response :success
730       assert_equal "application/gpx+xml", @response.media_type
731       assert_select "gpx", :count => 1 do
732         assert_select "wpt", :count => 0
733       end
734     end
735
736     def test_index_large_area
737       get api_notes_path(:bbox => "-2.5,-2.5,2.5,2.5", :format => :json)
738       assert_response :success
739       assert_equal "application/json", @response.media_type
740
741       get api_notes_path(:l => "-2.5", :b => "-2.5", :r => "2.5", :t => "2.5", :format => :json)
742       assert_response :success
743       assert_equal "application/json", @response.media_type
744
745       get api_notes_path(:bbox => "-10,-10,12,12", :format => :json)
746       assert_response :bad_request
747       assert_equal "text/plain", @response.media_type
748
749       get api_notes_path(:l => "-10", :b => "-10", :r => "12", :t => "12", :format => :json)
750       assert_response :bad_request
751       assert_equal "text/plain", @response.media_type
752     end
753
754     def test_index_closed
755       create(:note_with_comments, :status => "closed", :closed_at => Time.now.utc - 5.days)
756       create(:note_with_comments, :status => "closed", :closed_at => Time.now.utc - 100.days)
757       create(:note_with_comments, :status => "hidden")
758       create(:note_with_comments)
759
760       # Open notes + closed in last 7 days
761       get api_notes_path(:bbox => "1,1,1.7,1.7", :closed => "7", :format => "json")
762       assert_response :success
763       assert_equal "application/json", @response.media_type
764       js = ActiveSupport::JSON.decode(@response.body)
765       assert_not_nil js
766       assert_equal "FeatureCollection", js["type"]
767       assert_equal 2, js["features"].count
768
769       # Only open notes
770       get api_notes_path(:bbox => "1,1,1.7,1.7", :closed => "0", :format => "json")
771       assert_response :success
772       assert_equal "application/json", @response.media_type
773       js = ActiveSupport::JSON.decode(@response.body)
774       assert_not_nil js
775       assert_equal "FeatureCollection", js["type"]
776       assert_equal 1, js["features"].count
777
778       # Open notes + all closed notes
779       get api_notes_path(:bbox => "1,1,1.7,1.7", :closed => "-1", :format => "json")
780       assert_response :success
781       assert_equal "application/json", @response.media_type
782       js = ActiveSupport::JSON.decode(@response.body)
783       assert_not_nil js
784       assert_equal "FeatureCollection", js["type"]
785       assert_equal 3, js["features"].count
786     end
787
788     def test_index_bad_params
789       get api_notes_path
790       assert_response :bad_request
791       assert_equal "The parameter bbox is required", @response.body
792
793       get api_notes_path(:bbox => "-2.5,-2.5,2.5")
794       assert_response :bad_request
795
796       get api_notes_path(:bbox => "-2.5,-2.5,2.5,2.5,2.5")
797       assert_response :bad_request
798
799       get api_notes_path(:b => "-2.5", :r => "2.5", :t => "2.5")
800       assert_response :bad_request
801
802       get api_notes_path(:l => "-2.5", :r => "2.5", :t => "2.5")
803       assert_response :bad_request
804
805       get api_notes_path(:l => "-2.5", :b => "-2.5", :t => "2.5")
806       assert_response :bad_request
807
808       get api_notes_path(:l => "-2.5", :b => "-2.5", :r => "2.5")
809       assert_response :bad_request
810
811       get api_notes_path(:bbox => "1,1,1.7,1.7", :limit => "0", :format => "json")
812       assert_response :bad_request
813
814       get api_notes_path(:bbox => "1,1,1.7,1.7", :limit => Settings.max_note_query_limit + 1, :format => "json")
815       assert_response :bad_request
816     end
817
818     def test_search_success
819       create(:note_with_comments)
820
821       get search_api_notes_path(:q => "note comment", :format => "xml")
822       assert_response :success
823       assert_equal "application/xml", @response.media_type
824       assert_select "osm", :count => 1 do
825         assert_select "note", :count => 1
826       end
827
828       get search_api_notes_path(:q => "note comment", :format => "json")
829       assert_response :success
830       assert_equal "application/json", @response.media_type
831       js = ActiveSupport::JSON.decode(@response.body)
832       assert_not_nil js
833       assert_equal "FeatureCollection", js["type"]
834       assert_equal 1, js["features"].count
835
836       get search_api_notes_path(:q => "note comment", :format => "rss")
837       assert_response :success
838       assert_equal "application/rss+xml", @response.media_type
839       assert_select "rss", :count => 1 do
840         assert_select "channel", :count => 1 do
841           assert_select "item", :count => 1
842         end
843       end
844
845       get search_api_notes_path(:q => "note comment", :format => "gpx")
846       assert_response :success
847       assert_equal "application/gpx+xml", @response.media_type
848       assert_select "gpx", :count => 1 do
849         assert_select "wpt", :count => 1
850       end
851
852       get search_api_notes_path(:q => "note comment", :limit => Settings.max_note_query_limit, :format => "xml")
853       assert_response :success
854     end
855
856     def test_search_by_display_name_success
857       user = create(:user)
858
859       create(:note) do |note|
860         create(:note_comment, :note => note, :author => user)
861       end
862
863       get search_api_notes_path(:display_name => user.display_name, :format => "xml")
864       assert_response :success
865       assert_equal "application/xml", @response.media_type
866       assert_select "osm", :count => 1 do
867         assert_select "note", :count => 1
868       end
869
870       get search_api_notes_path(:display_name => user.display_name, :format => "json")
871       assert_response :success
872       assert_equal "application/json", @response.media_type
873       js = ActiveSupport::JSON.decode(@response.body)
874       assert_not_nil js
875       assert_equal "FeatureCollection", js["type"]
876       assert_equal 1, js["features"].count
877
878       get search_api_notes_path(:display_name => user.display_name, :format => "rss")
879       assert_response :success
880       assert_equal "application/rss+xml", @response.media_type
881       assert_select "rss", :count => 1 do
882         assert_select "channel", :count => 1 do
883           assert_select "item", :count => 1
884         end
885       end
886
887       get search_api_notes_path(:display_name => user.display_name, :format => "gpx")
888       assert_response :success
889       assert_equal "application/gpx+xml", @response.media_type
890       assert_select "gpx", :count => 1 do
891         assert_select "wpt", :count => 1
892       end
893     end
894
895     def test_search_by_user_success
896       user = create(:user)
897
898       create(:note) do |note|
899         create(:note_comment, :note => note, :author => user)
900       end
901
902       get search_api_notes_path(:user => user.id, :format => "xml")
903       assert_response :success
904       assert_equal "application/xml", @response.media_type
905       assert_select "osm", :count => 1 do
906         assert_select "note", :count => 1
907       end
908
909       get search_api_notes_path(:user => user.id, :format => "json")
910       assert_response :success
911       assert_equal "application/json", @response.media_type
912       js = ActiveSupport::JSON.decode(@response.body)
913       assert_not_nil js
914       assert_equal "FeatureCollection", js["type"]
915       assert_equal 1, js["features"].count
916
917       get search_api_notes_path(:user => user.id, :format => "rss")
918       assert_response :success
919       assert_equal "application/rss+xml", @response.media_type
920       assert_select "rss", :count => 1 do
921         assert_select "channel", :count => 1 do
922           assert_select "item", :count => 1
923         end
924       end
925
926       get search_api_notes_path(:user => user.id, :format => "gpx")
927       assert_response :success
928       assert_equal "application/gpx+xml", @response.media_type
929       assert_select "gpx", :count => 1 do
930         assert_select "wpt", :count => 1
931       end
932     end
933
934     def test_search_by_bbox_success
935       notes = Array.new(5) do |i|
936         position = ((1.0 + (i * 0.1)) * GeoRecord::SCALE).to_i
937         create(:note_with_comments, :created_at => Time.parse("2020-01-01T00:00:00Z") + i.day, :latitude => position, :longitude => position)
938       end
939
940       get search_api_notes_path(:bbox => "1.0,1.0,1.6,1.6", :sort => "created_at", :order => "oldest", :format => "xml")
941       assert_response :success
942       assert_equal "application/xml", @response.media_type
943       assert_notes_in_order notes
944
945       get search_api_notes_path(:bbox => "1.25,1.25,1.45,1.45", :sort => "created_at", :order => "oldest", :format => "xml")
946       assert_response :success
947       assert_equal "application/xml", @response.media_type
948       assert_notes_in_order [notes[3], notes[4]]
949
950       get search_api_notes_path(:bbox => "2.0,2.0,2.5,2.5", :sort => "created_at", :order => "oldest", :format => "xml")
951       assert_response :success
952       assert_equal "application/xml", @response.media_type
953       assert_notes_in_order []
954     end
955
956     def test_search_no_match
957       create(:note_with_comments)
958
959       get search_api_notes_path(:q => "no match", :format => "xml")
960       assert_response :success
961       assert_equal "application/xml", @response.media_type
962       assert_select "osm", :count => 1 do
963         assert_select "note", :count => 0
964       end
965
966       get search_api_notes_path(:q => "no match", :format => "json")
967       assert_response :success
968       assert_equal "application/json", @response.media_type
969       js = ActiveSupport::JSON.decode(@response.body)
970       assert_not_nil js
971       assert_equal "FeatureCollection", js["type"]
972       assert_equal 0, js["features"].count
973
974       get search_api_notes_path(:q => "no match", :format => "rss")
975       assert_response :success
976       assert_equal "application/rss+xml", @response.media_type
977       assert_select "rss", :count => 1 do
978         assert_select "channel", :count => 1 do
979           assert_select "item", :count => 0
980         end
981       end
982
983       get search_api_notes_path(:q => "no match", :format => "gpx")
984       assert_response :success
985       assert_equal "application/gpx+xml", @response.media_type
986       assert_select "gpx", :count => 1 do
987         assert_select "wpt", :count => 0
988       end
989     end
990
991     def test_search_by_time_no_match
992       create(:note_with_comments)
993
994       get search_api_notes_path(:from => "01.01.2010", :to => "01.10.2010", :format => "xml")
995       assert_response :success
996       assert_equal "application/xml", @response.media_type
997       assert_select "osm", :count => 1 do
998         assert_select "note", :count => 0
999       end
1000
1001       get search_api_notes_path(:from => "01.01.2010", :to => "01.10.2010", :format => "json")
1002       assert_response :success
1003       assert_equal "application/json", @response.media_type
1004       js = ActiveSupport::JSON.decode(@response.body)
1005       assert_not_nil js
1006       assert_equal "FeatureCollection", js["type"]
1007       assert_equal 0, js["features"].count
1008
1009       get search_api_notes_path(:from => "01.01.2010", :to => "01.10.2010", :format => "rss")
1010       assert_response :success
1011       assert_equal "application/rss+xml", @response.media_type
1012       assert_select "rss", :count => 1 do
1013         assert_select "channel", :count => 1 do
1014           assert_select "item", :count => 0
1015         end
1016       end
1017
1018       get search_api_notes_path(:from => "01.01.2010", :to => "01.10.2010", :format => "gpx")
1019       assert_response :success
1020       assert_equal "application/gpx+xml", @response.media_type
1021       assert_select "gpx", :count => 1 do
1022         assert_select "wpt", :count => 0
1023       end
1024     end
1025
1026     def test_search_bad_params
1027       get search_api_notes_path(:q => "no match", :limit => "0", :format => "json")
1028       assert_response :bad_request
1029
1030       get search_api_notes_path(:q => "no match", :limit => Settings.max_note_query_limit + 1, :format => "json")
1031       assert_response :bad_request
1032
1033       get search_api_notes_path(:display_name => "non-existent")
1034       assert_response :bad_request
1035
1036       get search_api_notes_path(:user => "-1")
1037       assert_response :bad_request
1038
1039       get search_api_notes_path(:from => "wrong-date", :to => "wrong-date")
1040       assert_response :bad_request
1041
1042       get search_api_notes_path(:from => "01.01.2010", :to => "2010.01.2010")
1043       assert_response :bad_request
1044     end
1045
1046     def test_feed_success
1047       position = (1.1 * GeoRecord::SCALE).to_i
1048       create(:note_with_comments, :latitude => position, :longitude => position)
1049       create(:note_with_comments, :latitude => position, :longitude => position)
1050       position = (1.5 * GeoRecord::SCALE).to_i
1051       create(:note_with_comments, :latitude => position, :longitude => position)
1052       create(:note_with_comments, :latitude => position, :longitude => position)
1053
1054       get feed_api_notes_path(:format => "rss")
1055       assert_response :success
1056       assert_equal "application/rss+xml", @response.media_type
1057       assert_select "rss", :count => 1 do
1058         assert_select "channel", :count => 1 do
1059           assert_select "item", :count => 4
1060         end
1061       end
1062
1063       get feed_api_notes_path(:bbox => "1,1,1.2,1.2", :format => "rss")
1064       assert_response :success
1065       assert_equal "application/rss+xml", @response.media_type
1066       assert_select "rss", :count => 1 do
1067         assert_select "channel", :count => 1 do
1068           assert_select "description", :text => /1\.2/, :count => 1
1069           assert_select "item", :count => 2
1070         end
1071       end
1072
1073       get feed_api_notes_path(:bbox => "1,1,1.2,1.2", :limit => Settings.max_note_query_limit, :format => "rss")
1074       assert_response :success
1075     end
1076
1077     def test_feed_fail
1078       get feed_api_notes_path(:bbox => "1,1,1.2", :format => "rss")
1079       assert_response :bad_request
1080
1081       get feed_api_notes_path(:bbox => "1,1,1.2,1.2,1.2", :format => "rss")
1082       assert_response :bad_request
1083
1084       get feed_api_notes_path(:bbox => "1,1,1.2,1.2", :limit => "0", :format => "rss")
1085       assert_response :bad_request
1086
1087       get feed_api_notes_path(:bbox => "1,1,1.2,1.2", :limit => Settings.max_note_query_limit + 1, :format => "rss")
1088       assert_response :bad_request
1089     end
1090
1091     private
1092
1093     def assert_notes_in_order(notes)
1094       assert_select "osm>note", notes.size
1095       notes.each_with_index do |note, index|
1096         assert_select "osm>note:nth-child(#{index + 1})>id", :text => note.id.to_s, :count => 1
1097       end
1098     end
1099   end
1100 end