make conversation-id deterministic (#154)
[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["id"]
433 assert res["published"]
434 end
435
436 test "returns map with fake id and published data" do
437 assert %{
438 "context" => "pleroma:fakecontext",
439 "id" => "pleroma:fakeid",
440 "published" => _
441 } = Utils.lazy_put_activity_defaults(%{}, true)
442 end
443
444 test "returns activity data with object" do
445 note_activity = insert(:note_activity)
446 object = Object.normalize(note_activity, fetch: false)
447
448 res =
449 Utils.lazy_put_activity_defaults(%{
450 "context" => object.data["id"],
451 "object" => %{}
452 })
453
454 assert res["context"] == object.data["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 end
461 end
462
463 describe "make_flag_data" do
464 test "returns empty map when params is invalid" do
465 assert Utils.make_flag_data(%{}, %{}) == %{}
466 end
467
468 test "returns map with Flag object" do
469 reporter = insert(:user)
470 target_account = insert(:user)
471 {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
472 context = Utils.generate_context_id()
473 content = "foobar"
474
475 target_ap_id = target_account.ap_id
476 activity_ap_id = activity.data["id"]
477
478 res =
479 Utils.make_flag_data(
480 %{
481 actor: reporter,
482 context: context,
483 account: target_account,
484 statuses: [%{"id" => activity.data["id"]}],
485 content: content
486 },
487 %{}
488 )
489
490 note_obj = %{
491 "type" => "Note",
492 "id" => activity_ap_id,
493 "content" => content,
494 "published" => activity.object.data["published"],
495 "actor" =>
496 AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
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