CI: Bump lint stage to elixir-1.12
[akkoma] / test / pleroma / web / media_proxy / media_proxy_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Mock
9
10 alias Pleroma.Web.MediaProxy
11 alias Plug.Conn
12
13 describe "Media Proxy" do
14 setup do
15 clear_config([:media_proxy, :enabled], true)
16 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
17
18 [url: MediaProxy.encode_url("https://google.fn/test.png")]
19 end
20
21 test "it returns 404 when disabled", %{conn: conn} do
22 clear_config([:media_proxy, :enabled], false)
23
24 assert %Conn{
25 status: 404,
26 resp_body: "Not Found"
27 } = get(conn, "/proxy/hhgfh/eeeee")
28
29 assert %Conn{
30 status: 404,
31 resp_body: "Not Found"
32 } = get(conn, "/proxy/hhgfh/eeee/fff")
33 end
34
35 test "it returns 403 for invalid signature", %{conn: conn, url: url} do
36 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000")
37 %{path: path} = URI.parse(url)
38
39 assert %Conn{
40 status: 403,
41 resp_body: "Forbidden"
42 } = get(conn, path)
43
44 assert %Conn{
45 status: 403,
46 resp_body: "Forbidden"
47 } = get(conn, "/proxy/hhgfh/eeee")
48
49 assert %Conn{
50 status: 403,
51 resp_body: "Forbidden"
52 } = get(conn, "/proxy/hhgfh/eeee/fff")
53 end
54
55 test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
56 invalid_url = String.replace(url, "test.png", "test-file.png")
57 response = get(conn, invalid_url)
58 assert response.status == 302
59 assert redirected_to(response) == url
60 end
61
62 test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do
63 with_mock Pleroma.ReverseProxy,
64 call: fn _conn, _url, _opts -> %Conn{status: :success} end do
65 assert %Conn{status: :success} = get(conn, url)
66 end
67 end
68
69 test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do
70 MediaProxy.put_in_banned_urls("https://google.fn/test.png")
71
72 with_mock Pleroma.ReverseProxy,
73 call: fn _conn, _url, _opts -> %Conn{status: :success} end do
74 assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
75 end
76 end
77 end
78
79 describe "Media Preview Proxy" do
80 def assert_dependencies_installed do
81 missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies()
82
83 assert missing_dependencies == [],
84 "Error: missing dependencies (please refer to `docs/installation`): #{inspect(missing_dependencies)}"
85 end
86
87 setup do
88 clear_config([:media_proxy, :enabled], true)
89 clear_config([:media_preview_proxy, :enabled], true)
90 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
91
92 original_url = "https://google.fn/test.png"
93
94 [
95 url: MediaProxy.encode_preview_url(original_url),
96 media_proxy_url: MediaProxy.encode_url(original_url)
97 ]
98 end
99
100 test "returns 404 when media proxy is disabled", %{conn: conn} do
101 clear_config([:media_proxy, :enabled], false)
102
103 assert %Conn{
104 status: 404,
105 resp_body: "Not Found"
106 } = get(conn, "/proxy/preview/hhgfh/eeeee")
107
108 assert %Conn{
109 status: 404,
110 resp_body: "Not Found"
111 } = get(conn, "/proxy/preview/hhgfh/fff")
112 end
113
114 test "returns 404 when disabled", %{conn: conn} do
115 clear_config([:media_preview_proxy, :enabled], false)
116
117 assert %Conn{
118 status: 404,
119 resp_body: "Not Found"
120 } = get(conn, "/proxy/preview/hhgfh/eeeee")
121
122 assert %Conn{
123 status: 404,
124 resp_body: "Not Found"
125 } = get(conn, "/proxy/preview/hhgfh/fff")
126 end
127
128 test "it returns 403 for invalid signature", %{conn: conn, url: url} do
129 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000")
130 %{path: path} = URI.parse(url)
131
132 assert %Conn{
133 status: 403,
134 resp_body: "Forbidden"
135 } = get(conn, path)
136
137 assert %Conn{
138 status: 403,
139 resp_body: "Forbidden"
140 } = get(conn, "/proxy/preview/hhgfh/eeee")
141
142 assert %Conn{
143 status: 403,
144 resp_body: "Forbidden"
145 } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
146 end
147
148 test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
149 invalid_url = String.replace(url, "test.png", "test-file.png")
150 response = get(conn, invalid_url)
151 assert response.status == 302
152 assert redirected_to(response) == url
153 end
154
155 test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
156 conn: conn,
157 url: url,
158 media_proxy_url: media_proxy_url
159 } do
160 Tesla.Mock.mock(fn
161 %{method: "head", url: ^media_proxy_url} ->
162 %Tesla.Env{status: 500, body: ""}
163 end)
164
165 response = get(conn, url)
166 assert response.status == 424
167 assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
168 end
169
170 test "redirects to media proxy URI on unsupported content type", %{
171 conn: conn,
172 url: url,
173 media_proxy_url: media_proxy_url
174 } do
175 Tesla.Mock.mock(fn
176 %{method: "head", url: ^media_proxy_url} ->
177 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
178 end)
179
180 response = get(conn, url)
181 assert response.status == 302
182 assert redirected_to(response) == media_proxy_url
183 end
184
185 test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
186 conn: conn,
187 url: url,
188 media_proxy_url: media_proxy_url
189 } do
190 assert_dependencies_installed()
191
192 # Setting a high :min_content_length to ensure this scenario is not affected by its logic
193 clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
194
195 Tesla.Mock.mock(fn
196 %{method: "head", url: ^media_proxy_url} ->
197 %Tesla.Env{
198 status: 200,
199 body: "",
200 headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
201 }
202
203 %{method: :get, url: ^media_proxy_url} ->
204 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
205 end)
206
207 response = get(conn, url <> "?static=true")
208
209 assert response.status == 200
210 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
211 assert response.resp_body != ""
212 end
213
214 test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
215 %{
216 conn: conn,
217 url: url,
218 media_proxy_url: media_proxy_url
219 } do
220 Tesla.Mock.mock(fn
221 %{method: "head", url: ^media_proxy_url} ->
222 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
223 end)
224
225 response = get(conn, url)
226
227 assert response.status == 302
228 assert redirected_to(response) == media_proxy_url
229 end
230
231 test "with `static` param and non-GIF image preview requested, " <>
232 "redirects to media preview proxy URI without `static` param",
233 %{
234 conn: conn,
235 url: url,
236 media_proxy_url: media_proxy_url
237 } do
238 Tesla.Mock.mock(fn
239 %{method: "head", url: ^media_proxy_url} ->
240 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
241 end)
242
243 response = get(conn, url <> "?static=true")
244
245 assert response.status == 302
246 assert redirected_to(response) == url
247 end
248
249 test "with :min_content_length setting not matched by Content-Length header, " <>
250 "redirects to media proxy URI",
251 %{
252 conn: conn,
253 url: url,
254 media_proxy_url: media_proxy_url
255 } do
256 clear_config([:media_preview_proxy, :min_content_length], 100_000)
257
258 Tesla.Mock.mock(fn
259 %{method: "head", url: ^media_proxy_url} ->
260 %Tesla.Env{
261 status: 200,
262 body: "",
263 headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
264 }
265 end)
266
267 response = get(conn, url)
268
269 assert response.status == 302
270 assert redirected_to(response) == media_proxy_url
271 end
272
273 test "thumbnails PNG images into PNG", %{
274 conn: conn,
275 url: url,
276 media_proxy_url: media_proxy_url
277 } do
278 assert_dependencies_installed()
279
280 Tesla.Mock.mock(fn
281 %{method: "head", url: ^media_proxy_url} ->
282 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
283
284 %{method: :get, url: ^media_proxy_url} ->
285 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
286 end)
287
288 response = get(conn, url)
289
290 assert response.status == 200
291 assert Conn.get_resp_header(response, "content-type") == ["image/png"]
292 assert response.resp_body != ""
293 end
294
295 test "thumbnails JPEG images into JPEG", %{
296 conn: conn,
297 url: url,
298 media_proxy_url: media_proxy_url
299 } do
300 assert_dependencies_installed()
301
302 Tesla.Mock.mock(fn
303 %{method: "head", url: ^media_proxy_url} ->
304 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
305
306 %{method: :get, url: ^media_proxy_url} ->
307 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
308 end)
309
310 response = get(conn, url)
311
312 assert response.status == 200
313 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
314 assert response.resp_body != ""
315 end
316
317 test "redirects to media proxy URI in case of thumbnailing error", %{
318 conn: conn,
319 url: url,
320 media_proxy_url: media_proxy_url
321 } do
322 Tesla.Mock.mock(fn
323 %{method: "head", url: ^media_proxy_url} ->
324 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
325
326 %{method: :get, url: ^media_proxy_url} ->
327 %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
328 end)
329
330 response = get(conn, url)
331
332 assert response.status == 302
333 assert redirected_to(response) == media_proxy_url
334 end
335 end
336 end