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