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