Apply 2 suggestion(s) to 2 file(s)
[akkoma] / test / pleroma / web / activity_pub / utils_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.UtilsTest do
6 use Pleroma.DataCase, async: true
7 alias Pleroma.Activity
8 alias Pleroma.Object
9 alias Pleroma.Repo
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.Utils
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.CommonAPI
14
15 import Pleroma.Factory
16
17 require Pleroma.Constants
18
19 describe "fetch the latest Follow" do
20 test "fetches the latest Follow activity" do
21 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
22 follower = User.get_cached_by_ap_id(activity.data["actor"])
23 followed = User.get_cached_by_ap_id(activity.data["object"])
24
25 assert activity == Utils.fetch_latest_follow(follower, followed)
26 end
27 end
28
29 describe "determine_explicit_mentions()" do
30 test "works with an object that has mentions" do
31 object = %{
32 "tag" => [
33 %{
34 "type" => "Mention",
35 "href" => "https://example.com/~alyssa",
36 "name" => "Alyssa P. Hacker"
37 }
38 ]
39 }
40
41 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
42 end
43
44 test "works with an object that does not have mentions" do
45 object = %{
46 "tag" => [
47 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
48 ]
49 }
50
51 assert Utils.determine_explicit_mentions(object) == []
52 end
53
54 test "works with an object that has mentions and other tags" do
55 object = %{
56 "tag" => [
57 %{
58 "type" => "Mention",
59 "href" => "https://example.com/~alyssa",
60 "name" => "Alyssa P. Hacker"
61 },
62 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
63 ]
64 }
65
66 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
67 end
68
69 test "works with an object that has no tags" do
70 object = %{}
71
72 assert Utils.determine_explicit_mentions(object) == []
73 end
74
75 test "works with an object that has only IR tags" do
76 object = %{"tag" => ["2hu"]}
77
78 assert Utils.determine_explicit_mentions(object) == []
79 end
80
81 test "works with an object has tags as map" do
82 object = %{
83 "tag" => %{
84 "type" => "Mention",
85 "href" => "https://example.com/~alyssa",
86 "name" => "Alyssa P. Hacker"
87 }
88 }
89
90 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
91 end
92 end
93
94 describe "make_like_data" do
95 setup do
96 user = insert(:user)
97 other_user = insert(:user)
98 third_user = insert(:user)
99 [user: user, other_user: other_user, third_user: third_user]
100 end
101
102 test "addresses actor's follower address if the activity is public", %{
103 user: user,
104 other_user: other_user,
105 third_user: third_user
106 } do
107 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
108 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
109
110 {:ok, activity} =
111 CommonAPI.post(user, %{
112 status:
113 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
114 })
115
116 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
117 assert Enum.sort(to) == expected_to
118 assert Enum.sort(cc) == expected_cc
119 end
120
121 test "does not adress actor's follower address if the activity is not public", %{
122 user: user,
123 other_user: other_user,
124 third_user: third_user
125 } do
126 expected_to = Enum.sort([user.ap_id])
127 expected_cc = [third_user.ap_id]
128
129 {:ok, activity} =
130 CommonAPI.post(user, %{
131 status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
132 visibility: "private"
133 })
134
135 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
136 assert Enum.sort(to) == expected_to
137 assert Enum.sort(cc) == expected_cc
138 end
139 end
140
141 test "make_json_ld_header/0" do
142 assert Utils.make_json_ld_header() == %{
143 "@context" => [
144 "https://www.w3.org/ns/activitystreams",
145 "http://localhost:4001/schemas/litepub-0.1.jsonld",
146 %{
147 "@language" => "und"
148 }
149 ]
150 }
151 end
152
153 describe "get_existing_votes" do
154 test "fetches existing votes" do
155 user = insert(:user)
156 other_user = insert(:user)
157
158 {:ok, activity} =
159 CommonAPI.post(user, %{
160 status: "How do I pronounce LaTeX?",
161 poll: %{
162 options: ["laytekh", "lahtekh", "latex"],
163 expires_in: 20,
164 multiple: true
165 }
166 })
167
168 object = Object.normalize(activity, fetch: false)
169 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
170 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
171 end
172
173 test "fetches only Create activities" do
174 user = insert(:user)
175 other_user = insert(:user)
176
177 {:ok, activity} =
178 CommonAPI.post(user, %{
179 status: "Are we living in a society?",
180 poll: %{
181 options: ["yes", "no"],
182 expires_in: 20
183 }
184 })
185
186 object = Object.normalize(activity, fetch: false)
187 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
188 {:ok, _activity} = CommonAPI.favorite(user, activity.id)
189 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
190 assert fetched_vote.id == vote.id
191 end
192 end
193
194 describe "update_follow_state_for_all/2" do
195 test "updates the state of all Follow activities with the same actor and object" do
196 user = insert(:user, is_locked: true)
197 follower = insert(:user)
198
199 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
200 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
201
202 data =
203 follow_activity_two.data
204 |> Map.put("state", "accept")
205
206 cng = Ecto.Changeset.change(follow_activity_two, data: data)
207
208 {:ok, follow_activity_two} = Repo.update(cng)
209
210 {:ok, follow_activity_two} =
211 Utils.update_follow_state_for_all(follow_activity_two, "accept")
212
213 assert refresh_record(follow_activity).data["state"] == "accept"
214 assert refresh_record(follow_activity_two).data["state"] == "accept"
215 end
216 end
217
218 describe "update_follow_state/2" do
219 test "updates the state of the given follow activity" do
220 user = insert(:user, is_locked: true)
221 follower = insert(:user)
222
223 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
224 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
225
226 data =
227 follow_activity_two.data
228 |> Map.put("state", "accept")
229
230 cng = Ecto.Changeset.change(follow_activity_two, data: data)
231
232 {:ok, follow_activity_two} = Repo.update(cng)
233
234 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
235
236 assert refresh_record(follow_activity).data["state"] == "pending"
237 assert refresh_record(follow_activity_two).data["state"] == "reject"
238 end
239 end
240
241 describe "update_element_in_object/3" do
242 test "updates likes" do
243 user = insert(:user)
244 activity = insert(:note_activity)
245 object = Object.normalize(activity, fetch: false)
246
247 assert {:ok, updated_object} =
248 Utils.update_element_in_object(
249 "like",
250 [user.ap_id],
251 object
252 )
253
254 assert updated_object.data["likes"] == [user.ap_id]
255 assert updated_object.data["like_count"] == 1
256 end
257 end
258
259 describe "add_like_to_object/2" do
260 test "add actor to likes" do
261 user = insert(:user)
262 user2 = insert(:user)
263 object = insert(:note)
264
265 assert {:ok, updated_object} =
266 Utils.add_like_to_object(
267 %Activity{data: %{"actor" => user.ap_id}},
268 object
269 )
270
271 assert updated_object.data["likes"] == [user.ap_id]
272 assert updated_object.data["like_count"] == 1
273
274 assert {:ok, updated_object2} =
275 Utils.add_like_to_object(
276 %Activity{data: %{"actor" => user2.ap_id}},
277 updated_object
278 )
279
280 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
281 assert updated_object2.data["like_count"] == 2
282 end
283 end
284
285 describe "remove_like_from_object/2" do
286 test "removes ap_id from likes" do
287 user = insert(:user)
288 user2 = insert(:user)
289 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
290
291 assert {:ok, updated_object} =
292 Utils.remove_like_from_object(
293 %Activity{data: %{"actor" => user.ap_id}},
294 object
295 )
296
297 assert updated_object.data["likes"] == [user2.ap_id]
298 assert updated_object.data["like_count"] == 1
299 end
300 end
301
302 describe "get_existing_like/2" do
303 test "fetches existing like" do
304 note_activity = insert(:note_activity)
305 assert object = Object.normalize(note_activity, fetch: false)
306
307 user = insert(:user)
308 refute Utils.get_existing_like(user.ap_id, object)
309 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
310
311 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
312 end
313 end
314
315 describe "get_get_existing_announce/2" do
316 test "returns nil if announce not found" do
317 actor = insert(:user)
318 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
319 end
320
321 test "fetches existing announce" do
322 note_activity = insert(:note_activity)
323 assert object = Object.normalize(note_activity, fetch: false)
324 actor = insert(:user)
325
326 {:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
327 assert Utils.get_existing_announce(actor.ap_id, object) == announce
328 end
329 end
330
331 describe "fetch_latest_block/2" do
332 test "fetches last block activities" do
333 user1 = insert(:user)
334 user2 = insert(:user)
335
336 assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
337 assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
338 assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
339
340 assert Utils.fetch_latest_block(user1, user2) == activity
341 end
342 end
343
344 describe "recipient_in_message/3" do
345 test "returns true when recipient in `to`" do
346 recipient = insert(:user)
347 actor = insert(:user)
348 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
349
350 assert Utils.recipient_in_message(
351 recipient,
352 actor,
353 %{"to" => [recipient.ap_id], "cc" => ""}
354 )
355 end
356
357 test "returns true when recipient in `cc`" do
358 recipient = insert(:user)
359 actor = insert(:user)
360 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
361
362 assert Utils.recipient_in_message(
363 recipient,
364 actor,
365 %{"cc" => [recipient.ap_id], "to" => ""}
366 )
367 end
368
369 test "returns true when recipient in `bto`" do
370 recipient = insert(:user)
371 actor = insert(:user)
372 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
373
374 assert Utils.recipient_in_message(
375 recipient,
376 actor,
377 %{"bcc" => "", "bto" => [recipient.ap_id]}
378 )
379 end
380
381 test "returns true when recipient in `bcc`" do
382 recipient = insert(:user)
383 actor = insert(:user)
384 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
385
386 assert Utils.recipient_in_message(
387 recipient,
388 actor,
389 %{"bto" => "", "bcc" => [recipient.ap_id]}
390 )
391 end
392
393 test "returns true when message without addresses fields" do
394 recipient = insert(:user)
395 actor = insert(:user)
396 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
397
398 assert Utils.recipient_in_message(
399 recipient,
400 actor,
401 %{"btod" => "", "bccc" => [recipient.ap_id]}
402 )
403 end
404
405 test "returns false" do
406 recipient = insert(:user)
407 actor = insert(:user)
408 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
409 end
410 end
411
412 describe "lazy_put_activity_defaults/2" do
413 test "returns map with id and published data" do
414 note_activity = insert(:note_activity)
415 object = Object.normalize(note_activity, fetch: false)
416 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
417 assert res["context"] == object.data["id"]
418 assert res["context_id"] == object.id
419 assert res["id"]
420 assert res["published"]
421 end
422
423 test "returns map with fake id and published data" do
424 assert %{
425 "context" => "pleroma:fakecontext",
426 "context_id" => -1,
427 "id" => "pleroma:fakeid",
428 "published" => _
429 } = Utils.lazy_put_activity_defaults(%{}, true)
430 end
431
432 test "returns activity data with object" do
433 note_activity = insert(:note_activity)
434 object = Object.normalize(note_activity, fetch: false)
435
436 res =
437 Utils.lazy_put_activity_defaults(%{
438 "context" => object.data["id"],
439 "object" => %{}
440 })
441
442 assert res["context"] == object.data["id"]
443 assert res["context_id"] == object.id
444 assert res["id"]
445 assert res["published"]
446 assert res["object"]["id"]
447 assert res["object"]["published"]
448 assert res["object"]["context"] == object.data["id"]
449 assert res["object"]["context_id"] == object.id
450 end
451 end
452
453 describe "make_flag_data" do
454 test "returns empty map when params is invalid" do
455 assert Utils.make_flag_data(%{}, %{}) == %{}
456 end
457
458 test "returns map with Flag object" do
459 reporter = insert(:user)
460 target_account = insert(:user)
461 {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
462 context = Utils.generate_context_id()
463 content = "foobar"
464
465 target_ap_id = target_account.ap_id
466 activity_ap_id = activity.data["id"]
467
468 res =
469 Utils.make_flag_data(
470 %{
471 actor: reporter,
472 context: context,
473 account: target_account,
474 statuses: [%{"id" => activity.data["id"]}],
475 content: content
476 },
477 %{}
478 )
479
480 note_obj = %{
481 "type" => "Note",
482 "id" => activity_ap_id,
483 "content" => content,
484 "published" => activity.object.data["published"],
485 "actor" =>
486 AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
487 }
488
489 assert %{
490 "type" => "Flag",
491 "content" => ^content,
492 "context" => ^context,
493 "object" => [^target_ap_id, ^note_obj],
494 "state" => "open"
495 } = res
496 end
497 end
498
499 describe "add_announce_to_object/2" do
500 test "adds actor to announcement" do
501 user = insert(:user)
502 object = insert(:note)
503
504 activity =
505 insert(:note_activity,
506 data: %{
507 "actor" => user.ap_id,
508 "cc" => [Pleroma.Constants.as_public()]
509 }
510 )
511
512 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
513 assert updated_object.data["announcements"] == [user.ap_id]
514 assert updated_object.data["announcement_count"] == 1
515 end
516 end
517
518 describe "remove_announce_from_object/2" do
519 test "removes actor from announcements" do
520 user = insert(:user)
521 user2 = insert(:user)
522
523 object =
524 insert(:note,
525 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
526 )
527
528 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
529
530 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
531 assert updated_object.data["announcements"] == [user2.ap_id]
532 assert updated_object.data["announcement_count"] == 1
533 end
534 end
535
536 describe "get_cached_emoji_reactions/1" do
537 test "returns the data or an emtpy list" do
538 object = insert(:note)
539 assert Utils.get_cached_emoji_reactions(object) == []
540
541 object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
542 assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]]
543
544 object = insert(:note, data: %{"reactions" => %{}})
545 assert Utils.get_cached_emoji_reactions(object) == []
546 end
547 end
548 end