activity_pub_test.exs 51.9 KB
Newer Older
kaniini's avatar
kaniini committed
1
# Pleroma: A lightweight social networking server
minibikini's avatar
Reports  
minibikini committed
2
# Copyright © 2017-2019 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
Haelwenn's avatar
Haelwenn committed
7
  alias Pleroma.Activity
8
  alias Pleroma.Builders.ActivityBuilder
Haelwenn's avatar
Haelwenn committed
9 10
  alias Pleroma.Object
  alias Pleroma.User
11 12
  alias Pleroma.Web.ActivityPub.ActivityPub
  alias Pleroma.Web.ActivityPub.Utils
13
  alias Pleroma.Web.AdminAPI.AccountView
14
  alias Pleroma.Web.CommonAPI
lain's avatar
lain committed
15

lain's avatar
lain committed
16
  import Pleroma.Factory
Maksim's avatar
Maksim committed
17
  import Tesla.Mock
18
  import Mock
Maksim's avatar
Maksim committed
19 20 21 22 23

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

25 26
  clear_config([:instance, :federating])

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
  describe "streaming out participations" do
    test "it streams them out" do
      user = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})

      {: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)

42
        assert called(Pleroma.Web.Streamer.stream("participation", participations))
43 44
      end
    end
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

    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, %{
            "status" => "@#{user_two.nickname}",
            "visibility" => "direct"
          })

        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
66 67
  end

lain's avatar
lain committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  describe "fetching restricted by visibility" do
    test "it restricts by the appropriate visibility" do
      user = insert(:user)

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

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

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

      {:ok, private_activity} =
        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})

      activities =
        ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})

      assert activities == [direct_activity]

      activities =
        ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})

      assert activities == [unlisted_activity]

      activities =
        ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})

      assert activities == [private_activity]

      activities =
        ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})

      assert activities == [public_activity]
101 102 103 104 105 106 107 108

      activities =
        ActivityPub.fetch_activities([], %{
          :visibility => ~w[private public],
          "actor_id" => user.ap_id
        })

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

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  describe "fetching excluded by visibility" do
    test "it excludes by the appropriate visibility" do
      user = insert(:user)

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

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

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

      {:ok, private_activity} =
        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})

      activities =
        ActivityPub.fetch_activities([], %{
          "exclude_visibilities" => "direct",
          "actor_id" => user.ap_id
        })

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

      activities =
        ActivityPub.fetch_activities([], %{
          "exclude_visibilities" => "unlisted",
          "actor_id" => user.ap_id
        })

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

      activities =
        ActivityPub.fetch_activities([], %{
          "exclude_visibilities" => "private",
          "actor_id" => user.ap_id
        })

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

      activities =
        ActivityPub.fetch_activities([], %{
          "exclude_visibilities" => "public",
          "actor_id" => user.ap_id
        })

      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
172 173 174 175 176 177
  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"
178 179
      assert user.source_data
      assert user.ap_enabled
lain's avatar
lain committed
180 181
      assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
    end
182

183 184 185
    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)
186
      assert User.invisible?(user)
187 188
    end

189 190 191 192 193 194 195
    test "it fetches the appropriate tag-restricted posts" do
      user = insert(:user)

      {: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"})

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

      fetch_two =
        ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
200 201 202

      fetch_three =
        ActivityPub.fetch_activities([], %{
203
          "type" => "Create",
204 205 206 207
          "tag" => ["test", "essais"],
          "tag_reject" => ["reject"]
        })

Haelwenn's avatar
Haelwenn committed
208 209
      fetch_four =
        ActivityPub.fetch_activities([], %{
210
          "type" => "Create",
Haelwenn's avatar
Haelwenn committed
211 212 213 214
          "tag" => ["test"],
          "tag_all" => ["test", "reject"]
        })

215 216 217
      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
218
      assert fetch_four == [status_three]
219
    end
lain's avatar
lain committed
220 221
  end

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

      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

241
    test "doesn't drop activities with content being null" do
lain's avatar
lain committed
242 243
      user = insert(:user)

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

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

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

262
      assert activity.id == new_activity.id
lain's avatar
lain committed
263 264
    end

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

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

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

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

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

      {:ok, %Activity{} = activity} = ActivityPub.insert(data)
      assert activity.data["ok"] == data["ok"]
      assert activity.data["id"] == given_id
301 302 303 304 305
      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
306 307
      user = insert(:user)

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

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

      assert is_binary(activity.data["context"])
Roman Chvanikov's avatar
Roman Chvanikov committed
323
      assert is_binary(object.data["context"])
324
      assert activity.data["context_id"]
Roman Chvanikov's avatar
Roman Chvanikov committed
325
      assert object.data["context_id"]
lain's avatar
lain committed
326
    end
lain's avatar
lain committed
327

lain's avatar
lain committed
328
    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
329 330
      user = insert(:user)

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

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

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
  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)
