emoji api error on not writable dir
[akkoma] / test / web / pleroma_api / controllers / emoji_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Tesla.Mock
9 import Pleroma.Factory
10
11 @emoji_dir_path Path.join(
12 Pleroma.Config.get!([:instance, :static_dir]),
13 "emoji"
14 )
15
16 clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
17 Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
18 end
19
20 test "shared & non-shared pack information in list_packs is ok" do
21 conn = build_conn()
22 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
23
24 assert Map.has_key?(resp, "test_pack")
25
26 pack = resp["test_pack"]
27
28 assert Map.has_key?(pack["pack"], "download-sha256")
29 assert pack["pack"]["can-download"]
30
31 assert pack["files"] == %{"blank" => "blank.png"}
32
33 # Non-shared pack
34
35 assert Map.has_key?(resp, "test_pack_nonshared")
36
37 pack = resp["test_pack_nonshared"]
38
39 refute pack["pack"]["shared"]
40 refute pack["pack"]["can-download"]
41 end
42
43 test "listing remote packs" do
44 admin = insert(:user, is_admin: true)
45 %{conn: conn} = oauth_access(["admin:write"], user: admin)
46
47 resp =
48 build_conn()
49 |> get(emoji_api_path(conn, :list_packs))
50 |> json_response(200)
51
52 mock(fn
53 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
54 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
55
56 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
57 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
58
59 %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} ->
60 json(resp)
61 end)
62
63 assert conn
64 |> post(emoji_api_path(conn, :list_from), %{instance_address: "https://example.com"})
65 |> json_response(200) == resp
66 end
67
68 test "downloading a shared pack from download_shared" do
69 conn = build_conn()
70
71 resp =
72 conn
73 |> get(emoji_api_path(conn, :download_shared, "test_pack"))
74 |> response(200)
75
76 {:ok, arch} = :zip.unzip(resp, [:memory])
77
78 assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
79 assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
80 end
81
82 test "downloading shared & unshared packs from another instance via download_from, deleting them" do
83 on_exit(fn ->
84 File.rm_rf!("#{@emoji_dir_path}/test_pack2")
85 File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2")
86 end)
87
88 mock(fn
89 %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
90 json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
91
92 %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
93 json(%{metadata: %{features: []}})
94
95 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
96 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
97
98 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
99 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
100
101 %{
102 method: :get,
103 url: "https://example.com/api/pleroma/emoji/packs/list"
104 } ->
105 conn = build_conn()
106
107 conn
108 |> get(emoji_api_path(conn, :list_packs))
109 |> json_response(200)
110 |> json()
111
112 %{
113 method: :get,
114 url: "https://example.com/api/pleroma/emoji/packs/download_shared/test_pack"
115 } ->
116 conn = build_conn()
117
118 conn
119 |> get(emoji_api_path(conn, :download_shared, "test_pack"))
120 |> response(200)
121 |> text()
122
123 %{
124 method: :get,
125 url: "https://nonshared-pack"
126 } ->
127 text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
128 end)
129
130 admin = insert(:user, is_admin: true)
131
132 conn =
133 build_conn()
134 |> assign(:user, admin)
135 |> assign(:token, insert(:oauth_admin_token, user: admin, scopes: ["admin:write"]))
136
137 assert (conn
138 |> put_req_header("content-type", "application/json")
139 |> post(
140 emoji_api_path(
141 conn,
142 :download_from
143 ),
144 %{
145 instance_address: "https://old-instance",
146 pack_name: "test_pack",
147 as: "test_pack2"
148 }
149 |> Jason.encode!()
150 )
151 |> json_response(500))["error"] =~ "does not support"
152
153 assert conn
154 |> put_req_header("content-type", "application/json")
155 |> post(
156 emoji_api_path(
157 conn,
158 :download_from
159 ),
160 %{
161 instance_address: "https://example.com",
162 pack_name: "test_pack",
163 as: "test_pack2"
164 }
165 |> Jason.encode!()
166 )
167 |> json_response(200) == "ok"
168
169 assert File.exists?("#{@emoji_dir_path}/test_pack2/pack.json")
170 assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png")
171
172 assert conn
173 |> delete(emoji_api_path(conn, :delete, "test_pack2"))
174 |> json_response(200) == "ok"
175
176 refute File.exists?("#{@emoji_dir_path}/test_pack2")
177
178 # non-shared, downloaded from the fallback URL
179
180 assert conn
181 |> put_req_header("content-type", "application/json")
182 |> post(
183 emoji_api_path(
184 conn,
185 :download_from
186 ),
187 %{
188 instance_address: "https://example.com",
189 pack_name: "test_pack_nonshared",
190 as: "test_pack_nonshared2"
191 }
192 |> Jason.encode!()
193 )
194 |> json_response(200) == "ok"
195
196 assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/pack.json")
197 assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png")
198
199 assert conn
200 |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2"))
201 |> json_response(200) == "ok"
202
203 refute File.exists?("#{@emoji_dir_path}/test_pack_nonshared2")
204 end
205
206 describe "updating pack metadata" do
207 setup do
208 pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
209 original_content = File.read!(pack_file)
210
211 on_exit(fn ->
212 File.write!(pack_file, original_content)
213 end)
214
215 admin = insert(:user, is_admin: true)
216 %{conn: conn} = oauth_access(["admin:write"], user: admin)
217
218 {:ok,
219 admin: admin,
220 conn: conn,
221 pack_file: pack_file,
222 new_data: %{
223 "license" => "Test license changed",
224 "homepage" => "https://pleroma.social",
225 "description" => "Test description",
226 "share-files" => false
227 }}
228 end
229
230 test "for a pack without a fallback source", ctx do
231 conn = ctx[:conn]
232
233 assert conn
234 |> post(
235 emoji_api_path(conn, :update_metadata, "test_pack"),
236 %{
237 "new_data" => ctx[:new_data]
238 }
239 )
240 |> json_response(200) == ctx[:new_data]
241
242 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
243 end
244
245 test "for a pack with a fallback source", ctx do
246 mock(fn
247 %{
248 method: :get,
249 url: "https://nonshared-pack"
250 } ->
251 text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
252 end)
253
254 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
255
256 new_data_with_sha =
257 Map.put(
258 new_data,
259 "fallback-src-sha256",
260 "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF"
261 )
262
263 conn = ctx[:conn]
264
265 assert conn
266 |> post(
267 emoji_api_path(conn, :update_metadata, "test_pack"),
268 %{
269 "new_data" => new_data
270 }
271 )
272 |> json_response(200) == new_data_with_sha
273
274 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
275 end
276
277 test "when the fallback source doesn't have all the files", ctx do
278 mock(fn
279 %{
280 method: :get,
281 url: "https://nonshared-pack"
282 } ->
283 {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory])
284 text(empty_arch)
285 end)
286
287 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
288
289 conn = ctx[:conn]
290
291 assert (conn
292 |> post(
293 emoji_api_path(conn, :update_metadata, "test_pack"),
294 %{
295 "new_data" => new_data
296 }
297 )
298 |> json_response(:bad_request))["error"] =~ "does not have all"
299 end
300 end
301
302 test "updating pack files" do
303 pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
304 original_content = File.read!(pack_file)
305
306 on_exit(fn ->
307 File.write!(pack_file, original_content)
308
309 File.rm_rf!("#{@emoji_dir_path}/test_pack/blank_url.png")
310 File.rm_rf!("#{@emoji_dir_path}/test_pack/dir")
311 File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2")
312 end)
313
314 admin = insert(:user, is_admin: true)
315 %{conn: conn} = oauth_access(["admin:write"], user: admin)
316
317 same_name = %{
318 "action" => "add",
319 "shortcode" => "blank",
320 "filename" => "dir/blank.png",
321 "file" => %Plug.Upload{
322 filename: "blank.png",
323 path: "#{@emoji_dir_path}/test_pack/blank.png"
324 }
325 }
326
327 different_name = %{same_name | "shortcode" => "blank_2"}
328
329 assert (conn
330 |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name)
331 |> json_response(:conflict))["error"] =~ "already exists"
332
333 assert conn
334 |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name)
335 |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"}
336
337 assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png")
338
339 assert conn
340 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
341 "action" => "update",
342 "shortcode" => "blank_2",
343 "new_shortcode" => "blank_3",
344 "new_filename" => "dir_2/blank_3.png"
345 })
346 |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"}
347
348 refute File.exists?("#{@emoji_dir_path}/test_pack/dir/")
349 assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png")
350
351 assert conn
352 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
353 "action" => "remove",
354 "shortcode" => "blank_3"
355 })
356 |> json_response(200) == %{"blank" => "blank.png"}
357
358 refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/")
359
360 mock(fn
361 %{
362 method: :get,
363 url: "https://test-blank/blank_url.png"
364 } ->
365 text(File.read!("#{@emoji_dir_path}/test_pack/blank.png"))
366 end)
367
368 # The name should be inferred from the URL ending
369 from_url = %{
370 "action" => "add",
371 "shortcode" => "blank_url",
372 "file" => "https://test-blank/blank_url.png"
373 }
374
375 assert conn
376 |> post(emoji_api_path(conn, :update_file, "test_pack"), from_url)
377 |> json_response(200) == %{
378 "blank" => "blank.png",
379 "blank_url" => "blank_url.png"
380 }
381
382 assert File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
383
384 assert conn
385 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
386 "action" => "remove",
387 "shortcode" => "blank_url"
388 })
389 |> json_response(200) == %{"blank" => "blank.png"}
390
391 refute File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
392 end
393
394 test "creating and deleting a pack" do
395 on_exit(fn ->
396 File.rm_rf!("#{@emoji_dir_path}/test_created")
397 end)
398
399 admin = insert(:user, is_admin: true)
400 %{conn: conn} = oauth_access(["admin:write"], user: admin)
401
402 assert conn
403 |> put_req_header("content-type", "application/json")
404 |> put(
405 emoji_api_path(
406 conn,
407 :create,
408 "test_created"
409 )
410 )
411 |> json_response(200) == "ok"
412
413 assert File.exists?("#{@emoji_dir_path}/test_created/pack.json")
414
415 assert Jason.decode!(File.read!("#{@emoji_dir_path}/test_created/pack.json")) == %{
416 "pack" => %{},
417 "files" => %{}
418 }
419
420 assert conn
421 |> delete(emoji_api_path(conn, :delete, "test_created"))
422 |> json_response(200) == "ok"
423
424 refute File.exists?("#{@emoji_dir_path}/test_created/pack.json")
425 end
426
427 test "filesystem import" do
428 on_exit(fn ->
429 File.rm!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt")
430 File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
431 end)
432
433 conn = build_conn()
434 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
435
436 refute Map.has_key?(resp, "test_pack_for_import")
437
438 admin = insert(:user, is_admin: true)
439 %{conn: conn} = oauth_access(["admin:write"], user: admin)
440
441 assert conn
442 |> post(emoji_api_path(conn, :import_from_fs))
443 |> json_response(200) == ["test_pack_for_import"]
444
445 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
446 assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
447
448 File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
449 refute File.exists?("#{@emoji_dir_path}/test_pack_for_import/pack.json")
450
451 emoji_txt_content = "blank, blank.png, Fun\n\nblank2, blank.png"
452
453 File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
454
455 assert conn
456 |> post(emoji_api_path(conn, :import_from_fs))
457 |> json_response(200) == ["test_pack_for_import"]
458
459 resp = build_conn() |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
460
461 assert resp["test_pack_for_import"]["files"] == %{
462 "blank" => "blank.png",
463 "blank2" => "blank.png"
464 }
465 end
466 end