Merge branch 'develop' into feature/bulk-confirmation
[akkoma] / test / web / pleroma_api / controllers / emoji_pack_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.EmojiPackControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Tesla.Mock
9 import Pleroma.Factory
10
11 @emoji_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 setup do: clear_config([:instance, :public], true)
18
19 setup do
20 admin = insert(:user, is_admin: true)
21 token = insert(:oauth_admin_token, user: admin)
22
23 admin_conn =
24 build_conn()
25 |> assign(:user, admin)
26 |> assign(:token, token)
27
28 Pleroma.Emoji.reload()
29 {:ok, %{admin_conn: admin_conn}}
30 end
31
32 test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do
33 Config.put([:instance, :public], false)
34 conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
35 end
36
37 test "GET /api/pleroma/emoji/packs", %{conn: conn} do
38 resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
39
40 assert resp["count"] == 3
41
42 assert resp["packs"]
43 |> Map.keys()
44 |> length() == 3
45
46 shared = resp["packs"]["test_pack"]
47 assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}
48 assert Map.has_key?(shared["pack"], "download-sha256")
49 assert shared["pack"]["can-download"]
50 assert shared["pack"]["share-files"]
51
52 non_shared = resp["packs"]["test_pack_nonshared"]
53 assert non_shared["pack"]["share-files"] == false
54 assert non_shared["pack"]["can-download"] == false
55
56 resp =
57 conn
58 |> get("/api/pleroma/emoji/packs?page_size=1")
59 |> json_response_and_validate_schema(200)
60
61 assert resp["count"] == 3
62
63 packs = Map.keys(resp["packs"])
64
65 assert length(packs) == 1
66
67 [pack1] = packs
68
69 resp =
70 conn
71 |> get("/api/pleroma/emoji/packs?page_size=1&page=2")
72 |> json_response_and_validate_schema(200)
73
74 assert resp["count"] == 3
75 packs = Map.keys(resp["packs"])
76 assert length(packs) == 1
77 [pack2] = packs
78
79 resp =
80 conn
81 |> get("/api/pleroma/emoji/packs?page_size=1&page=3")
82 |> json_response_and_validate_schema(200)
83
84 assert resp["count"] == 3
85 packs = Map.keys(resp["packs"])
86 assert length(packs) == 1
87 [pack3] = packs
88 assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3
89 end
90
91 describe "GET /api/pleroma/emoji/packs/remote" do
92 test "shareable instance", %{admin_conn: admin_conn, conn: conn} do
93 resp =
94 conn
95 |> get("/api/pleroma/emoji/packs")
96 |> json_response_and_validate_schema(200)
97
98 mock(fn
99 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
100 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
101
102 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
103 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
104
105 %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} ->
106 json(resp)
107 end)
108
109 assert admin_conn
110 |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
111 |> json_response_and_validate_schema(200) == resp
112 end
113
114 test "non shareable instance", %{admin_conn: admin_conn} do
115 mock(fn
116 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
117 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
118
119 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
120 json(%{metadata: %{features: []}})
121 end)
122
123 assert admin_conn
124 |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
125 |> json_response_and_validate_schema(500) == %{
126 "error" => "The requested instance does not support sharing emoji packs"
127 }
128 end
129 end
130
131 describe "GET /api/pleroma/emoji/packs/:name/archive" do
132 test "download shared pack", %{conn: conn} do
133 resp =
134 conn
135 |> get("/api/pleroma/emoji/packs/test_pack/archive")
136 |> response(200)
137
138 {:ok, arch} = :zip.unzip(resp, [:memory])
139
140 assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
141 assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
142 end
143
144 test "non existing pack", %{conn: conn} do
145 assert conn
146 |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive")
147 |> json_response_and_validate_schema(:not_found) == %{
148 "error" => "Pack test_pack_for_import does not exist"
149 }
150 end
151
152 test "non downloadable pack", %{conn: conn} do
153 assert conn
154 |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive")
155 |> json_response_and_validate_schema(:forbidden) == %{
156 "error" =>
157 "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
158 }
159 end
160 end
161
162 describe "POST /api/pleroma/emoji/packs/download" do
163 test "shared pack from remote and non shared from fallback-src", %{
164 admin_conn: admin_conn,
165 conn: conn
166 } do
167 mock(fn
168 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
169 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
170
171 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
172 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
173
174 %{
175 method: :get,
176 url: "https://example.com/api/pleroma/emoji/packs/test_pack"
177 } ->
178 conn
179 |> get("/api/pleroma/emoji/packs/test_pack")
180 |> json_response_and_validate_schema(200)
181 |> json()
182
183 %{
184 method: :get,
185 url: "https://example.com/api/pleroma/emoji/packs/test_pack/archive"
186 } ->
187 conn
188 |> get("/api/pleroma/emoji/packs/test_pack/archive")
189 |> response(200)
190 |> text()
191
192 %{
193 method: :get,
194 url: "https://example.com/api/pleroma/emoji/packs/test_pack_nonshared"
195 } ->
196 conn
197 |> get("/api/pleroma/emoji/packs/test_pack_nonshared")
198 |> json_response_and_validate_schema(200)
199 |> json()
200
201 %{
202 method: :get,
203 url: "https://nonshared-pack"
204 } ->
205 text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
206 end)
207
208 assert admin_conn
209 |> put_req_header("content-type", "multipart/form-data")
210 |> post("/api/pleroma/emoji/packs/download", %{
211 url: "https://example.com",
212 name: "test_pack",
213 as: "test_pack2"
214 })
215 |> json_response_and_validate_schema(200) == "ok"
216
217 assert File.exists?("#{@emoji_path}/test_pack2/pack.json")
218 assert File.exists?("#{@emoji_path}/test_pack2/blank.png")
219
220 assert admin_conn
221 |> delete("/api/pleroma/emoji/packs/test_pack2")
222 |> json_response_and_validate_schema(200) == "ok"
223
224 refute File.exists?("#{@emoji_path}/test_pack2")
225
226 assert admin_conn
227 |> put_req_header("content-type", "multipart/form-data")
228 |> post(
229 "/api/pleroma/emoji/packs/download",
230 %{
231 url: "https://example.com",
232 name: "test_pack_nonshared",
233 as: "test_pack_nonshared2"
234 }
235 )
236 |> json_response_and_validate_schema(200) == "ok"
237
238 assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json")
239 assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png")
240
241 assert admin_conn
242 |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2")
243 |> json_response_and_validate_schema(200) == "ok"
244
245 refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
246 end
247
248 test "nonshareable instance", %{admin_conn: admin_conn} do
249 mock(fn
250 %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
251 json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
252
253 %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
254 json(%{metadata: %{features: []}})
255 end)
256
257 assert admin_conn
258 |> put_req_header("content-type", "multipart/form-data")
259 |> post(
260 "/api/pleroma/emoji/packs/download",
261 %{
262 url: "https://old-instance",
263 name: "test_pack",
264 as: "test_pack2"
265 }
266 )
267 |> json_response_and_validate_schema(500) == %{
268 "error" => "The requested instance does not support sharing emoji packs"
269 }
270 end
271
272 test "checksum fail", %{admin_conn: admin_conn} do
273 mock(fn
274 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
275 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
276
277 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
278 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
279
280 %{
281 method: :get,
282 url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha"
283 } ->
284 {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha")
285 %Tesla.Env{status: 200, body: Jason.encode!(pack)}
286
287 %{
288 method: :get,
289 url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha/archive"
290 } ->
291 %Tesla.Env{
292 status: 200,
293 body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip")
294 }
295 end)
296
297 assert admin_conn
298 |> put_req_header("content-type", "multipart/form-data")
299 |> post("/api/pleroma/emoji/packs/download", %{
300 url: "https://example.com",
301 name: "pack_bad_sha",
302 as: "pack_bad_sha2"
303 })
304 |> json_response_and_validate_schema(:internal_server_error) == %{
305 "error" => "SHA256 for the pack doesn't match the one sent by the server"
306 }
307 end
308
309 test "other error", %{admin_conn: admin_conn} do
310 mock(fn
311 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
312 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
313
314 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
315 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
316
317 %{
318 method: :get,
319 url: "https://example.com/api/pleroma/emoji/packs/test_pack"
320 } ->
321 {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack")
322 %Tesla.Env{status: 200, body: Jason.encode!(pack)}
323 end)
324
325 assert admin_conn
326 |> put_req_header("content-type", "multipart/form-data")
327 |> post("/api/pleroma/emoji/packs/download", %{
328 url: "https://example.com",
329 name: "test_pack",
330 as: "test_pack2"
331 })
332 |> json_response_and_validate_schema(:internal_server_error) == %{
333 "error" =>
334 "The pack was not set as shared and there is no fallback src to download from"
335 }
336 end
337 end
338
339 describe "PATCH /api/pleroma/emoji/packs/:name" do
340 setup do
341 pack_file = "#{@emoji_path}/test_pack/pack.json"
342 original_content = File.read!(pack_file)
343
344 on_exit(fn ->
345 File.write!(pack_file, original_content)
346 end)
347
348 {:ok,
349 pack_file: pack_file,
350 new_data: %{
351 "license" => "Test license changed",
352 "homepage" => "https://pleroma.social",
353 "description" => "Test description",
354 "share-files" => false
355 }}
356 end
357
358 test "for a pack without a fallback source", ctx do
359 assert ctx[:admin_conn]
360 |> put_req_header("content-type", "multipart/form-data")
361 |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]})
362 |> json_response_and_validate_schema(200) == ctx[:new_data]
363
364 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
365 end
366
367 test "for a pack with a fallback source", ctx do
368 mock(fn
369 %{
370 method: :get,
371 url: "https://nonshared-pack"
372 } ->
373 text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
374 end)
375
376 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
377
378 new_data_with_sha =
379 Map.put(
380 new_data,
381 "fallback-src-sha256",
382 "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D"
383 )
384
385 assert ctx[:admin_conn]
386 |> put_req_header("content-type", "multipart/form-data")
387 |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
388 |> json_response_and_validate_schema(200) == new_data_with_sha
389
390 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
391 end
392
393 test "when the fallback source doesn't have all the files", ctx do
394 mock(fn
395 %{
396 method: :get,
397 url: "https://nonshared-pack"
398 } ->
399 {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory])
400 text(empty_arch)
401 end)
402
403 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
404
405 assert ctx[:admin_conn]
406 |> put_req_header("content-type", "multipart/form-data")
407 |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
408 |> json_response_and_validate_schema(:bad_request) == %{
409 "error" => "The fallback archive does not have all files specified in pack.json"
410 }
411 end
412 end
413
414 describe "POST/DELETE /api/pleroma/emoji/packs/:name" do
415 test "creating and deleting a pack", %{admin_conn: admin_conn} do
416 assert admin_conn
417 |> post("/api/pleroma/emoji/packs/test_created")
418 |> json_response_and_validate_schema(200) == "ok"
419
420 assert File.exists?("#{@emoji_path}/test_created/pack.json")
421
422 assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
423 "pack" => %{},
424 "files" => %{},
425 "files_count" => 0
426 }
427
428 assert admin_conn
429 |> delete("/api/pleroma/emoji/packs/test_created")
430 |> json_response_and_validate_schema(200) == "ok"
431
432 refute File.exists?("#{@emoji_path}/test_created/pack.json")
433 end
434
435 test "if pack exists", %{admin_conn: admin_conn} do
436 path = Path.join(@emoji_path, "test_created")
437 File.mkdir(path)
438 pack_file = Jason.encode!(%{files: %{}, pack: %{}})
439 File.write!(Path.join(path, "pack.json"), pack_file)
440
441 assert admin_conn
442 |> post("/api/pleroma/emoji/packs/test_created")
443 |> json_response_and_validate_schema(:conflict) == %{
444 "error" => "A pack named \"test_created\" already exists"
445 }
446
447 on_exit(fn -> File.rm_rf(path) end)
448 end
449
450 test "with empty name", %{admin_conn: admin_conn} do
451 assert admin_conn
452 |> post("/api/pleroma/emoji/packs/ ")
453 |> json_response_and_validate_schema(:bad_request) == %{
454 "error" => "pack name cannot be empty"
455 }
456 end
457 end
458
459 test "deleting nonexisting pack", %{admin_conn: admin_conn} do
460 assert admin_conn
461 |> delete("/api/pleroma/emoji/packs/non_existing")
462 |> json_response_and_validate_schema(:not_found) == %{
463 "error" => "Pack non_existing does not exist"
464 }
465 end
466
467 test "deleting with empty name", %{admin_conn: admin_conn} do
468 assert admin_conn
469 |> delete("/api/pleroma/emoji/packs/ ")
470 |> json_response_and_validate_schema(:bad_request) == %{
471 "error" => "pack name cannot be empty"
472 }
473 end
474
475 test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
476 on_exit(fn ->
477 File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt")
478 File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
479 end)
480
481 resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
482
483 refute Map.has_key?(resp["packs"], "test_pack_for_import")
484
485 assert admin_conn
486 |> get("/api/pleroma/emoji/packs/import")
487 |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
488
489 resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
490 assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
491
492 File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
493 refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
494
495 emoji_txt_content = """
496 blank, blank.png, Fun
497 blank2, blank.png
498 foo, /emoji/test_pack_for_import/blank.png
499 bar
500 """
501
502 File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
503
504 assert admin_conn
505 |> get("/api/pleroma/emoji/packs/import")
506 |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
507
508 resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
509
510 assert resp["packs"]["test_pack_for_import"]["files"] == %{
511 "blank" => "blank.png",
512 "blank2" => "blank.png",
513 "foo" => "blank.png"
514 }
515 end
516
517 describe "GET /api/pleroma/emoji/packs/:name" do
518 test "shows pack.json", %{conn: conn} do
519 assert %{
520 "files" => files,
521 "files_count" => 2,
522 "pack" => %{
523 "can-download" => true,
524 "description" => "Test description",
525 "download-sha256" => _,
526 "homepage" => "https://pleroma.social",
527 "license" => "Test license",
528 "share-files" => true
529 }
530 } =
531 conn
532 |> get("/api/pleroma/emoji/packs/test_pack")
533 |> json_response_and_validate_schema(200)
534
535 assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"}
536
537 assert %{
538 "files" => files,
539 "files_count" => 2
540 } =
541 conn
542 |> get("/api/pleroma/emoji/packs/test_pack?page_size=1")
543 |> json_response_and_validate_schema(200)
544
545 assert files |> Map.keys() |> length() == 1
546
547 assert %{
548 "files" => files,
549 "files_count" => 2
550 } =
551 conn
552 |> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2")
553 |> json_response_and_validate_schema(200)
554
555 assert files |> Map.keys() |> length() == 1
556 end
557
558 test "non existing pack", %{conn: conn} do
559 assert conn
560 |> get("/api/pleroma/emoji/packs/non_existing")
561 |> json_response_and_validate_schema(:not_found) == %{
562 "error" => "Pack non_existing does not exist"
563 }
564 end
565
566 test "error name", %{conn: conn} do
567 assert conn
568 |> get("/api/pleroma/emoji/packs/ ")
569 |> json_response_and_validate_schema(:bad_request) == %{
570 "error" => "pack name cannot be empty"
571 }
572 end
573 end
574 end