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