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