370
      assert user.note_count == 0
371 372 373 374 375 376 377 378 379 380 381 382 383
    end

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

      timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})

      assert length(timeline) == 3
    end
  end

lain's avatar
lain committed
384 385
  describe "create activities" do
    test "removes doubled 'to' recipients" do
eal's avatar
eal committed
386 387
      user = insert(:user)

lain's avatar
lain committed
388 389 390
      {:ok, activity} =
        ActivityPub.create(%{
          to: ["user1", "user1", "user2"],
eal's avatar
eal committed
391
          actor: user,
lain's avatar
lain committed
392
          context: "",
393 394 395 396 397
          object: %{
            "to" => ["user1", "user1", "user2"],
            "type" => "Note",
            "content" => "testing"
          }
lain's avatar
lain committed
398 399
        })

lain's avatar
lain committed
400
      assert activity.data["to"] == ["user1", "user2"]
eal's avatar
eal committed
401
      assert activity.actor == user.ap_id
Maxim Filippov's avatar
Maxim Filippov committed
402
      assert activity.recipients == ["user1", "user2", user.ap_id]
lain's avatar
lain committed
403
    end
404 405 406 407 408

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

      {:ok, _} =
minibikini's avatar
minibikini committed
409 410 411 412
        CommonAPI.post(User.get_cached_by_id(user.id), %{
          "status" => "1",
          "visibility" => "public"
        })
413 414

      {:ok, _} =
minibikini's avatar
minibikini committed
415 416 417 418
        CommonAPI.post(User.get_cached_by_id(user.id), %{
          "status" => "2",
          "visibility" => "unlisted"
        })
419 420

      {:ok, _} =
minibikini's avatar
minibikini committed
421 422 423 424
        CommonAPI.post(User.get_cached_by_id(user.id), %{
          "status" => "2",
          "visibility" => "private"
        })
425 426

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

minibikini's avatar
minibikini committed
432
      user = User.get_cached_by_id(user.id)
433
      assert user.note_count == 2
434
    end
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

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

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

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

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

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

      # direct
      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
      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
464 465
  end

lain's avatar
lain committed
466 467 468 469
  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
470
      {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
lain's avatar
lain committed
471 472 473 474 475 476 477

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

lain's avatar
lain committed
478 479
  describe "fetch activities in context" do
    test "retrieves activities that have a given context" do
480 481 482 483
      {: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"})
484 485
      activity_five = insert(:note_activity)
      user = insert(:user)
lain's avatar
lain committed
486

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

489
      activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
490
      assert activities == [activity_two, activity]
lain's avatar
lain committed
491 492 493
    end
  end

494 495 496
  test "doesn't return blocked activities" do
    activity_one = insert(:note_activity)
    activity_two = insert(:note_activity)
497
    activity_three = insert(:note_activity)
498
    user = insert(:user)
499
    booster = insert(:user)
500 501
    {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})

502 503
    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
504 505

    assert Enum.member?(activities, activity_two)
506
    assert Enum.member?(activities, activity_three)
507 508 509 510
    refute Enum.member?(activities, activity_one)

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

511 512
    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
513 514

    assert Enum.member?(activities, activity_two)
515 516 517 518 519
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, activity_one)

    {:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
    {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
520
    %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
521
    activity_three = Activity.get_by_id(activity_three.id)
522

523 524
    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
525 526 527 528

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

531 532
    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
lain's avatar
lain committed
533 534

    assert Enum.member?(activities, activity_two)
535 536
    assert Enum.member?(activities, activity_three)
    assert Enum.member?(activities, boost_activity)
lain's avatar
lain committed
537
    assert Enum.member?(activities, activity_one)
538 539
  end

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
  test "doesn't return transitive interactions concerning blocked users" do
    blocker = insert(:user)
    blockee = insert(:user)
    friend = insert(:user)

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

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

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

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

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

    activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})

    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

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
  test "doesn't return announce activities concerning blocked users" do
    blocker = insert(:user)
    blockee = insert(:user)
    friend = insert(:user)

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

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

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

    {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)

    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
      |> 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
