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