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