Restricted embedding of relationships where applicable (statuses / notifications...
[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 {:ok, _activity} = CommonAPI.favorite(user, activity.id)
228 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
229 assert fetched_vote.id == vote.id
230 end
231 end
232
233 describe "update_follow_state_for_all/2" do
234 test "updates the state of all Follow activities with the same actor and object" do
235 user = insert(:user, locked: true)
236 follower = insert(:user)
237
238 {:ok, follow_activity} = ActivityPub.follow(follower, user)
239 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
240
241 data =
242 follow_activity_two.data
243 |> Map.put("state", "accept")
244
245 cng = Ecto.Changeset.change(follow_activity_two, data: data)
246
247 {:ok, follow_activity_two} = Repo.update(cng)
248
249 {:ok, follow_activity_two} =
250 Utils.update_follow_state_for_all(follow_activity_two, "accept")
251
252 assert refresh_record(follow_activity).data["state"] == "accept"
253 assert refresh_record(follow_activity_two).data["state"] == "accept"
254 end
255 end
256
257 describe "update_follow_state/2" do
258 test "updates the state of the given follow activity" do
259 user = insert(:user, locked: true)
260 follower = insert(:user)
261
262 {:ok, follow_activity} = ActivityPub.follow(follower, user)
263 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
264
265 data =
266 follow_activity_two.data
267 |> Map.put("state", "accept")
268
269 cng = Ecto.Changeset.change(follow_activity_two, data: data)
270
271 {:ok, follow_activity_two} = Repo.update(cng)
272
273 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
274
275 assert refresh_record(follow_activity).data["state"] == "pending"
276 assert refresh_record(follow_activity_two).data["state"] == "reject"
277 end
278 end
279
280 describe "update_element_in_object/3" do
281 test "updates likes" do
282 user = insert(:user)
283 activity = insert(:note_activity)
284 object = Object.normalize(activity)
285
286 assert {:ok, updated_object} =
287 Utils.update_element_in_object(
288 "like",
289 [user.ap_id],
290 object
291 )
292
293 assert updated_object.data["likes"] == [user.ap_id]
294 assert updated_object.data["like_count"] == 1
295 end
296 end
297
298 describe "add_like_to_object/2" do
299 test "add actor to likes" do
300 user = insert(:user)
301 user2 = insert(:user)
302 object = insert(:note)
303
304 assert {:ok, updated_object} =
305 Utils.add_like_to_object(
306 %Activity{data: %{"actor" => user.ap_id}},
307 object
308 )
309
310 assert updated_object.data["likes"] == [user.ap_id]
311 assert updated_object.data["like_count"] == 1
312
313 assert {:ok, updated_object2} =
314 Utils.add_like_to_object(
315 %Activity{data: %{"actor" => user2.ap_id}},
316 updated_object
317 )
318
319 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
320 assert updated_object2.data["like_count"] == 2
321 end
322 end
323
324 describe "remove_like_from_object/2" do
325 test "removes ap_id from likes" do
326 user = insert(:user)
327 user2 = insert(:user)
328 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
329
330 assert {:ok, updated_object} =
331 Utils.remove_like_from_object(
332 %Activity{data: %{"actor" => user.ap_id}},
333 object
334 )
335
336 assert updated_object.data["likes"] == [user2.ap_id]
337 assert updated_object.data["like_count"] == 1
338 end
339 end
340
341 describe "get_existing_like/2" do
342 test "fetches existing like" do
343 note_activity = insert(:note_activity)
344 assert object = Object.normalize(note_activity)
345
346 user = insert(:user)
347 refute Utils.get_existing_like(user.ap_id, object)
348 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
349
350 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
351 end
352 end
353
354 describe "get_get_existing_announce/2" do
355 test "returns nil if announce not found" do
356 actor = insert(:user)
357 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
358 end
359
360 test "fetches existing announce" do
361 note_activity = insert(:note_activity)
362 assert object = Object.normalize(note_activity)
363 actor = insert(:user)
364
365 {:ok, announce, _object} = ActivityPub.announce(actor, object)
366 assert Utils.get_existing_announce(actor.ap_id, object) == announce
367 end
368 end
369
370 describe "fetch_latest_block/2" do
371 test "fetches last block activities" do
372 user1 = insert(:user)
373 user2 = insert(:user)
374
375 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
376 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
377 assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
378
379 assert Utils.fetch_latest_block(user1, user2) == activity
380 end
381 end
382
383 describe "recipient_in_message/3" do
384 test "returns true when recipient in `to`" do
385 recipient = insert(:user)
386 actor = insert(:user)
387 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
388
389 assert Utils.recipient_in_message(
390 recipient,
391 actor,
392 %{"to" => [recipient.ap_id], "cc" => ""}
393 )
394 end
395
396 test "returns true when recipient in `cc`" do
397 recipient = insert(:user)
398 actor = insert(:user)
399 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
400
401 assert Utils.recipient_in_message(
402 recipient,
403 actor,
404 %{"cc" => [recipient.ap_id], "to" => ""}
405 )
406 end
407
408 test "returns true when recipient in `bto`" do
409 recipient = insert(:user)
410 actor = insert(:user)
411 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
412
413 assert Utils.recipient_in_message(
414 recipient,
415 actor,
416 %{"bcc" => "", "bto" => [recipient.ap_id]}
417 )
418 end
419
420 test "returns true when recipient in `bcc`" do
421 recipient = insert(:user)
422 actor = insert(:user)
423 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
424
425 assert Utils.recipient_in_message(
426 recipient,
427 actor,
428 %{"bto" => "", "bcc" => [recipient.ap_id]}
429 )
430 end
431
432 test "returns true when message without addresses fields" do
433 recipient = insert(:user)
434 actor = insert(:user)
435 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
436
437 assert Utils.recipient_in_message(
438 recipient,
439 actor,
440 %{"btod" => "", "bccc" => [recipient.ap_id]}
441 )
442 end
443
444 test "returns false" do
445 recipient = insert(:user)
446 actor = insert(:user)
447 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
448 end
449 end
450
451 describe "lazy_put_activity_defaults/2" do
452 test "returns map with id and published data" do
453 note_activity = insert(:note_activity)
454 object = Object.normalize(note_activity)
455 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
456 assert res["context"] == object.data["id"]
457 assert res["context_id"] == object.id
458 assert res["id"]
459 assert res["published"]
460 end
461
462 test "returns map with fake id and published data" do
463 assert %{
464 "context" => "pleroma:fakecontext",
465 "context_id" => -1,
466 "id" => "pleroma:fakeid",
467 "published" => _
468 } = Utils.lazy_put_activity_defaults(%{}, true)
469 end
470
471 test "returns activity data with object" do
472 note_activity = insert(:note_activity)
473 object = Object.normalize(note_activity)
474
475 res =
476 Utils.lazy_put_activity_defaults(%{
477 "context" => object.data["id"],
478 "object" => %{}
479 })
480
481 assert res["context"] == object.data["id"]
482 assert res["context_id"] == object.id
483 assert res["id"]
484 assert res["published"]
485 assert res["object"]["id"]
486 assert res["object"]["published"]
487 assert res["object"]["context"] == object.data["id"]
488 assert res["object"]["context_id"] == object.id
489 end
490 end
491
492 describe "make_flag_data" do
493 test "returns empty map when params is invalid" do
494 assert Utils.make_flag_data(%{}, %{}) == %{}
495 end
496
497 test "returns map with Flag object" do
498 reporter = insert(:user)
499 target_account = insert(:user)
500 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
501 context = Utils.generate_context_id()
502 content = "foobar"
503
504 target_ap_id = target_account.ap_id
505 activity_ap_id = activity.data["id"]
506
507 res =
508 Utils.make_flag_data(
509 %{
510 actor: reporter,
511 context: context,
512 account: target_account,
513 statuses: [%{"id" => activity.data["id"]}],
514 content: content
515 },
516 %{}
517 )
518
519 note_obj = %{
520 "type" => "Note",
521 "id" => activity_ap_id,
522 "content" => content,
523 "published" => activity.object.data["published"],
524 "actor" => AccountView.render("show.json", %{user: target_account})
525 }
526
527 assert %{
528 "type" => "Flag",
529 "content" => ^content,
530 "context" => ^context,
531 "object" => [^target_ap_id, ^note_obj],
532 "state" => "open"
533 } = res
534 end
535 end
536
537 describe "add_announce_to_object/2" do
538 test "adds actor to announcement" do
539 user = insert(:user)
540 object = insert(:note)
541
542 activity =
543 insert(:note_activity,
544 data: %{
545 "actor" => user.ap_id,
546 "cc" => [Pleroma.Constants.as_public()]
547 }
548 )
549
550 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
551 assert updated_object.data["announcements"] == [user.ap_id]
552 assert updated_object.data["announcement_count"] == 1
553 end
554 end
555
556 describe "remove_announce_from_object/2" do
557 test "removes actor from announcements" do
558 user = insert(:user)
559 user2 = insert(:user)
560
561 object =
562 insert(:note,
563 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
564 )
565
566 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
567
568 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
569 assert updated_object.data["announcements"] == [user2.ap_id]
570 assert updated_object.data["announcement_count"] == 1
571 end
572 end
573
574 describe "get_cached_emoji_reactions/1" do
575 test "returns the data or an emtpy list" do
576 object = insert(:note)
577 assert Utils.get_cached_emoji_reactions(object) == []
578
579 object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
580 assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]]
581
582 object = insert(:note, data: %{"reactions" => %{}})
583 assert Utils.get_cached_emoji_reactions(object) == []
584 end
585 end
586 end