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