activity_pub_test.exs 63.9 KB
Newer Older
kaniini's avatar
kaniini committed
1
# Pleroma: A lightweight social networking server
2
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
kaniini's avatar
kaniini committed
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
6
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
  use Pleroma.DataCase
minibikini's avatar
minibikini committed
7
8
  use Oban.Testing, repo: Pleroma.Repo

Haelwenn's avatar
Haelwenn committed
9
  alias Pleroma.Activity
10
  alias Pleroma.Builders.ActivityBuilder
11
  alias Pleroma.Config
12
  alias Pleroma.Notification
Haelwenn's avatar
Haelwenn committed
13
14
  alias Pleroma.Object
  alias Pleroma.User
15
16
  alias Pleroma.Web.ActivityPub.ActivityPub
  alias Pleroma.Web.ActivityPub.Utils
17
  alias Pleroma.Web.AdminAPI.AccountView
18
  alias Pleroma.Web.CommonAPI
lain's avatar
lain committed
19

20
21
  import ExUnit.CaptureLog
  import Mock
lain's avatar
lain committed
22
  import Pleroma.Factory
Maksim's avatar
Maksim committed
23
24
25
26
27
28
  import Tesla.Mock

  setup do
    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
    :ok
  end
lain's avatar
lain committed
29

30
  setup do: clear_config([:instance, :federating])
31

32
33
34
  describe "streaming out participations" do
    test "it streams them out" do
      user = insert(:user)