585 586 587 588 589 590 591 592 593 594 595 596
  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)

    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})

    refute activity in activities
597 598 599 600 601 602 603 604 605

    followed_user = insert(:user)
    ActivityPub.follow(user, followed_user)
    {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)

    activities =
      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})

    refute repeat_activity in activities
Aaron Tinio's avatar
Aaron Tinio committed
606 607
  end

lain's avatar
lain committed
608 609 610 611 612 613 614 615
  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)
    {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})

616 617
    activities =
      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
lain's avatar
lain committed
618 619 620 621 622

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

lain's avatar
lain committed
623
    # Calling with 'with_muted' will deliver muted activities, too.
624 625 626 627 628 629
    activities =
      ActivityPub.fetch_activities([], %{
        "muting_user" => user,
        "with_muted" => true,
        "skip_preload" => true
      })
lain's avatar
lain committed
630 631 632 633 634

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

lain's avatar
lain committed
635 636
    {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})

637 638
    activities =
      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
lain's avatar
lain committed
639 640 641 642 643 644 645 646

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

    {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
    {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
    %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
647
    activity_three = Activity.get_by_id(activity_three.id)
lain's avatar
lain committed
648

649 650
    activities =
      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
lain's avatar
lain committed
651 652 653 654 655 656

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

657
    activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
lain's avatar
lain committed
658 659 660 661 662 663 664

    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

665 666
  test "doesn't return thread muted activities" do
    user = insert(:user)
kaniini's avatar
kaniini committed
667
    _activity_one = insert(:note_activity)
668 669 670 671 672
    note_two = insert(:note, data: %{"context" => "suya.."})
    activity_two = insert(:note_activity, note: note_two)

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

kaniini's avatar
kaniini committed
673
    assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
674 675 676 677
  end

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

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

kaniini's avatar
kaniini committed
684
    assert [_activity_two, _activity_one] =
685 686 687
             ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
  end

688 689 690 691 692 693 694 695 696
  test "does include announces on request" do
    activity_three = insert(:note_activity)
    user = insert(:user)
    booster = insert(:user)

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

    {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)

697
    [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
698 699 700 701

    assert announce_activity.id == announce.id
  end

702 703 704 705 706 707 708 709 710 711
  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})

    [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})

    assert activity == expected_activity
  end

lain's avatar
lain committed
712
  describe "public fetch activities" do
lain's avatar
lain committed
713 714
    test "doesn't retrieve unlisted activities" do
      user = insert(:user)
lain's avatar
lain committed
715

Maksim's avatar
Maksim committed
716
      {:ok, _unlisted_activity} =
lain's avatar
lain committed
717 718
        CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})

lain's avatar
lain committed
719 720 721 722 723 724 725
      {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})

      [activity] = ActivityPub.fetch_public_activities()

      assert activity == listed_activity
    end

lain's avatar
lain committed
726
    test "retrieves public activities" do
lain's avatar
lain committed
727
      _activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
728

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

lain's avatar
lain committed
731
      activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
732 733 734
      assert length(activities) == 1
      assert Enum.at(activities, 0) == public
    end
lain's avatar
lain committed
735 736

    test "retrieves a maximum of 20 activities" do
Maksim's avatar
Maksim committed
737 738
      ActivityBuilder.insert_list(10)
      expected_activities = ActivityBuilder.insert_list(20)
lain's avatar
lain committed
739

lain's avatar
lain committed
740
      activities = ActivityPub.fetch_public_activities()
lain's avatar
lain committed
741

