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

5 6
defmodule Pleroma.ObjectTest do
  use Pleroma.DataCase
rinpatch's avatar
rinpatch committed
7
  import ExUnit.CaptureLog
8
  import Pleroma.Factory
rinpatch's avatar
rinpatch committed
9
  import Tesla.Mock
10
  alias Pleroma.Activity
Haelwenn's avatar
Haelwenn committed
11
  alias Pleroma.Object
12
  alias Pleroma.Repo
13
  alias Pleroma.Web.CommonAPI
14

rinpatch's avatar
rinpatch committed
15 16 17 18 19
  setup do
    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
    :ok
  end

20 21
  test "returns an object by it's AP id" do
    object = insert(:note)
lain's avatar
lain committed
22
    found_object = Object.get_by_ap_id(object.data["id"])
23 24 25

    assert object == found_object
  end
lain's avatar
lain committed
26 27 28 29 30 31 32

  describe "generic changeset" do
    test "it ensures uniqueness of the id" do
      object = insert(:note)
      cs = Object.change(%Object{}, %{data: %{id: object.data["id"]}})
      assert cs.valid?

lain's avatar
lain committed
33
      {:error, _result} = Repo.insert(cs)
lain's avatar
lain committed
34 35
    end
  end
36 37 38 39 40 41 42 43 44 45 46 47 48

  describe "deletion function" do
    test "deletes an object" do
      object = insert(:note)
      found_object = Object.get_by_ap_id(object.data["id"])

      assert object == found_object

      Object.delete(found_object)

      found_object = Object.get_by_ap_id(object.data["id"])

      refute object == found_object
49 50

      assert found_object.data["type"] == "Tombstone"
51 52 53 54 55 56 57 58
    end

    test "ensures cache is cleared for the object" do
      object = insert(:note)
      cached_object = Object.get_cached_by_ap_id(object.data["id"])

      assert object == cached_object

minibikini's avatar
minibikini committed
59 60
      Cachex.put(:web_resp_cache, URI.parse(object.data["id"]).path, "cofe")

61 62
      Object.delete(cached_object)

63
      {:ok, nil} = Cachex.get(:object_cache, "object:#{object.data["id"]}")
minibikini's avatar
minibikini committed
64
      {:ok, nil} = Cachex.get(:web_resp_cache, URI.parse(object.data["id"]).path)
65 66 67 68

      cached_object = Object.get_cached_by_ap_id(object.data["id"])

      refute object == cached_object
69 70

      assert cached_object.data["type"] == "Tombstone"
71 72
    end
  end
73 74 75

  describe "normalizer" do
    test "fetches unknown objects by default" do
William Pitcock's avatar
William Pitcock committed
76 77
      %Object{} =
        object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367")
78 79 80 81 82

      assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
    end

    test "fetches unknown objects when fetch_remote is explicitly true" do
William Pitcock's avatar
William Pitcock committed
83 84
      %Object{} =
        object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true)
85 86 87 88 89

      assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
    end

    test "does not fetch unknown objects when fetch_remote is false" do
William Pitcock's avatar
William Pitcock committed
90 91 92
      assert is_nil(
               Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false)
             )
93 94
    end
  end
rinpatch's avatar
rinpatch committed
95 96

  describe "get_by_id_and_maybe_refetch" do
97
    setup do
rinpatch's avatar
rinpatch committed
98 99 100 101 102 103 104 105
      mock(fn
        %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
          %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}

        env ->
          apply(HttpRequestMock, :request, [env])
      end)

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
      mock_modified = fn resp ->
        mock(fn
          %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
            resp

          env ->
            apply(HttpRequestMock, :request, [env])
        end)
      end

      on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)

      [mock_modified: mock_modified]
    end

    test "refetches if the time since the last refetch is greater than the interval", %{
      mock_modified: mock_modified
    } do
rinpatch's avatar
rinpatch committed
124 125 126
      %Object{} =
        object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")

127 128
      Object.set_cache(object)

rinpatch's avatar
rinpatch committed
129 130 131
      assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
      assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0

132 133 134 135
      mock_modified.(%Tesla.Env{
        status: 200,
        body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
      })
rinpatch's avatar
rinpatch committed
136 137

      updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
138 139
      object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
      assert updated_object == object_in_cache
rinpatch's avatar
rinpatch committed
140 141 142 143
      assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
      assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
    end

144
    test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
rinpatch's avatar
rinpatch committed
145 146 147
      %Object{} =
        object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")

148 149
      Object.set_cache(object)

rinpatch's avatar
rinpatch committed
150 151 152
      assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
      assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0

rinpatch's avatar
rinpatch committed
153
      assert capture_log(fn ->
154
               mock_modified.(%Tesla.Env{status: 404, body: ""})
rinpatch's avatar
rinpatch committed
155 156

               updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
157 158
               object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
               assert updated_object == object_in_cache
rinpatch's avatar
rinpatch committed
159 160 161 162
               assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
               assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
             end) =~
               "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
rinpatch's avatar
rinpatch committed
163 164
    end

165 166 167
    test "does not refetch if the time since the last refetch is greater than the interval", %{
      mock_modified: mock_modified
    } do
rinpatch's avatar
rinpatch committed
168 169 170
      %Object{} =
        object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")

171 172
      Object.set_cache(object)

rinpatch's avatar
rinpatch committed
173 174 175
      assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
      assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0

176 177 178 179
      mock_modified.(%Tesla.Env{
        status: 200,
        body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
      })
rinpatch's avatar
rinpatch committed
180 181

      updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
182 183
      object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
      assert updated_object == object_in_cache
rinpatch's avatar
rinpatch committed
184 185 186
      assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
      assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
    end
187 188 189 190 191

    test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
      %Object{} =
        object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")

192 193
      Object.set_cache(object)

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
      assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
      assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0

      user = insert(:user)
      activity = Activity.get_create_by_object_ap_id(object.data["id"])
      {:ok, _activity, object} = CommonAPI.favorite(activity.id, user)

      assert object.data["like_count"] == 1

      mock_modified.(%Tesla.Env{
        status: 200,
        body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
      })

      updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
209 210
      object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
      assert updated_object == object_in_cache
211 212 213 214 215
      assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
      assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3

      assert updated_object.data["like_count"] == 1
    end
rinpatch's avatar
rinpatch committed
216
  end
217
end