Fix importing of GPX traces with a leading byte order marker
[rails.git] / test / controllers / api / traces_controller_test.rb
1 require "test_helper"
2 require "minitest/mock"
3
4 module Api
5   class TracesControllerTest < ActionController::TestCase
6     def teardown
7       File.unlink(*Dir.glob(File.join(Settings.gpx_trace_dir, "*.gpx")))
8       File.unlink(*Dir.glob(File.join(Settings.gpx_image_dir, "*.gif")))
9     end
10
11     ##
12     # test all routes which lead to this controller
13     def test_routes
14       assert_routing(
15         { :path => "/api/0.6/gpx/create", :method => :post },
16         { :controller => "api/traces", :action => "create" }
17       )
18       assert_routing(
19         { :path => "/api/0.6/gpx/1", :method => :get },
20         { :controller => "api/traces", :action => "show", :id => "1" }
21       )
22       assert_routing(
23         { :path => "/api/0.6/gpx/1", :method => :put },
24         { :controller => "api/traces", :action => "update", :id => "1" }
25       )
26       assert_routing(
27         { :path => "/api/0.6/gpx/1", :method => :delete },
28         { :controller => "api/traces", :action => "destroy", :id => "1" }
29       )
30       assert_recognizes(
31         { :controller => "api/traces", :action => "show", :id => "1" },
32         { :path => "/api/0.6/gpx/1/details", :method => :get }
33       )
34       assert_routing(
35         { :path => "/api/0.6/gpx/1/data", :method => :get },
36         { :controller => "api/traces", :action => "data", :id => "1" }
37       )
38       assert_routing(
39         { :path => "/api/0.6/gpx/1/data.xml", :method => :get },
40         { :controller => "api/traces", :action => "data", :id => "1", :format => "xml" }
41       )
42     end
43
44     # Check getting a specific trace through the api
45     def test_show
46       public_trace_file = create(:trace, :visibility => "public")
47
48       # First with no auth
49       get :show, :params => { :id => public_trace_file.id }
50       assert_response :unauthorized
51
52       # Now with some other user, which should work since the trace is public
53       basic_authorization create(:user).display_name, "test"
54       get :show, :params => { :id => public_trace_file.id }
55       assert_response :success
56
57       # And finally we should be able to do it with the owner of the trace
58       basic_authorization public_trace_file.user.display_name, "test"
59       get :show, :params => { :id => public_trace_file.id }
60       assert_response :success
61     end
62
63     # Check an anoymous trace can't be specifically fetched by another user
64     def test_show_anon
65       anon_trace_file = create(:trace, :visibility => "private")
66
67       # First with no auth
68       get :show, :params => { :id => anon_trace_file.id }
69       assert_response :unauthorized
70
71       # Now try with another user, which shouldn't work since the trace is anon
72       basic_authorization create(:user).display_name, "test"
73       get :show, :params => { :id => anon_trace_file.id }
74       assert_response :forbidden
75
76       # And finally we should be able to get the trace details with the trace owner
77       basic_authorization anon_trace_file.user.display_name, "test"
78       get :show, :params => { :id => anon_trace_file.id }
79       assert_response :success
80     end
81
82     # Check the api details for a trace that doesn't exist
83     def test_show_not_found
84       deleted_trace_file = create(:trace, :deleted)
85
86       # Try first with no auth, as it should require it
87       get :show, :params => { :id => 0 }
88       assert_response :unauthorized
89
90       # Login, and try again
91       basic_authorization deleted_trace_file.user.display_name, "test"
92       get :show, :params => { :id => 0 }
93       assert_response :not_found
94
95       # Now try a trace which did exist but has been deleted
96       basic_authorization deleted_trace_file.user.display_name, "test"
97       get :show, :params => { :id => deleted_trace_file.id }
98       assert_response :not_found
99     end
100
101     # Test downloading a trace through the api
102     def test_data
103       public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
104
105       # First with no auth
106       get :data, :params => { :id => public_trace_file.id }
107       assert_response :unauthorized
108
109       # Now with some other user, which should work since the trace is public
110       basic_authorization create(:user).display_name, "test"
111       get :data, :params => { :id => public_trace_file.id }
112       check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
113
114       # And finally we should be able to do it with the owner of the trace
115       basic_authorization public_trace_file.user.display_name, "test"
116       get :data, :params => { :id => public_trace_file.id }
117       check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
118     end
119
120     # Test downloading a compressed trace through the api
121     def test_data_compressed
122       identifiable_trace_file = create(:trace, :visibility => "identifiable", :fixture => "d")
123
124       # Authenticate as the owner of the trace we will be using
125       basic_authorization identifiable_trace_file.user.display_name, "test"
126
127       # First get the data as is
128       get :data, :params => { :id => identifiable_trace_file.id }
129       check_trace_data identifiable_trace_file, "c6422a3d8750faae49ed70e7e8a51b93", "application/x-gzip", "gpx.gz"
130
131       # Now ask explicitly for XML format
132       get :data, :params => { :id => identifiable_trace_file.id, :format => "xml" }
133       check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d", "application/xml", "xml"
134
135       # Now ask explicitly for GPX format
136       get :data, :params => { :id => identifiable_trace_file.id, :format => "gpx" }
137       check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d"
138     end
139
140     # Check an anonymous trace can't be downloaded by another user through the api
141     def test_data_anon
142       anon_trace_file = create(:trace, :visibility => "private", :fixture => "b")
143
144       # First with no auth
145       get :data, :params => { :id => anon_trace_file.id }
146       assert_response :unauthorized
147
148       # Now with some other user, which shouldn't work since the trace is anon
149       basic_authorization create(:user).display_name, "test"
150       get :data, :params => { :id => anon_trace_file.id }
151       assert_response :forbidden
152
153       # And finally we should be able to do it with the owner of the trace
154       basic_authorization anon_trace_file.user.display_name, "test"
155       get :data, :params => { :id => anon_trace_file.id }
156       check_trace_data anon_trace_file, "db4cb5ed2d7d2b627b3b504296c4f701"
157     end
158
159     # Test downloading a trace that doesn't exist through the api
160     def test_data_not_found
161       deleted_trace_file = create(:trace, :deleted)
162
163       # Try first with no auth, as it should require it
164       get :data, :params => { :id => 0 }
165       assert_response :unauthorized
166
167       # Login, and try again
168       basic_authorization create(:user).display_name, "test"
169       get :data, :params => { :id => 0 }
170       assert_response :not_found
171
172       # Now try a trace which did exist but has been deleted
173       basic_authorization deleted_trace_file.user.display_name, "test"
174       get :data, :params => { :id => deleted_trace_file.id }
175       assert_response :not_found
176     end
177
178     # Test creating a trace through the api
179     def test_create
180       # Get file to use
181       fixture = Rails.root.join("test", "gpx", "fixtures", "a.gpx")
182       file = Rack::Test::UploadedFile.new(fixture, "application/gpx+xml")
183       user = create(:user)
184
185       # First with no auth
186       post :create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
187       assert_response :unauthorized
188
189       # Rewind the file
190       file.rewind
191
192       # Now authenticated
193       create(:user_preference, :user => user, :k => "gps.trace.visibility", :v => "identifiable")
194       assert_not_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
195       basic_authorization user.display_name, "test"
196       post :create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
197       assert_response :success
198       trace = Trace.find(response.body.to_i)
199       assert_equal "a.gpx", trace.name
200       assert_equal "New Trace", trace.description
201       assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
202       assert_equal "trackable", trace.visibility
203       assert_equal false, trace.inserted
204       assert_equal File.new(fixture).read, File.new(trace.trace_name).read
205       trace.destroy
206       assert_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
207
208       # Rewind the file
209       file.rewind
210
211       # Now authenticated, with the legacy public flag
212       assert_not_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
213       basic_authorization user.display_name, "test"
214       post :create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 1 }
215       assert_response :success
216       trace = Trace.find(response.body.to_i)
217       assert_equal "a.gpx", trace.name
218       assert_equal "New Trace", trace.description
219       assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
220       assert_equal "public", trace.visibility
221       assert_equal false, trace.inserted
222       assert_equal File.new(fixture).read, File.new(trace.trace_name).read
223       trace.destroy
224       assert_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
225
226       # Rewind the file
227       file.rewind
228
229       # Now authenticated, with the legacy private flag
230       second_user = create(:user)
231       assert_nil second_user.preferences.where(:k => "gps.trace.visibility").first
232       basic_authorization second_user.display_name, "test"
233       post :create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 0 }
234       assert_response :success
235       trace = Trace.find(response.body.to_i)
236       assert_equal "a.gpx", trace.name
237       assert_equal "New Trace", trace.description
238       assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
239       assert_equal "private", trace.visibility
240       assert_equal false, trace.inserted
241       assert_equal File.new(fixture).read, File.new(trace.trace_name).read
242       trace.destroy
243       assert_equal "private", second_user.preferences.where(:k => "gps.trace.visibility").first.v
244     end
245
246     # Check updating a trace through the api
247     def test_update
248       public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
249       deleted_trace_file = create(:trace, :deleted)
250       anon_trace_file = create(:trace, :visibility => "private")
251
252       # First with no auth
253       put :update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
254       assert_response :unauthorized
255
256       # Now with some other user, which should fail
257       basic_authorization create(:user).display_name, "test"
258       put :update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
259       assert_response :forbidden
260
261       # Now with a trace which doesn't exist
262       basic_authorization create(:user).display_name, "test"
263       put :update, :params => { :id => 0 }, :body => public_trace_file.to_xml.to_s
264       assert_response :not_found
265
266       # Now with a trace which did exist but has been deleted
267       basic_authorization deleted_trace_file.user.display_name, "test"
268       put :update, :params => { :id => deleted_trace_file.id }, :body => deleted_trace_file.to_xml.to_s
269       assert_response :not_found
270
271       # Now try an update with the wrong ID
272       basic_authorization public_trace_file.user.display_name, "test"
273       put :update, :params => { :id => public_trace_file.id }, :body => anon_trace_file.to_xml.to_s
274       assert_response :bad_request,
275                       "should not be able to update a trace with a different ID from the XML"
276
277       # And finally try an update that should work
278       basic_authorization public_trace_file.user.display_name, "test"
279       t = public_trace_file
280       t.description = "Changed description"
281       t.visibility = "private"
282       put :update, :params => { :id => t.id }, :body => t.to_xml.to_s
283       assert_response :success
284       nt = Trace.find(t.id)
285       assert_equal nt.description, t.description
286       assert_equal nt.visibility, t.visibility
287     end
288
289     # Test that updating a trace doesn't duplicate the tags
290     def test_update_tags
291       tracetag = create(:tracetag)
292       trace = tracetag.trace
293       basic_authorization trace.user.display_name, "test"
294
295       put :update, :params => { :id => trace.id }, :body => trace.to_xml.to_s
296       assert_response :success
297
298       updated = Trace.find(trace.id)
299       # Ensure there's only one tag in the database after updating
300       assert_equal Tracetag.count, 1
301       # The new tag object might have a different id, so check the string representation
302       assert_equal trace.tagstring, updated.tagstring
303     end
304
305     # Check deleting a trace through the api
306     def test_destroy
307       public_trace_file = create(:trace, :visibility => "public")
308
309       # First with no auth
310       delete :destroy, :params => { :id => public_trace_file.id }
311       assert_response :unauthorized
312
313       # Now with some other user, which should fail
314       basic_authorization create(:user).display_name, "test"
315       delete :destroy, :params => { :id => public_trace_file.id }
316       assert_response :forbidden
317
318       # Now with a trace which doesn't exist
319       basic_authorization create(:user).display_name, "test"
320       delete :destroy, :params => { :id => 0 }
321       assert_response :not_found
322
323       # And finally we should be able to do it with the owner of the trace
324       basic_authorization public_trace_file.user.display_name, "test"
325       delete :destroy, :params => { :id => public_trace_file.id }
326       assert_response :success
327
328       # Try it a second time, which should fail
329       basic_authorization public_trace_file.user.display_name, "test"
330       delete :destroy, :params => { :id => public_trace_file.id }
331       assert_response :not_found
332     end
333
334     private
335
336     def check_trace_data(trace, digest, content_type = "application/gpx+xml", extension = "gpx")
337       assert_response :success
338       assert_equal digest, Digest::MD5.hexdigest(response.body)
339       assert_equal content_type, response.content_type
340       assert_equal "attachment; filename=\"#{trace.id}.#{extension}\"", @response.header["Content-Disposition"]
341     end
342   end
343 end