Lint
[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_like_data" do
106 setup do
107 user = insert(:user)
108 other_user = insert(:user)
109 third_user = insert(:user)
110 [user: user, other_user: other_user, third_user: third_user]
111 end
112
113 test "addresses actor's follower address if the activity is public", %{
114 user: user,
115 other_user: other_user,
116 third_user: third_user
117 } do
118 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
119 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
120
121 {:ok, activity} =
122 CommonAPI.post(user, %{
123 status:
124 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
125 })
126
127 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
128 assert Enum.sort(to) == expected_to
129 assert Enum.sort(cc) == expected_cc
130 end
131
132 test "does not adress actor's follower address if the activity is not public", %{
133 user: user,
134 other_user: other_user,
135 third_user: third_user
136 } do
137 expected_to = Enum.sort([user.ap_id])
138 expected_cc = [third_user.ap_id]
139
140 {:ok, activity} =
141 CommonAPI.post(user, %{
142 status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
143 visibility: "private"
144 })
145
146 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
147 assert Enum.sort(to) == expected_to
148 assert Enum.sort(cc) == expected_cc
149 end
150 end
151
152 test "make_json_ld_header/0" do
153 assert Utils.make_json_ld_header() == %{
154 "@context" => [
155 "https://www.w3.org/ns/activitystreams",
156 "http://localhost:4001/schemas/litepub-0.1.jsonld",
157 %{
158 "@language" => "und"
159 }
160 ]
161 }
162 end
163
164 describe "get_existing_votes" do
165 test "fetches existing votes" do
166 user = insert(:user)
167 other_user = insert(:user)
168
169 {:ok, activity} =
170 CommonAPI.post(user, %{
171 status: "How do I pronounce LaTeX?",
172 poll: %{
173 options: ["laytekh", "lahtekh", "latex"],
174 expires_in: 20,
175 multiple: true
176 }
177 })
178
179 object = Object.normalize(activity)
180 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
181 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
182 end
183
184 test "fetches only Create activities" do
185 user = insert(:user)
186 other_user = insert(:user)
187
188 {:ok, activity} =
189 CommonAPI.post(user, %{
190 status: "Are we living in a society?",
191 poll: %{
192 options: ["yes", "no"],
193 expires_in: 20
194 }
195 })
196
197 object = Object.normalize(activity)
198 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
199 {:ok, _activity} = CommonAPI.favorite(user, activity.id)
200 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
201 assert fetched_vote.id == vote.id
202 end
203 end
204
205 describe "update_follow_state_for_all/2" do
206 test "updates the state of all Follow activities with the same actor and object" do
207 user = insert(:user, locked: true)
208 follower = insert(:user)
209
210 {:ok, follow_activity} = ActivityPub.follow(follower, user)
211 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
212
213 data =
214 follow_activity_two.data
215 |> Map.put("state", "accept")
216
217 cng = Ecto.Changeset.change(follow_activity_two, data: data)
218
219 {:ok, follow_activity_two} = Repo.update(cng)
220
221 {:ok, follow_activity_two} =
222 Utils.update_follow_state_for_all(follow_activity_two, "accept")
223
224 assert refresh_record(follow_activity).data["state"] == "accept"
225 assert refresh_record(follow_activity_two).data["state"] == "accept"
226 end
227 end
228
229 describe "update_follow_state/2" do
230 test "updates the state of the given follow activity" do
231 user = insert(:user, locked: true)
232 follower = insert(:user)
233
234 {:ok, follow_activity} = ActivityPub.follow(follower, user)
235 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
236
237 data =
238 follow_activity_two.data
239 |> Map.put("state", "accept")
240
241 cng = Ecto.Changeset.change(follow_activity_two, data: data)
242
243 {:ok, follow_activity_two} = Repo.update(cng)
244
245 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
246
247 assert refresh_record(follow_activity).data["state"] == "pending"
248 assert refresh_record(follow_activity_two).data["state"] == "reject"
249 end
250 end
251
252 describe "update_element_in_object/3" do
253 test "updates likes" do
254 user = insert(:user)
255 activity = insert(:note_activity)
256 object = Object.normalize(activity)
257
258 assert {:ok, updated_object} =
259 Utils.update_element_in_object(
260 "like",
261 [user.ap_id],
262 object
263 )
264
265 assert updated_object.data["likes"] == [user.ap_id]
266 assert updated_object.data["like_count"] == 1
267 end
268 end
269
270 describe "add_like_to_object/2" do
271 test "add actor to likes" do
272 user = insert(:user)
273 user2 = insert(:user)
274 object = insert(:note)
275
276 assert {:ok, updated_object} =
277 Utils.add_like_to_object(
278 %Activity{data: %{"actor" => user.ap_id}},
279 object
280 )
281
282 assert updated_object.data["likes"] == [user.ap_id]
283 assert updated_object.data["like_count"] == 1
284
285 assert {:ok, updated_object2} =
286 Utils.add_like_to_object(
287 %Activity{data: %{"actor" => user2.ap_id}},
288 updated_object
289 )
290
291 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
292 assert updated_object2.data["like_count"] == 2
293 end
294 end
295
296 describe "remove_like_from_object/2" do
297 test "removes ap_id from likes" do
298 user = insert(:user)
299 user2 = insert(:user)
300 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
301
302 assert {:ok, updated_object} =
303 Utils.remove_like_from_object(
304 %Activity{data: %{"actor" => user.ap_id}},
305 object
306 )
307
308 assert updated_object.data["likes"] == [user2.ap_id]
309 assert updated_object.data["like_count"] == 1
310 end
311 end
312
313 describe "get_existing_like/2" do
314 test "fetches existing like" do
315 note_activity = insert(:note_activity)
316 assert object = Object.normalize(note_activity)
317
318 user = insert(:user)
319 refute Utils.get_existing_like(user.ap_id, object)
320 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
321
322 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
323 end
324 end
325
326 describe "get_get_existing_announce/2" do
327 test "returns nil if announce not found" do
328 actor = insert(:user)
329 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
330 end
331
332 test "fetches existing announce" do
333 note_activity = insert(:note_activity)
334 assert object = Object.normalize(note_activity)
335 actor = insert(:user)
336
337 {:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
338 assert Utils.get_existing_announce(actor.ap_id, object) == announce
339 end
340 end
341
342 describe "fetch_latest_block/2" do
343 test "fetches last block activities" do
344 user1 = insert(:user)
345 user2 = insert(:user)
346
347 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
348 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
349 assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
350
351 assert Utils.fetch_latest_block(user1, user2) == activity
352 end
353 end
354
355 describe "recipient_in_message/3" do
356 test "returns true when recipient in `to`" do
357 recipient = insert(:user)
358 actor = insert(:user)
359 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
360
361 assert Utils.recipient_in_message(
362 recipient,
363 actor,
364 %{"to" => [recipient.ap_id], "cc" => ""}
365 )
366 end
367
368 test "returns true when recipient in `cc`" do
369 recipient = insert(:user)
370 actor = insert(:user)
371 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
372
373 assert Utils.recipient_in_message(
374 recipient,
375 actor,
376 %{"cc" => [recipient.ap_id], "to" => ""}
377 )
378 end
379
380 test "returns true when recipient in `bto`" do
381 recipient = insert(:user)
382 actor = insert(:user)
383 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
384
385 assert Utils.recipient_in_message(
386 recipient,
387 actor,
388 %{"bcc" => "", "bto" => [recipient.ap_id]}
389 )
390 end
391
392 test "returns true when recipient in `bcc`" do
393 recipient = insert(:user)
394 actor = insert(:user)
395 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
396
397 assert Utils.recipient_in_message(
398 recipient,
399 actor,
400 %{"bto" => "", "bcc" => [recipient.ap_id]}
401 )
402 end
403
404 test "returns true when message without addresses fields" do
405 recipient = insert(:user)
406 actor = insert(:user)
407 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
408
409 assert Utils.recipient_in_message(
410 recipient,
411 actor,
412 %{"btod" => "", "bccc" => [recipient.ap_id]}
413 )
414 end
415
416 test "returns false" do
417 recipient = insert(:user)
418 actor = insert(:user)
419 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
420 end
421 end
422
423 describe "lazy_put_activity_defaults/2" do
424 test "returns map with id and published data" do
425 note_activity = insert(:note_activity)
426 object = Object.normalize(note_activity)
427 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
428 assert res["context"] == object.data["id"]
429 assert res["context_id"] == object.id
430 assert res["id"]
431 assert res["published"]
432 end
433
434 test "returns map with fake id and published data" do
435 assert %{
436 "context" => "pleroma:fakecontext",
437 "context_id" => -1,
438 "id" => "pleroma:fakeid",
439 "published" => _
440 } = Utils.lazy_put_activity_defaults(%{}, true)
441 end
442
443 test "returns activity data with object" do
444 note_activity = insert(:note_activity)
445 object = Object.normalize(note_activity)
446
447 res =
448 Utils.lazy_put_activity_defaults(%{
449 "context" => object.data["id"],
450 "object" => %{}
451 })
452
453 assert res["context"] == object.data["id"]
454 assert res["context_id"] == object.id
455 assert res["id"]
456 assert res["published"]
457 assert res["object"]["id"]
458 assert res["object"]["published"]
459 assert res["object"]["context"] == object.data["id"]
460 assert res["object"]["context_id"] == object.id
461 end
462 end
463
464 describe "make_flag_data" do
465 test "returns empty map when params is invalid" do
466 assert Utils.make_flag_data(%{}, %{}) == %{}
467 end
468
469 test "returns map with Flag object" do
470 reporter = insert(:user)
471 target_account = insert(:user)
472 {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
473 context = Utils.generate_context_id()
474 content = "foobar"
475
476 target_ap_id = target_account.ap_id
477 activity_ap_id = activity.data["id"]
478
479 res =
480 Utils.make_flag_data(
481 %{
482 actor: reporter,
483 context: context,
484 account: target_account,
485 statuses: [%{"id" => activity.data["id"]}],
486 content: content
487 },
488 %{}
489 )
490
491 note_obj = %{
492 "type" => "Note",
493 "id" => activity_ap_id,
494 "content" => content,
495 "published" => activity.object.data["published"],
496 "actor" => AccountView.render("show.json", %{user: target_account})
497 }
498
499 assert %{
500 "type" => "Flag",
501 "content" => ^content,
502 "context" => ^context,
503 "object" => [^target_ap_id, ^note_obj],
504 "state" => "open"
505 } = res
506 end
507 end
508
509 describe "add_announce_to_object/2" do
510 test "adds actor to announcement" do
511 user = insert(:user)
512 object = insert(:note)
513
514 activity =
515 insert(:note_activity,
516 data: %{
517 "actor" => user.ap_id,
518 "cc" => [Pleroma.Constants.as_public()]
519 }
520 )
521
522 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
523 assert updated_object.data["announcements"] == [user.ap_id]
524 assert updated_object.data["announcement_count"] == 1
525 end
526 end
527
528 describe "remove_announce_from_object/2" do
529 test "removes actor from announcements" do
530 user = insert(:user)
531 user2 = insert(:user)
532
533 object =
534 insert(:note,
535 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
536 )
537
538 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
539
540 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
541 assert updated_object.data["announcements"] == [user2.ap_id]
542 assert updated_object.data["announcement_count"] == 1
543 end
544 end
545
546 describe "get_cached_emoji_reactions/1" do
547 test "returns the data or an emtpy list" do
548 object = insert(:note)
549 assert Utils.get_cached_emoji_reactions(object) == []
550
551 object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
552 assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]]
553
554 object = insert(:note, data: %{"reactions" => %{}})
555 assert Utils.get_cached_emoji_reactions(object) == []
556 end
557 end
558 end