[#1149] Replaced RetryQueue with oban-based retries.
[akkoma] / test / web / activity_pub / publisher_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.PublisherTest do
6 use Pleroma.DataCase
7
8 import Pleroma.Factory
9 import Tesla.Mock
10 import Mock
11
12 alias Pleroma.Activity
13 alias Pleroma.Instances
14 alias Pleroma.Web.ActivityPub.Publisher
15
16 @as_public "https://www.w3.org/ns/activitystreams#Public"
17
18 setup do
19 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
20 :ok
21 end
22
23 describe "determine_inbox/2" do
24 test "it returns sharedInbox for messages involving as:Public in to" do
25 user =
26 insert(:user, %{
27 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
28 })
29
30 activity = %Activity{
31 data: %{"to" => [@as_public], "cc" => [user.follower_address]}
32 }
33
34 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
35 end
36
37 test "it returns sharedInbox for messages involving as:Public in cc" do
38 user =
39 insert(:user, %{
40 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
41 })
42
43 activity = %Activity{
44 data: %{"cc" => [@as_public], "to" => [user.follower_address]}
45 }
46
47 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
48 end
49
50 test "it returns sharedInbox for messages involving multiple recipients in to" do
51 user =
52 insert(:user, %{
53 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
54 })
55
56 user_two = insert(:user)
57 user_three = insert(:user)
58
59 activity = %Activity{
60 data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
61 }
62
63 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
64 end
65
66 test "it returns sharedInbox for messages involving multiple recipients in cc" do
67 user =
68 insert(:user, %{
69 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
70 })
71
72 user_two = insert(:user)
73 user_three = insert(:user)
74
75 activity = %Activity{
76 data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
77 }
78
79 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
80 end
81
82 test "it returns sharedInbox for messages involving multiple recipients in total" do
83 user =
84 insert(:user, %{
85 info: %{
86 source_data: %{
87 "inbox" => "http://example.com/personal-inbox",
88 "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
89 }
90 }
91 })
92
93 user_two = insert(:user)
94
95 activity = %Activity{
96 data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
97 }
98
99 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
100 end
101
102 test "it returns inbox for messages involving single recipients in total" do
103 user =
104 insert(:user, %{
105 info: %{
106 source_data: %{
107 "inbox" => "http://example.com/personal-inbox",
108 "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
109 }
110 }
111 })
112
113 activity = %Activity{
114 data: %{"to" => [user.ap_id], "cc" => []}
115 }
116
117 assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
118 end
119 end
120
121 describe "publish_one/1" do
122 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
123 Instances,
124 [:passthrough],
125 [] do
126 actor = insert(:user)
127 inbox = "http://200.site/users/nick1/inbox"
128
129 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
130
131 assert called(Instances.set_reachable(inbox))
132 end
133
134 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
135 Instances,
136 [:passthrough],
137 [] do
138 actor = insert(:user)
139 inbox = "http://200.site/users/nick1/inbox"
140
141 assert {:ok, _} =
142 Publisher.publish_one(%{
143 inbox: inbox,
144 json: "{}",
145 actor: actor,
146 id: 1,
147 unreachable_since: NaiveDateTime.utc_now()
148 })
149
150 assert called(Instances.set_reachable(inbox))
151 end
152
153 test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
154 Instances,
155 [:passthrough],
156 [] do
157 actor = insert(:user)
158 inbox = "http://200.site/users/nick1/inbox"
159
160 assert {:ok, _} =
161 Publisher.publish_one(%{
162 inbox: inbox,
163 json: "{}",
164 actor: actor,
165 id: 1,
166 unreachable_since: nil
167 })
168
169 refute called(Instances.set_reachable(inbox))
170 end
171
172 test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
173 Instances,
174 [:passthrough],
175 [] do
176 actor = insert(:user)
177 inbox = "http://404.site/users/nick1/inbox"
178
179 assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
180
181 assert called(Instances.set_unreachable(inbox))
182 end
183
184 test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
185 Instances,
186 [:passthrough],
187 [] do
188 actor = insert(:user)
189 inbox = "http://connrefused.site/users/nick1/inbox"
190
191 assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
192
193 assert called(Instances.set_unreachable(inbox))
194 end
195
196 test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
197 Instances,
198 [:passthrough],
199 [] do
200 actor = insert(:user)
201 inbox = "http://200.site/users/nick1/inbox"
202
203 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
204
205 refute called(Instances.set_unreachable(inbox))
206 end
207
208 test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
209 Instances,
210 [:passthrough],
211 [] do
212 actor = insert(:user)
213 inbox = "http://connrefused.site/users/nick1/inbox"
214
215 assert {:error, _} =
216 Publisher.publish_one(%{
217 inbox: inbox,
218 json: "{}",
219 actor: actor,
220 id: 1,
221 unreachable_since: NaiveDateTime.utc_now()
222 })
223
224 refute called(Instances.set_unreachable(inbox))
225 end
226 end
227
228 describe "publish/2" do
229 test_with_mock "publishes an activity with BCC to all relevant peers.",
230 Pleroma.Web.Federator.Publisher,
231 [:passthrough],
232 [] do
233 follower =
234 insert(:user,
235 local: false,
236 info: %{
237 ap_enabled: true,
238 source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
239 }
240 )
241
242 actor = insert(:user, follower_address: follower.ap_id)
243 user = insert(:user)
244
245 {:ok, _follower_one} = Pleroma.User.follow(follower, actor)
246 actor = refresh_record(actor)
247
248 note_activity =
249 insert(:note_activity,
250 recipients: [follower.ap_id],
251 data_attrs: %{"bcc" => [user.ap_id]}
252 )
253
254 res = Publisher.publish(actor, note_activity)
255 assert res == :ok
256
257 assert called(
258 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
259 inbox: "https://domain.com/users/nick1/inbox",
260 actor_id: actor.id,
261 id: note_activity.data["id"]
262 })
263 )
264 end
265 end
266 end