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