Maksim's avatar
Maksim committed
742
      assert collect_ids(activities) == collect_ids(expected_activities)
lain's avatar
lain committed
743 744
      assert length(activities) == 20
    end
lain's avatar
lain committed
745 746 747

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

lain's avatar
lain committed
751
      activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
lain's avatar
lain committed
752

Maksim's avatar
Maksim committed
753
      assert collect_ids(activities) == collect_ids(expected_activities)
lain's avatar
lain committed
754 755
      assert length(activities) == 10
    end
756 757

    test "retrieves ids up to max_id" do
Maksim's avatar
Maksim committed
758 759 760 761 762 763 764
      ActivityBuilder.insert_list(10)
      expected_activities = ActivityBuilder.insert_list(20)

      %{id: max_id} =
        10
        |> ActivityBuilder.insert_list()
        |> List.first()
765 766 767 768

      activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})

      assert length(activities) == 20
Maksim's avatar
Maksim committed
769
      assert collect_ids(activities) == collect_ids(expected_activities)
770
    end
771

772
    test "paginates via offset/limit" do
Maksim's avatar
Maksim committed
773 774 775 776
      _first_part_activities = ActivityBuilder.insert_list(10)
      second_part_activities = ActivityBuilder.insert_list(10)

      later_activities = ActivityBuilder.insert_list(10)
777 778 779 780 781

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

      assert length(activities) == 20
Maksim's avatar
Maksim committed
782 783 784

      assert collect_ids(activities) ==
               collect_ids(second_part_activities) ++ collect_ids(later_activities)
785 786
    end

787 788 789 790 791 792 793 794 795 796
    test "doesn't return reblogs for users for whom reblogs have been muted" do
      activity = insert(:note_activity)
      user = insert(:user)
      booster = insert(:user)
      {:ok, user} = CommonAPI.hide_reblogs(user, booster)

      {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)

      activities = ActivityPub.fetch_activities([], %{"muting_user" => user})

rinpatch's avatar
rinpatch committed
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
      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)
      {:ok, user} = CommonAPI.hide_reblogs(user, booster)
      {:ok, user} = CommonAPI.show_reblogs(user, booster)

      {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)

      activities = ActivityPub.fetch_activities([], %{"muting_user" => user})

      assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
812
    end
lain's avatar
lain committed
813
  end
lain's avatar
lain committed
814

lain's avatar
lain committed
815
  describe "react to an object" do
lain's avatar
lain committed
816 817 818 819 820 821 822 823 824 825 826 827
    test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
      Pleroma.Config.put([:instance, :federating], true)
      user = insert(:user)
      reactor = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
      assert object = Object.normalize(activity)

      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")

      assert called(Pleroma.Web.Federator.publish(reaction_activity))
    end

lain's avatar
lain committed
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
    test "adds an emoji reaction activity to the db" do
      user = insert(:user)
      reactor = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
      assert object = Object.normalize(activity)

      {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")

      assert reaction_activity

      assert reaction_activity.data["actor"] == reactor.ap_id
      assert reaction_activity.data["type"] == "EmojiReaction"
      assert reaction_activity.data["content"] == "🔥"
      assert reaction_activity.data["object"] == object.data["id"]
      assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
      assert reaction_activity.data["context"] == object.data["context"]
844 845
      assert object.data["reaction_count"] == 1
      assert object.data["reactions"]["🔥"] == [reactor.ap_id]
lain's avatar
lain committed
846 847 848
    end
  end

lain's avatar
.  
lain committed
849
  describe "unreacting to an object" do
850 851
    test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
      Pleroma.Config.put([:instance, :federating], true)
lain's avatar
.  
lain committed
852 853 854 855 856
      user = insert(:user)
      reactor = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
      assert object = Object.normalize(activity)

857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")

      assert called(Pleroma.Web.Federator.publish(reaction_activity))

      {:ok, unreaction_activity, _object} =
        ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])

      assert called(Pleroma.Web.Federator.publish(unreaction_activity))
    end

    test "adds an undo activity to the db" do
      user = insert(:user)
      reactor = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
      assert object = Object.normalize(activity)

      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")

      {:ok, unreaction_activity, _object} =
        ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])

      assert unreaction_activity.actor == reactor.ap_id
      assert unreaction_activity.data["object"] == reaction_activity.data["id"]