35
      {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
36
37
38
39
40
41
42
43
44
45
46

      {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)

      participations =
        conversation.participations
        |> Repo.preload(:user)

      with_mock Pleroma.Web.Streamer,
        stream: fn _, _ -> nil end do
        ActivityPub.stream_out_participations(conversation.participations)

47
        assert called(Pleroma.Web.Streamer.stream("participation", participations))
48
49
      end
    end
50
51
52
53
54
55
56
57
58

    test "streams them out on activity creation" do
      user_one = insert(:user)
      user_two = insert(:user)

      with_mock Pleroma.Web.Streamer,
        stream: fn _, _ -> nil end do
        {:ok, activity} =
          CommonAPI.post(user_one, %{
59
60
            status: "@#{user_two.nickname}",
            visibility: "direct"
61
62
63
64
65
66
67
68
69
70
          })

        conversation =
          activity.data["context"]
          |> Pleroma.Conversation.get_for_ap_id()
          |> Repo.preload(participations: :user)

        assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
      end
    end
71
72
  end

lain's avatar
lain committed
73
74
75
76
  describe "fetching restricted by visibility" do
    test "it restricts by the appropriate visibility" do
      user = insert(:user)

77
      {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
lain's avatar
lain committed
78

79
      {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
lain's avatar
lain committed
80

81
      {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
lain's avatar
lain committed
82

83
      {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
lain's avatar
lain committed
84

85
      activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
lain's avatar
lain committed
86
87
88
89

      assert activities == [direct_activity]

      activities =
90
        ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
lain's avatar
lain committed
91
92
93
94

      assert activities == [unlisted_activity]

      activities =
95
        ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
lain's avatar
lain committed
96
97
98

      assert activities == [private_activity]

99
      activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
lain's avatar
lain committed
100
101

      assert activities == [public_activity]
102
103
104

      activities =
        ActivityPub.fetch_activities([], %{
105
106
          visibility: ~w[private public],
          actor_id: user.ap_id
107
108
109
        })

      assert activities == [public_activity, private_activity]
lain's avatar
lain committed
110
111
112
    end
  end

113
114
115
116
  describe "fetching excluded by visibility" do
    test "it excludes by the appropriate visibility" do
      user = insert(:user)

117
      {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
118

119
      {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
120

121
      {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
122

123
      {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
124
125
126

      activities =
        ActivityPub.fetch_activities([], %{
127
128
          exclude_visibilities: "direct",
          actor_id: user.ap_id
129
130
131
132
133
134
135
136
137
        })

      assert public_activity in activities
      assert unlisted_activity in activities
      assert private_activity in activities
      refute direct_activity in activities

      activities =
        ActivityPub.fetch_activities([], %{
138
139
          exclude_visibilities: "unlisted",
          actor_id: user.ap_id
140
141
142
143
144
145
146
147
148
        })

      assert public_activity in activities
      refute unlisted_activity in activities
      assert private_activity in activities
      assert direct_activity in activities

      activities =
        ActivityPub.fetch_activities([], %{
149
150
          exclude_visibilities: "private",
          actor_id: user.ap_id
151
152
153
154
155
156
157
158
159
        })

      assert public_activity in activities
      assert unlisted_activity in activities
      refute private_activity in activities
      assert direct_activity in activities

      activities =
        ActivityPub.fetch_activities([], %{
160
161
          exclude_visibilities: "public",
          actor_id: user.ap_id
162
163
164
165
166
167
168
169
170
        })

      refute public_activity in activities
      assert unlisted_activity in activities
      assert private_activity in activities
      assert direct_activity in activities
    end
  end

lain's avatar
lain committed
171
172
173
174
175
176
  describe "building a user from his ap id" do
    test "it returns a user" do
      user_id = "http://mastodon.example.org/users/admin"
      {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
      assert user.ap_id == user_id
      assert user.nickname == "admin@mastodon.example.org"
177
      assert user.ap_enabled
lain's avatar
lain committed
178
179
      assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
    end
180

181
182
183
    test "it returns a user that is invisible" do
      user_id = "http://mastodon.example.org/users/relay"
      {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
184
      assert User.invisible?(user)
185
186
    end

187
188
189
    test "it fetches the appropriate tag-restricted posts" do
      user = insert(:user)

190
191
192
      {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
      {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
      {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
193

194
      fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
195

196
      fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
197
198
199

      fetch_three =
        ActivityPub.fetch_activities([], %{
200
201
202
          type: "Create",
          tag: ["test", "essais"],
          tag_reject: ["reject"]
203
204
        })

Haelwenn's avatar
Haelwenn committed
205
206
      fetch_four =
        ActivityPub.fetch_activities([], %{
207
208
209
          type: "Create",
          tag: ["test"],
          tag_all: ["test", "reject"]
Haelwenn's avatar
Haelwenn committed
210
211
        })

212
213
214
      assert fetch_one == [status_one, status_three]
      assert fetch_two == [status_one, status_two, status_three]
      assert fetch_three == [status_one, status_two]
Haelwenn's avatar
Haelwenn committed
215
      assert fetch_four == [status_three]
216
    end
lain's avatar
lain committed
217
218
  end

lain's avatar
lain committed
219
  describe "insertion" do
220
    test "drops activities beyond a certain limit" do
221
      limit = Config.get([:instance, :remote_limit])
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

      random_text =
        :crypto.strong_rand_bytes(limit + 1)
        |> Base.encode64()
        |> binary_part(0, limit + 1)

      data = %{
        "ok" => true,
        "object" => %{
          "content" => random_text
        }
      }

      assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
    end

238
    test "doesn't drop activities with content being null" do
lain's avatar
lain committed
239
240
      user = insert(:user)

241
      data = %{
lain's avatar
lain committed
242
243
        "actor" => user.ap_id,
        "to" => [],
244
        "object" => %{
lain's avatar
lain committed
245
246
247
          "actor" => user.ap_id,
          "to" => [],
          "type" => "Note",
248
249
250
251
252
253
254
          "content" => nil
        }
      }

      assert {:ok, _} = ActivityPub.insert(data)
    end

lain's avatar
lain committed
255
256
    test "returns the activity if one with the same id is already in" do
      activity = insert(:note_activity)
lain's avatar
lain committed
257
      {:ok, new_activity} = ActivityPub.insert(activity.data)
lain's avatar
lain committed
258

259
      assert activity.id == new_activity.id
lain's avatar
lain committed
260
261
    end

lain's avatar
lain committed
262
    test "inserts a given map into the activity database, giving it an id if it has none." do
lain's avatar
lain committed
263
264
      user = insert(:user)

lain's avatar
lain committed
265
      data = %{
lain's avatar
lain committed
266
267
268
269
270
271
272
273
        "actor" => user.ap_id,
        "to" => [],
        "object" => %{
          "actor" => user.ap_id,
          "to" => [],
          "type" => "Note",
          "content" => "hey"
        }
lain's avatar
lain committed
274
275
276
      }

      {:ok, %Activity{} = activity} = ActivityPub.insert(data)
lain's avatar
lain committed
277
278
279
280
      assert activity.data["ok"] == data["ok"]
      assert is_binary(activity.data["id"])

      given_id = "bla"
lain's avatar
lain committed
281

lain's avatar
lain committed
282
      data = %{
283
        "id" => given_id,
lain's avatar
lain committed
284
285
286
287
288
289
290
291
292
        "actor" => user.ap_id,
        "to" => [],
        "context" => "blabla",
        "object" => %{
          "actor" => user.ap_id,
          "to" => [],
          "type" => "Note",
          "content" => "hey"
        }
lain's avatar
lain committed
293
294
295
296
297
      }

      {:ok, %Activity{} = activity} = ActivityPub.insert(data)
      assert activity.data["ok"] == data["ok"]
      assert activity.data["id"] == given_id
298
299
300
301
302
      assert activity.data["context"] == "blabla"
      assert activity.data["context_id"]
    end

    test "adds a context when none is there" do
lain's avatar
lain committed
303
304
      user = insert(:user)

305
      data = %{
lain's avatar
lain committed
306
307
        "actor" => user.ap_id,
        "to" => [],
308
        "object" => %{
lain's avatar
lain committed
309
310
311
312
          "actor" => user.ap_id,
          "to" => [],
          "type" => "Note",
          "content" => "hey"
lain's avatar
lain committed
313
        }
314
315
316
      }

      {:ok, %Activity{} = activity} = ActivityPub.insert(data)
Roman Chvanikov's avatar
Roman Chvanikov committed
317
      object = Pleroma.Object.normalize(activity)
318
319

      assert is_binary(activity.data["context"])
Roman Chvanikov's avatar
Roman Chvanikov committed
320
      assert is_binary(object.data["context"])
321
      assert activity.data["context_id"]
Roman Chvanikov's avatar
Roman Chvanikov committed
322
      assert object.data["context_id"]
lain's avatar
lain committed
323
    end
lain's avatar
lain committed
324

lain's avatar
lain committed
325
    test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
lain's avatar
lain committed
326
327
      user = insert(:user)

lain's avatar
lain committed
328
      data = %{
lain's avatar
lain committed
329
330
        "actor" => user.ap_id,
        "to" => [],
lain's avatar
lain committed
331
        "object" => %{
lain's avatar
lain committed
332
333
          "actor" => user.ap_id,
          "to" => [],
lain's avatar
lain committed
334
          "type" => "Note",
lain's avatar
lain committed
335
          "content" => "hey"
lain's avatar
lain committed
336
337
338
339
        }
      }

      {:ok, %Activity{} = activity} = ActivityPub.insert(data)
340
      assert object = Object.normalize(activity)
kaniini's avatar
kaniini committed
341
      assert is_binary(object.data["id"])
lain's avatar
lain committed
342
    end
lain's avatar
lain committed
343
344
  end

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  describe "listen activities" do
    test "does not increase user note count" do
      user = insert(:user)

      {:ok, activity} =
        ActivityPub.listen(%{
          to: ["https://www.w3.org/ns/activitystreams#Public"],
          actor: user,
          context: "",
          object: %{
            "actor" => user.ap_id,
            "to" => ["https://www.w3.org/ns/activitystreams#Public"],
            "artist" => "lain",
            "title" => "lain radio episode 1",
            "length" => 180_000,
            "type" => "Audio"
          }
        })

      assert activity.actor == user.ap_id

      user = User.get_cached_by_id(user.id)
367
      assert user.note_count == 0
368
369
370
371
372
373
374
    end

    test "can be fetched into a timeline" do
      _listen_activity_1 = insert(:listen)
      _listen_activity_2 = insert(:listen)
      _listen_activity_3 = insert(:listen)

375
      timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
376
377
378
379
380

      assert length(timeline) == 3
    end
  end

lain's avatar
lain committed
381
  describe "create activities" do
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
    test "it reverts create" do
      user = insert(:user)

      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
        assert {:error, :reverted} =
                 ActivityPub.create(%{
                   to: ["user1", "user2"],
                   actor: user,
                   context: "",
                   object: %{
                     "to" => ["user1", "user2"],
                     "type" => "Note",
                     "content" => "testing"
                   }
                 })
      end

      assert Repo.aggregate(Activity, :count, :id) == 0
      assert Repo.aggregate(Object, :count, :id) == 0
    end

lain's avatar
lain committed
403
    test "removes doubled 'to' recipients" do
eal's avatar
eal committed
404
405
      user = insert(:user)

lain's avatar
lain committed
406
407
408
      {:ok, activity} =
        ActivityPub.create(%{
          to: ["user1", "user1", "user2"],
eal's avatar
eal committed
409
          actor: user,
lain's avatar
lain committed
410
          context: "",
411
412
413
414
415
          object: %{
            "to" => ["user1", "user1", "user2"],
            "type" => "Note",
            "content" => "testing"
          }
lain's avatar
lain committed
416
417
        })

lain's avatar
lain committed
418
      assert activity.data["to"] == ["user1", "user2"]
eal's avatar
eal committed
419
      assert activity.actor == user.ap_id
Maxim Filippov's avatar
Maxim Filippov committed
420
      assert activity.recipients == ["user1", "user2", user.ap_id]
lain's avatar
lain committed
421
    end
422
423
424
425
426

    test "increases user note count only for public activities" do
      user = insert(:user)

      {:ok, _} =
minibikini's avatar
minibikini committed
427
        CommonAPI.post(User.get_cached_by_id(user.id), %{
428
429
          status: "1",
          visibility: "public"
minibikini's avatar
minibikini committed
430
        })
431
432

      {:ok, _} =
minibikini's avatar
minibikini committed
433
        CommonAPI.post(User.get_cached_by_id(user.id), %{
434
435
          status: "2",
          visibility: "unlisted"
minibikini's avatar
minibikini committed
436
        })
437
438

      {:ok, _} =
minibikini's avatar
minibikini committed
439
        CommonAPI.post(User.get_cached_by_id(user.id), %{
440
441
          status: "2",
          visibility: "private"
minibikini's avatar
minibikini committed
442
        })
443
444

      {:ok, _} =
minibikini's avatar
minibikini committed
445
        CommonAPI.post(User.get_cached_by_id(user.id), %{
446
447
          status: "3",
          visibility: "direct"
minibikini's avatar
minibikini committed
448
        })
449

minibikini's avatar
minibikini committed
450
      user = User.get_cached_by_id(user.id)
451
      assert user.note_count == 2
452
    end
453
454
455
456
457

    test "increases replies count" do
      user = insert(:user)
      user2 = insert(:user)

458
      {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
459
      ap_id = activity.data["id"]
460
      reply_data = %{status: "1", in_reply_to_status_id: activity.id}
461
462

      # public
463
      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
464
465
466
467
      assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
      assert object.data["repliesCount"] == 1

      # unlisted
468
      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
469
470
471
472
      assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
      assert object.data["repliesCount"] == 2

      # private
473
      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
474
475
476
477
      assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
      assert object.data["repliesCount"] == 2

      # direct
478
      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
479
480
481
      assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
      assert object.data["repliesCount"] == 2
    end
lain's avatar
lain committed
482
483
  end

lain's avatar
lain committed
484
485
486
487
  describe "fetch activities for recipients" do
    test "retrieve the activities for certain recipients" do
      {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
      {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
lain's avatar
lain committed
488
      {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
lain's avatar
lain committed
489
490
491
492
493
494
495

      activities = ActivityPub.fetch_activities(["someone", "someone_else"])
      assert length(activities) == 2
      assert activities == [activity_one, activity_two]
    end
  end

lain's avatar
lain committed
496
497
  describe "fetch activities in context" do
    test "retrieves activities that have a given context" do
498
499
500
501
      {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
      {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
      {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
      {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
502
503
      activity_five = insert(:note_activity)
      user = insert(:user)
lain's avatar
lain committed
504

505
      {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
lain's avatar
lain committed
506

507
      activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
508
      assert activities == [activity_two, activity]
lain's avatar
lain committed
509
510
511
    end
  end

512
513
514
  test "doesn't return blocked activities" do
    activity_one = insert(:note_activity)
    activity_two = insert(:note_activity)
515
    activity_three = insert(:note_activity)
516
    user = insert(:user)
517
    booster = insert(:user)
518
    {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
519

520
    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
521
522

    assert Enum.member?(activities, activity_two)
523
    assert Enum.member?(activities, activity_three)
524
525
    refute Enum.member?(activities, activity_one)

526
    {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
527

528
    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
529
530

    assert Enum.member?(activities, activity_two)
531
532
533
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, activity_one)

534
    {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
lain's avatar
lain committed
535
    {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
536
    %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
537
    activity_three = Activity.get_by_id(activity_three.id)
538

539
    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
540
541
542
543

    assert Enum.member?(activities, activity_two)
    refute Enum.member?(activities, activity_three)
    refute Enum.member?(activities, boost_activity)
544
    assert Enum.member?(activities, activity_one)
lain's avatar
lain committed
545

546
    activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
lain's avatar
lain committed
547
548

    assert Enum.member?(activities, activity_two)
549
550
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, boost_activity)
lain's avatar
lain committed
551
    assert Enum.member?(activities, activity_one)
552
553
  end

554
555
556
557
558
  test "doesn't return transitive interactions concerning blocked users" do
    blocker = insert(:user)
    blockee = insert(:user)
    friend = insert(:user)

559
    {:ok, _user_relationship} = User.block(blocker, blockee)
560

561
    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
562

563
    {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
564

565
    {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
566

567
    {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
568

569
    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
570
571
572
573
574
575
576

    assert Enum.member?(activities, activity_one)
    refute Enum.member?(activities, activity_two)
    refute Enum.member?(activities, activity_three)
    refute Enum.member?(activities, activity_four)
  end

Thibaut Girka's avatar
Thibaut Girka committed
577
  test "doesn't return announce activities with blocked users in 'to'" do
578
579
580
581
    blocker = insert(:user)
    blockee = insert(:user)
    friend = insert(:user)

582
    {:ok, _user_relationship} = User.block(blocker, blockee)
583

584
    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
585

586
    {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
587

lain's avatar
lain committed
588
    {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
589
590

    activities =
591
      ActivityPub.fetch_activities([], %{blocking_user: blocker})
592
593
594
595
596
597
598
      |> Enum.map(fn act -> act.id end)

    assert Enum.member?(activities, activity_one.id)
    refute Enum.member?(activities, activity_two.id)
    refute Enum.member?(activities, activity_three.id)
  end

Thibaut Girka's avatar
Thibaut Girka committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  test "doesn't return announce activities with blocked users in 'cc'" do
    blocker = insert(:user)
    blockee = insert(:user)
    friend = insert(:user)

    {:ok, _user_relationship} = User.block(blocker, blockee)

    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})

    {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})

    assert object = Pleroma.Object.normalize(activity_two)

    data = %{
      "actor" => friend.ap_id,
      "object" => object.data["id"],
      "context" => object.data["context"],
      "type" => "Announce",
      "to" => ["https://www.w3.org/ns/activitystreams#Public"],
      "cc" => [blockee.ap_id]
    }

    assert {:ok, activity_three} = ActivityPub.insert(data)

    activities =
624
      ActivityPub.fetch_activities([], %{blocking_user: blocker})
Thibaut Girka's avatar
Thibaut Girka committed
625
626
627
628
629
630
631
      |> Enum.map(fn act -> act.id end)

    assert Enum.member?(activities, activity_one.id)
    refute Enum.member?(activities, activity_two.id)
    refute Enum.member?(activities, activity_three.id)
  end

Aaron Tinio's avatar
Aaron Tinio committed
632
633
634
635
636
637
638
639
  test "doesn't return activities from blocked domains" do
    domain = "dogwhistle.zone"
    domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
    note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
    activity = insert(:note_activity, %{note: note})
    user = insert(:user)
    {:ok, user} = User.block_domain(user, domain)

640
    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
Aaron Tinio's avatar
Aaron Tinio committed
641
642

    refute activity in activities
643
644
645

    followed_user = insert(:user)
    ActivityPub.follow(user, followed_user)
lain's avatar
lain committed
646
    {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
647

648
    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
649
650

    refute repeat_activity in activities
Aaron Tinio's avatar
Aaron Tinio committed
651
652
  end

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  test "does return activities from followed users on blocked domains" do
    domain = "meanies.social"
    domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
    blocker = insert(:user)

    {:ok, blocker} = User.follow(blocker, domain_user)
    {:ok, blocker} = User.block_domain(blocker, domain)

    assert User.following?(blocker, domain_user)
    assert User.blocks_domain?(blocker, domain_user)
    refute User.blocks?(blocker, domain_user)

    note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
    activity = insert(:note_activity, %{note: note})

668
    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
669
670
671

    assert activity in activities

Sadposter's avatar
Sadposter committed
672
673
    # And check that if the guy we DO follow boosts someone else from their domain,
    # that should be hidden
674
675
676
    another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
    bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
    bad_activity = insert(:note_activity, %{note: bad_note})
lain's avatar
lain committed
677
    {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
678

679
    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
680
681
682
683

    refute repeat_activity in activities
  end

lain's avatar
lain committed
684
685
686
687
688
689
  test "doesn't return muted activities" do
    activity_one = insert(:note_activity)
    activity_two = insert(:note_activity)
    activity_three = insert(:note_activity)
    user = insert(:user)
    booster = insert(:user)
690
691

    activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
692
    {:ok, _user_relationships} = User.mute(user, activity_one_actor)
lain's avatar
lain committed
693

694
    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
lain's avatar
lain committed
695
696
697
698
699

    assert Enum.member?(activities, activity_two)
    assert Enum.member?(activities, activity_three)
    refute Enum.member?(activities, activity_one)

lain's avatar
lain committed
700
    # Calling with 'with_muted' will deliver muted activities, too.
701
702
    activities =
      ActivityPub.fetch_activities([], %{
703
704
705
        muting_user: user,
        with_muted: true,
        skip_preload: true
706
      })
lain's avatar
lain committed
707
708
709
710
711

    assert Enum.member?(activities, activity_two)
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, activity_one)

712
    {:ok, _user_mute} = User.unmute(user, activity_one_actor)
lain's avatar
lain committed
713

714
    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
lain's avatar
lain committed
715
716
717
718
719

    assert Enum.member?(activities, activity_two)
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, activity_one)

720
    activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
721
    {:ok, _user_relationships} = User.mute(user, activity_three_actor)
lain's avatar
lain committed
722
    {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
lain's avatar
lain committed
723
    %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
724
    activity_three = Activity.get_by_id(activity_three.id)
lain's avatar
lain committed
725

726
    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
lain's avatar
lain committed
727
728
729
730
731
732

    assert Enum.member?(activities, activity_two)
    refute Enum.member?(activities, activity_three)
    refute Enum.member?(activities, boost_activity)
    assert Enum.member?(activities, activity_one)

733
    activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
lain's avatar
lain committed
734
735
736
737
738
739
740

    assert Enum.member?(activities, activity_two)
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, boost_activity)
    assert Enum.member?(activities, activity_one)
  end

741
742
  test "doesn't return thread muted activities" do
    user = insert(:user)
kaniini's avatar
kaniini committed
743
    _activity_one = insert(:note_activity)
744
745
746
747
748
    note_two = insert(:note, data: %{"context" => "suya.."})
    activity_two = insert(:note_activity, note: note_two)

    {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)

749
    assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
750
751
752
753
  end

  test "returns thread muted activities when with_muted is set" do
    user = insert(:user)
kaniini's avatar
kaniini committed
754
    _activity_one = insert(:note_activity)
755
756
757
    note_two = insert(:note, data: %{"context" => "suya.."})
    activity_two = insert(:note_activity, note: note_two)

Maksim's avatar
Maksim committed
758
    {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
759

kaniini's avatar
kaniini committed
760
    assert [_activity_two, _activity_one] =
761
             ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
762
763
  end

764
765
766
767
768
769
770
  test "does include announces on request" do
    activity_three = insert(:note_activity)
    user = insert(:user)
    booster = insert(:user)

    {:ok, user} = User.follow(user, booster)

lain's avatar
lain committed
771
    {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
772

773
    [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
774
775
776
777

    assert announce_activity.id == announce.id
  end

778
779
780
781
782
  test "excludes reblogs on request" do
    user = insert(:user)
    {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
    {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})

783
    [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
784
785
786
787

    assert activity == expected_activity
  end

lain's avatar
lain committed
788
  describe "public fetch activities" do
lain's avatar
lain committed
789
790
    test "doesn't retrieve unlisted activities" do
      user = insert(:user)
lain's avatar
lain committed
791

792
      {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
lain's avatar
lain committed
793

794
      {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
lain's avatar
lain committed
795
796
797
798
799
800

      [activity] = ActivityPub.fetch_public_activities()

      assert activity == listed_activity
    end

lain's avatar
lain committed
801
    test "retrieves public activities" do
lain's avatar
lain committed
802
      _activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
803

lain's avatar
lain committed
804
      %{public: public} = ActivityBuilder.public_and_non_public()
lain's avatar
lain committed
805

lain's avatar
lain committed
806
      activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
807
808
809
      assert length(activities) == 1
      assert Enum.at(activities, 0) == public
    end
lain's avatar
lain committed
810
811

    test "retrieves a maximum of 20 activities" do
Maksim's avatar
Maksim committed
812
813
      ActivityBuilder.insert_list(10)
      expected_activities = ActivityBuilder.insert_list(20)
lain's avatar
lain committed
814

lain's avatar
lain committed
815
      activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
816

Maksim's avatar
Maksim committed
817
      assert collect_ids(activities) == collect_ids(expected_activities)
lain's avatar
lain committed
818
819
      assert length(activities) == 20
    end
lain's avatar
lain committed
820
821
822

    test "retrieves ids starting from a since_id" do
      activities = ActivityBuilder.insert_list(30)
Maksim's avatar
Maksim committed
823
      expected_activities = ActivityBuilder.insert_list(10)
lain's avatar
lain committed
824
825
      since_id = List.last(activities).id

826
      activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
lain's avatar
lain committed
827

Maksim's avatar
Maksim committed
828
      assert collect_ids(activities) == collect_ids(expected_activities)
lain's avatar
lain committed
829
830
      assert length(activities) == 10
    end
831
832

    test "retrieves ids up to max_id" do
Maksim's avatar
Maksim committed
833
834
835
836
837
838
839
      ActivityBuilder.insert_list(10)
      expected_activities = ActivityBuilder.insert_list(20)

      %{id: max_id} =
        10
        |> ActivityBuilder.insert_list()
        |> List.first()
840

841
      activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
842
843

      assert length(activities) == 20
Maksim's avatar
Maksim committed
844
      assert collect_ids(activities) == collect_ids(expected_activities)
845
    end
846

847
    test "paginates via offset/limit" do
Maksim's avatar
Maksim committed
848
849
850
851
      _first_part_activities = ActivityBuilder.insert_list(10)
      second_part_activities = ActivityBuilder.insert_list(10)

      later_activities = ActivityBuilder.insert_list(10)
852

853
      activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
854
855

      assert length(activities) == 20
Maksim's avatar
Maksim committed
856
857
858

      assert collect_ids(activities) ==
               collect_ids(second_part_activities) ++ collect_ids(later_activities)
859
860
    end

861
862
863
864
    test "doesn't return reblogs for users for whom reblogs have been muted" do
      activity = insert(:note_activity)
      user = insert(:user)
      booster = insert(:user)
865
      {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
866

lain's avatar
lain committed
867
      {:ok, activity} = CommonAPI.repeat(activity.id, booster)
868

869
      activities = ActivityPub.fetch_activities([], %{muting_user: user})
870

rinpatch's avatar
rinpatch committed
871
872
873
874
875
876
877
      refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
    end

    test "returns reblogs for users for whom reblogs have not been muted" do
      activity = insert(:note_activity)
      user = insert(:user)
      booster = insert(:user)
878
879
      {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
      {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
rinpatch's avatar
rinpatch committed
880

lain's avatar
lain committed
881
      {:ok, activity} = CommonAPI.repeat(activity.id, booster)
rinpatch's avatar
rinpatch committed
882

883
      activities = ActivityPub.fetch_activities([], %{muting_user: user})
rinpatch's avatar
rinpatch committed
884
885

      assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
886
    end
lain's avatar
lain committed
887
  end
lain's avatar
lain committed
888
889
890

  describe "uploading files" do
    test "copies the file to the configured folder" do
lain's avatar
lain committed
891
892
893
894
895
      file = %Plug.Upload{
        content_type: "image/jpg",
        path: Path.absname("test/fixtures/image.jpg"),
        filename: "an_image.jpg"
      }
lain's avatar
lain committed
896
897
898
899

      {:ok, %Object{} = object} = ActivityPub.upload(file)
      assert object.data["name"] == "an_image.jpg"
    end
lain's avatar
lain committed
900
901
902

    test "works with base64 encoded images" do
      file = %{
903
        img: data_uri()
lain's avatar
lain committed
904
905
      }

lain's avatar
lain committed
906
      {:ok, %Object{}} = ActivityPub.upload(file)
lain's avatar
lain committed
907
    end
lain's avatar
lain committed
908
  end
lain's avatar
lain committed
909

dtluna's avatar
dtluna committed
910
911
912
913
914
  describe "fetch the latest Follow" do
    test "fetches the latest Follow activity" do
      %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
      follower = Repo.get_by(User, ap_id: activity.data["actor"])
      followed = Repo.get_by(User, ap_id: activity.data["object"])
915

lain's avatar
lain committed
916
      assert activity == Utils.fetch_latest_follow(follower, followed)
917
    end
918
919
  end

920
  describe "following / unfollowing" do
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
    test "it reverts follow activity" do
      follower = insert(:user)
      followed = insert(:user)

      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
        assert {:error, :reverted} = ActivityPub.follow(follower, followed)
      end

      assert Repo.aggregate(Activity, :count, :id) == 0
      assert Repo.aggregate(Object, :count, :id) == 0
    end

    test "it reverts unfollow activity" do
      follower = insert(:user)
      followed = insert(:user)

      {:ok, follow_activity} = ActivityPub.follow(follower, followed)

      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
        assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
      end

      activity = Activity.get_by_id(follow_activity.id)
      assert activity.data["type"] == "Follow"
      assert activity.data["actor"] == follower.ap_id

      assert activity.data["object"] == followed.ap_id
    end

950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
    test "creates a follow activity" do
      follower = insert(:user)
      followed = insert(:user)

      {:ok, activity} = ActivityPub.follow(follower, followed)
      assert activity.data["type"] == "Follow"
      assert activity.data["actor"] == follower.ap_id
      assert activity.data["object"] == followed.ap_id
    end

    test "creates an undo activity for the last follow" do
      follower = insert(:user)
      followed = insert(:user)

      {:ok, follow_activity} = ActivityPub.follow(follower, followed)
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
      {:ok, activity} = ActivityPub.unfollow(follower, followed)

      assert activity.data["type"] == "Undo"
      assert activity.data["actor"] == follower.ap_id

      embedded_object = activity.data["object"]
      assert is_map(embedded_object)
      assert embedded_object["type"] == "Follow"
      assert embedded_object["object"] == followed.ap_id
      assert embedded_object["id"] == follow_activity.data["id"]
    end

    test "creates an undo activity for a pending follow request" do
      follower = insert(:user)
      followed = insert(:user, %{locked: true})

      {:ok, follow_activity} = ActivityPub.follow(follower, followed)
982
983
984
985
      {:ok, activity} = ActivityPub.unfollow(follower, followed)

      assert activity.data["type"] == "Undo"
      assert activity.data["actor"] == follower.ap_id
986

987
988
989
990
991
      embedded_object = activity.data["object"]
      assert is_map(embedded_object)
      assert embedded_object["type"] == "Follow"
      assert embedded_object["object"] == followed.ap_id
      assert embedded_object["id"] == follow_activity.data["id"]
992
993
994
    end
  end

995
  describe "blocking" do
996
997
998
999
1000
    test "reverts block activity on error" do
      [blocker, blocked] = insert_list(2, :user)

      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
        assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
For faster browsing, not all history is shown. View entire blame