lain's avatar
.  
lain committed
880

881 882 883
      object = Object.get_by_ap_id(object.data["id"])
      assert object.data["reaction_count"] == 0
      assert object.data["reactions"] == %{}
lain's avatar
.  
lain committed
884 885 886
    end
  end

lain's avatar
lain committed
887
  describe "like an object" do
888 889 890 891 892 893 894 895
    test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
      Pleroma.Config.put([:instance, :federating], true)
      note_activity = insert(:note_activity)
      assert object_activity = Object.normalize(note_activity)

      user = insert(:user)

      {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
896
      assert called(Pleroma.Web.Federator.publish(like_activity))
897 898 899 900 901 902 903 904 905 906 907 908 909 910
    end

    test "returns exist activity if object already liked" do
      note_activity = insert(:note_activity)
      assert object_activity = Object.normalize(note_activity)

      user = insert(:user)

      {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)

      {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
      assert like_activity == like_activity_exist
    end

lain's avatar
lain committed
911 912
    test "adds a like activity to the db" do
      note_activity = insert(:note_activity)
913 914
      assert object = Object.normalize(note_activity)

lain's avatar
lain committed
915 916 917 918 919 920 921
      user = insert(:user)
      user_two = insert(:user)

      {:ok, like_activity, object} = ActivityPub.like(user, object)

      assert like_activity.data["actor"] == user.ap_id
      assert like_activity.data["type"] == "Like"
922
      assert like_activity.data["object"] == object.data["id"]
923
      assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
lain's avatar
lain committed
924
      assert like_activity.data["context"] == object.data["context"]
lain's avatar
lain committed
925
      assert object.data["like_count"] == 1
lain's avatar
lain committed
926 927 928 929 930 931 932
      assert object.data["likes"] == [user.ap_id]

      # Just return the original activity if the user already liked it.
      {:ok, same_like_activity, object} = ActivityPub.like(user, object)

      assert like_activity == same_like_activity
      assert object.data["likes"] == [user.ap_id]
933
      assert object.data["like_count"] == 1
lain's avatar
lain committed
934 935 936 937 938 939

      {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
      assert object.data["like_count"] == 2
    end
  end

lain's avatar
lain committed
940
  describe "unliking" do
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
    test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
      Pleroma.Config.put([:instance, :federating], true)

      note_activity = insert(:note_activity)
      object = Object.normalize(note_activity)
      user = insert(:user)

      {:ok, object} = ActivityPub.unlike(user, object)
      refute called(Pleroma.Web.Federator.publish())

      {:ok, _like_activity, object} = ActivityPub.like(user, object)
      assert object.data["like_count"] == 1

      {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
      assert object.data["like_count"] == 0

957
      assert called(Pleroma.Web.Federator.publish(unlike_activity))
958 959
    end

lain's avatar
lain committed
960 961
    test "unliking a previously liked object" do
      note_activity = insert(:note_activity)
962
      object = Object.normalize(note_activity)
lain's avatar
lain committed
963 964 965 966 967 968 969 970 971
      user = insert(:user)

      # Unliking something that hasn't been liked does nothing
      {:ok, object} = ActivityPub.unlike(user, object)
      assert object.data["like_count"] == 0

      {:ok, like_activity, object} = ActivityPub.like(user, object)
      assert object.data["like_count"] == 1

lain's avatar
lain committed
972
      {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
lain's avatar
lain committed
973 974
      assert object.data["like_count"] == 0

975
      assert Activity.get_by_id(like_activity.id) == nil
lain's avatar
lain committed
976
      assert note_activity.actor in unlike_activity.recipients
lain's avatar
lain committed
977 978 979
    end
  end

lain's avatar
lain committed
980 981 982
  describe "announcing an object" do
    test "adds an announce activity to the db" do
      note_activity = insert(:note_activity)
983
      object = Object.normalize(note_activity)
lain's avatar
lain committed
984 985 986 987 988
      user = insert(:user)

      {:ok, announce_activity, object} = ActivityPub.announce(user, object)
      assert object.data["announcement_count"] == 1
      assert object.data["announcements"] == [user.ap_id]
lain's avatar
lain committed
989 990 991 992 993 994

      assert announce_activity.data["to"] == [
               User.ap_followers(user),
               note_activity.data["actor"]
             ]

lain's avatar
lain committed
995 996
      assert announce_activity.data["object"] == object.data["id"]
      assert announce_activity.data["actor"] == user.ap_id
lain's avatar
lain committed
997
      assert announce_activity.data["context"] == object.data["context"]
lain's avatar
lain committed
998 999 1000
    end
  end

Thibaut Girka's avatar
Thibaut Girka committed
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
  describe "announcing a private object" do
    test "adds an announce activity to the db if the audience is not widened" do
      user = insert(:user)
      {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
      object = Object.normalize(note_activity)

      {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)

      assert announce_activity.data["to"] == [User.ap_followers(user)]

      assert announce_activity.data["object"] == object.data["id"]
      assert announce_activity.data["actor"] == user.ap_id
      assert announce_activity.data["context"] == object.data["context"]
    end

    test "does not add an announce activity to the db if the audience is widened" do
      user = insert(:user)
      {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
      object = Object.normalize(note_activity)

      assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
    end

    test "does not add an announce activity to the db if the announcer is not the author" do
      user = insert(:user)
      announcer = insert(:user)
      {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
      object = Object.normalize(note_activity)

      assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
    end
  end

normandy's avatar
normandy committed
1034 1035 1036
  describe "unannouncing an object" do
    test "unannouncing a previously announced object" do
      note_activity = insert(:note_activity)
1037
      object = Object.normalize(note_activity)
normandy's avatar
normandy committed
1038 1039 1040
      user = insert(:user)

      # Unannouncing an object that is not announced does nothing
normandy's avatar
normandy committed
1041 1042
      # {:ok, object} = ActivityPub.unannounce(user, object)
      # assert object.data["announcement_count"] == 0
normandy's avatar
normandy committed
1043 1044 1045 1046

      {:ok, announce_activity, object} = ActivityPub.announce(user, object)
      assert object.data["announcement_count"] == 1

1047
      {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
normandy's avatar
normandy committed
1048 1049
      assert object.data["announcement_count"] == 0

1050 1051
      assert unannounce_activity.data["to"] == [
               User.ap_followers(user),
lain's avatar
lain committed
1052
               object.data["actor"]
normandy's avatar
normandy committed
1053
             ]
normandy's avatar
normandy committed
1054

1055
      assert unannounce_activity.data["type"] == "Undo"
1056
      assert unannounce_activity.data["object"] == announce_activity.data
1057
      assert unannounce_activity.data["actor"] == user.ap_id
normandy's avatar
normandy committed
1058
      assert unannounce_activity.data["context"] == announce_activity.data["context"]
1059

1060
      assert Activity.get_by_id(announce_activity.id) == nil
normandy's avatar
normandy committed
1061 1062 1063
    end
  end

lain's avatar
lain committed
1064 1065
  describe "uploading files" do
    test "copies the file to the configured folder" do
lain's avatar
lain committed
1066 1067 1068 1069 1070
      file = %Plug.Upload{
        content_type: "image/jpg",
        path: Path.absname("test/fixtures/image.jpg"),
        filename: "an_image.jpg"
      }
lain's avatar
lain committed
1071 1072 1073 1074

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

    test "works with base64 encoded images" do
      file = %{
lain's avatar
lain committed
1078
        "img" => data_uri()
lain's avatar
lain committed
1079 1080
      }

lain's avatar
lain committed
1081
      {:ok, %Object{}} = ActivityPub.upload(file)
lain's avatar
lain committed
1082
    end
lain's avatar
lain committed
1083
  end
lain's avatar
lain committed
1084

dtluna's avatar
dtluna committed
1085 1086 1087 1088 1089
  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"])
1090

lain's avatar
lain committed
1091
      assert activity == Utils.fetch_latest_follow(follower, followed)
1092
    end
1093 1094
  end

1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
  describe "following / unfollowing" do
    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)
      {:ok, activity} = ActivityPub.unfollow(follower, followed)

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