twitter_api_test.exs 14.3 KB
Newer Older
lain's avatar
lain committed
1
2
defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
  use Pleroma.DataCase
3
  alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView}
HJ's avatar
HJ committed
4
  alias Pleroma.{Activity, User, Object, Repo, UserInviteToken}
lain's avatar
lain committed
5
  alias Pleroma.Web.ActivityPub.ActivityPub
6
  alias Pleroma.Web.TwitterAPI.ActivityView
lain's avatar
lain committed
7

lain's avatar
lain committed
8
9
  import Pleroma.Factory

lain's avatar
lain committed
10
  test "create a status" do
11
    user = insert(:user)
12
    mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
13

lain's avatar
lain committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    object_data = %{
      "type" => "Image",
      "url" => [
        %{
          "type" => "Link",
          "mediaType" => "image/jpg",
          "href" => "http://example.org/image.jpg"
        }
      ],
      "uuid" => 1
    }

    object = Repo.insert!(%Object{data: object_data})

lain's avatar
lain committed
28
    input = %{
lain's avatar
lain committed
29
30
      "status" =>
        "Hello again, @shp.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric",
lain's avatar
lain committed
31
      "media_ids" => [object.id]
lain's avatar
lain committed
32
33
    }

lain's avatar
lain committed
34
35
36
    {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)

    expected_text =
37
      "Hello again, <span><a data-user='#{mentioned_user.id}' class='mention' href='shp'>@<span>shp</span></a></span>.&lt;script&gt;&lt;/script&gt;<br>This is on another :moominmamma: line. <a data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>"
lain's avatar
lain committed
38

lain's avatar
lain committed
39
    assert get_in(activity.data, ["object", "content"]) == expected_text
lain's avatar
lain committed
40
    assert get_in(activity.data, ["object", "type"]) == "Note"
lain's avatar
lain committed
41
    assert get_in(activity.data, ["object", "actor"]) == user.ap_id
42
    assert get_in(activity.data, ["actor"]) == user.ap_id
lain's avatar
lain committed
43
    assert Enum.member?(get_in(activity.data, ["cc"]), User.ap_followers(user))
lain's avatar
lain committed
44
45
46
47
48
49

    assert Enum.member?(
             get_in(activity.data, ["to"]),
             "https://www.w3.org/ns/activitystreams#Public"
           )

50
    assert Enum.member?(get_in(activity.data, ["to"]), "shp")
51
    assert activity.local == true
lain's avatar
lain committed
52

lain's avatar
lain committed
53
54
    assert %{"moominmamma" => "http://localhost:4001/finmoji/128px/moominmamma-128.png"} =
             activity.data["object"]["emoji"]
55

lain's avatar
lain committed
56
57
58
    # hashtags
    assert activity.data["object"]["tag"] == ["2hu", "epic", "phantasmagoric"]

lain's avatar
lain committed
59
    # Add a context
lain's avatar
lain committed
60
61
    assert is_binary(get_in(activity.data, ["context"]))
    assert is_binary(get_in(activity.data, ["object", "context"]))
lain's avatar
lain committed
62

63
    assert is_list(activity.data["object"]["attachment"])
64
65

    assert activity.data["object"] == Object.get_by_ap_id(activity.data["object"]["id"]).data
66
67
68

    user = User.get_by_ap_id(user.ap_id)

lain's avatar
lain committed
69
    assert user.info.note_count == 1
lain's avatar
lain committed
70
71
72
  end

  test "create a status that is a reply" do
73
    user = insert(:user)
lain's avatar
lain committed
74

lain's avatar
lain committed
75
76
77
78
    input = %{
      "status" => "Hello again."
    }

lain's avatar
lain committed
79
    {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
lain's avatar
lain committed
80
81
82
83
84
85

    input = %{
      "status" => "Here's your (you).",
      "in_reply_to_status_id" => activity.id
    }

lain's avatar
lain committed
86
    {:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input)
lain's avatar
lain committed
87
88

    assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"])
lain's avatar
lain committed
89
90
91
92

    assert get_in(reply.data, ["object", "context"]) ==
             get_in(activity.data, ["object", "context"])

lain's avatar
lain committed
93
94
    assert get_in(reply.data, ["object", "inReplyTo"]) == get_in(activity.data, ["object", "id"])
    assert get_in(reply.data, ["object", "inReplyToStatusId"]) == activity.id
lain's avatar
lain committed
95
96
  end

97
  test "Follow another user using user_id" do
lain's avatar
lain committed
98
    user = insert(:user)
99
    followed = insert(:user)
lain's avatar
lain committed
100

lain's avatar
lain committed
101
    {:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id})
102
    assert User.ap_followers(followed) in user.following
lain's avatar
lain committed
103

lain's avatar
lain committed
104
    {:error, msg} = TwitterAPI.follow(user, %{"user_id" => followed.id})
105
    assert msg == "Could not follow user: #{followed.nickname} is already on your list."
106
107
108
  end

  test "Follow another user using screen_name" do
109
    user = insert(:user)
110
    followed = insert(:user)
lain's avatar
lain committed
111

lain's avatar
lain committed
112
113
114
    {:ok, user, followed, _activity} =
      TwitterAPI.follow(user, %{"screen_name" => followed.nickname})

115
    assert User.ap_followers(followed) in user.following
116

117
    followed = User.get_by_ap_id(followed.ap_id)
lain's avatar
lain committed
118
    assert followed.info.follower_count == 1
119

lain's avatar
lain committed
120
    {:error, msg} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname})
121
    assert msg == "Could not follow user: #{followed.nickname} is already on your list."
lain's avatar
lain committed
122
  end
lain's avatar
lain committed
123

124
125
126
  test "Unfollow another user using user_id" do
    unfollowed = insert(:user)
    user = insert(:user, %{following: [User.ap_followers(unfollowed)]})
127
    ActivityPub.follow(user, unfollowed)
128

lain's avatar
lain committed
129
    {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id})
130
    assert user.following == []
lain's avatar
lain committed
131

lain's avatar
lain committed
132
    {:error, msg} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id})
133
    assert msg == "Not subscribed!"
134
  end
lain's avatar
lain committed
135

136
  test "Unfollow another user using screen_name" do
137
138
    unfollowed = insert(:user)
    user = insert(:user, %{following: [User.ap_followers(unfollowed)]})
lain's avatar
lain committed
139

140
141
    ActivityPub.follow(user, unfollowed)

lain's avatar
lain committed
142
    {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname})
lain's avatar
lain committed
143
    assert user.following == []
144

lain's avatar
lain committed
145
    {:error, msg} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname})
146
    assert msg == "Not subscribed!"
lain's avatar
lain committed
147
  end
148

eal's avatar
eal committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  test "Block another user using user_id" do
    user = insert(:user)
    blocked = insert(:user)

    {:ok, user, blocked} = TwitterAPI.block(user, %{"user_id" => blocked.id})
    assert User.blocks?(user, blocked)
  end

  test "Block another user using screen_name" do
    user = insert(:user)
    blocked = insert(:user)

    {:ok, user, blocked} = TwitterAPI.block(user, %{"screen_name" => blocked.nickname})
    assert User.blocks?(user, blocked)
  end

  test "Unblock another user using user_id" do
    unblocked = insert(:user)
    user = insert(:user)
normandy's avatar
normandy committed
168
    {:ok, user, _unblocked} = TwitterAPI.block(user, %{"user_id" => unblocked.id})
eal's avatar
eal committed
169

lain's avatar
lain committed
170
    {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"user_id" => unblocked.id})
lain's avatar
lain committed
171
    assert user.info.blocks == []
eal's avatar
eal committed
172
173
174
175
176
  end

  test "Unblock another user using screen_name" do
    unblocked = insert(:user)
    user = insert(:user)
normandy's avatar
normandy committed
177
    {:ok, user, _unblocked} = TwitterAPI.block(user, %{"screen_name" => unblocked.nickname})
eal's avatar
eal committed
178

lain's avatar
lain committed
179
    {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"screen_name" => unblocked.nickname})
lain's avatar
lain committed
180
    assert user.info.blocks == []
eal's avatar
eal committed
181
182
  end

lain's avatar
lain committed
183
  test "upload a file" do
184
    user = insert(:user)
Ivan Tashkinov's avatar
Ivan Tashkinov committed
185

lain's avatar
lain committed
186
187
188
189
190
    file = %Plug.Upload{
      content_type: "image/jpg",
      path: Path.absname("test/fixtures/image.jpg"),
      filename: "an_image.jpg"
    }
lain's avatar
lain committed
191

192
    response = TwitterAPI.upload(file, user)
lain's avatar
lain committed
193
194
195

    assert is_binary(response)
  end
196

197
  test "it favorites a status, returns the updated activity" do
lain's avatar
lain committed
198
199
200
    user = insert(:user)
    note_activity = insert(:note_activity)

lain's avatar
lain committed
201
    {:ok, status} = TwitterAPI.fav(user, note_activity.id)
lain's avatar
lain committed
202
203
    updated_activity = Activity.get_by_ap_id(note_activity.data["id"])

204
    assert status == updated_activity
lain's avatar
lain committed
205
  end
lain's avatar
lain committed
206

207
  test "it unfavorites a status, returns the updated activity" do
lain's avatar
lain committed
208
209
210
211
    user = insert(:user)
    note_activity = insert(:note_activity)
    object = Object.get_by_ap_id(note_activity.data["object"]["id"])

lain's avatar
lain committed
212
    {:ok, _like_activity, _object} = ActivityPub.like(user, object)
lain's avatar
lain committed
213
    updated_activity = Activity.get_by_ap_id(note_activity.data["id"])
lain's avatar
lain committed
214

215
    assert ActivityView.render("activity.json", activity: updated_activity)["fave_num"] == 1
lain's avatar
lain committed
216

217
    {:ok, activity} = TwitterAPI.unfav(user, note_activity.id)
lain's avatar
lain committed
218

219
    assert ActivityView.render("activity.json", activity: activity)["fave_num"] == 0
lain's avatar
lain committed
220
221
  end

lain's avatar
lain committed
222
223
224
225
  test "it retweets a status and returns the retweet" do
    user = insert(:user)
    note_activity = insert(:note_activity)

lain's avatar
lain committed
226
    {:ok, status} = TwitterAPI.repeat(user, note_activity.id)
lain's avatar
lain committed
227
228
    updated_activity = Activity.get_by_ap_id(note_activity.data["id"])

229
    assert status == updated_activity
lain's avatar
lain committed
230
231
  end

232
233
234
235
236
237
238
239
240
241
242
  test "it unretweets an already retweeted status" do
    user = insert(:user)
    note_activity = insert(:note_activity)

    {:ok, _status} = TwitterAPI.repeat(user, note_activity.id)
    {:ok, status} = TwitterAPI.unrepeat(user, note_activity.id)
    updated_activity = Activity.get_by_ap_id(note_activity.data["id"])

    assert status == updated_activity
  end

lain's avatar
lain committed
243
244
245
246
247
  test "it registers a new user and returns the user." do
    data = %{
      "nickname" => "lain",
      "email" => "lain@wired.jp",
      "fullname" => "lain iwakura",
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
      "password" => "bear",
      "confirm" => "bear"
    }

    {:ok, user} = TwitterAPI.register_user(data)

    fetched_user = Repo.get_by(User, nickname: "lain")

    assert UserView.render("show.json", %{user: user}) ==
             UserView.render("show.json", %{user: fetched_user})
  end

  test "it registers a new user with empty string in bio and returns the user." do
    data = %{
      "nickname" => "lain",
      "email" => "lain@wired.jp",
      "fullname" => "lain iwakura",
      "bio" => "",
lain's avatar
lain committed
266
267
268
269
270
271
272
      "password" => "bear",
      "confirm" => "bear"
    }

    {:ok, user} = TwitterAPI.register_user(data)

    fetched_user = Repo.get_by(User, nickname: "lain")
lain's avatar
lain committed
273
274
275

    assert UserView.render("show.json", %{user: user}) ==
             UserView.render("show.json", %{user: fetched_user})
lain's avatar
lain committed
276
277
  end

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  @moduletag skip: "needs 'account_activation_required: true' in config"
  test "it sends confirmation email if :account_activation_required is specified in instance config" do
    setting = Pleroma.Config.get([:instance, :account_activation_required])

    unless setting do
      Pleroma.Config.put([:instance, :account_activation_required], true)
      on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
    end

    data = %{
      "nickname" => "lain",
      "email" => "lain@wired.jp",
      "fullname" => "lain iwakura",
      "bio" => "",
      "password" => "bear",
      "confirm" => "bear"
    }

    {:ok, user} = TwitterAPI.register_user(data)

    assert user.info.confirmation_pending

    Swoosh.TestAssertions.assert_email_sent(Pleroma.UserEmail.account_confirmation_email(user))
  end

Maxim Filippov's avatar
Maxim Filippov committed
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  test "it registers a new user and parses mentions in the bio" do
    data1 = %{
      "nickname" => "john",
      "email" => "john@gmail.com",
      "fullname" => "John Doe",
      "bio" => "test",
      "password" => "bear",
      "confirm" => "bear"
    }

    {:ok, user1} = TwitterAPI.register_user(data1)

    data2 = %{
      "nickname" => "lain",
      "email" => "lain@wired.jp",
      "fullname" => "lain iwakura",
      "bio" => "@john test",
      "password" => "bear",
      "confirm" => "bear"
    }

    {:ok, user2} = TwitterAPI.register_user(data2)

Maxim Filippov's avatar
Maxim Filippov committed
326
    expected_text =
327
      "<span><a data-user='#{user1.id}' class='mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test"
Maxim Filippov's avatar
Maxim Filippov committed
328
329
330
331

    assert user2.bio == expected_text
  end

HJ's avatar
HJ committed
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  @moduletag skip: "needs 'registrations_open: false' in config"
  test "it registers a new user via invite token and returns the user." do
    {:ok, token} = UserInviteToken.create_token()

    data = %{
      "nickname" => "vinny",
      "email" => "pasta@pizza.vs",
      "fullname" => "Vinny Vinesauce",
      "bio" => "streamer",
      "password" => "hiptofbees",
      "confirm" => "hiptofbees",
      "token" => token.token
    }

    {:ok, user} = TwitterAPI.register_user(data)

    fetched_user = Repo.get_by(User, nickname: "vinny")
    token = Repo.get_by(UserInviteToken, token: token.token)

    assert token.used == true
HJ's avatar
HJ committed
352

HJ's avatar
HJ committed
353
    assert UserView.render("show.json", %{user: user}) ==
HJ's avatar
HJ committed
354
             UserView.render("show.json", %{user: fetched_user})
HJ's avatar
HJ committed
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  end

  @moduletag skip: "needs 'registrations_open: false' in config"
  test "it returns an error if invalid token submitted" do
    data = %{
      "nickname" => "GrimReaper",
      "email" => "death@reapers.afterlife",
      "fullname" => "Reaper Grim",
      "bio" => "Your time has come",
      "password" => "scythe",
      "confirm" => "scythe",
      "token" => "DudeLetMeInImAFairy"
    }

    {:error, msg} = TwitterAPI.register_user(data)

    assert msg == "Invalid token"
    refute Repo.get_by(User, nickname: "GrimReaper")
  end

  @moduletag skip: "needs 'registrations_open: false' in config"
  test "it returns an error if expired token submitted" do
    {:ok, token} = UserInviteToken.create_token()
    UserInviteToken.mark_as_used(token.token)

    data = %{
      "nickname" => "GrimReaper",
      "email" => "death@reapers.afterlife",
      "fullname" => "Reaper Grim",
      "bio" => "Your time has come",
      "password" => "scythe",
      "confirm" => "scythe",
      "token" => token.token
    }

    {:error, msg} = TwitterAPI.register_user(data)

    assert msg == "Expired token"
    refute Repo.get_by(User, nickname: "GrimReaper")
  end

lain's avatar
lain committed
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  test "it returns the error on registration problems" do
    data = %{
      "nickname" => "lain",
      "email" => "lain@wired.jp",
      "fullname" => "lain iwakura",
      "bio" => "close the world.",
      "password" => "bear"
    }

    {:error, error_object} = TwitterAPI.register_user(data)

    assert is_binary(error_object[:error])
    refute Repo.get_by(User, nickname: "lain")
  end

411
412
  test "it assigns an integer conversation_id" do
    note_activity = insert(:note_activity)
413
    status = ActivityView.render("activity.json", activity: note_activity)
414
415
416
417

    assert is_number(status["statusnet_conversation_id"])
  end

lain's avatar
lain committed
418
  setup do
419
420
    Supervisor.terminate_child(Pleroma.Supervisor, Cachex)
    Supervisor.restart_child(Pleroma.Supervisor, Cachex)
lain's avatar
lain committed
421
422
    :ok
  end
423
424
425
426
427
428
429
430
431
432

  describe "context_to_conversation_id" do
    test "creates a mapping object" do
      conversation_id = TwitterAPI.context_to_conversation_id("random context")
      object = Object.get_by_ap_id("random context")

      assert conversation_id == object.id
    end

    test "returns an existing mapping for an existing object" do
lain's avatar
lain committed
433
      {:ok, object} = Object.context_mapping("random context") |> Repo.insert()
434
435
436
437
438
      conversation_id = TwitterAPI.context_to_conversation_id("random context")

      assert conversation_id == object.id
    end
  end
lain's avatar
lain committed
439
440
441

  describe "fetching a user by uri" do
    test "fetches a user by uri" do
442
      id = "https://mastodon.social/users/lambadalambda"
lain's avatar
lain committed
443
      user = insert(:user)
444
445
      {:ok, represented} = TwitterAPI.get_external_profile(user, id)
      remote = User.get_by_ap_id(id)
lain's avatar
lain committed
446

lain's avatar
lain committed
447
      assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"]
448
449

      # Also fetches the feed.
450
      # assert Activity.get_create_activity_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status")
lain's avatar
lain committed
451
452
    end
  end
lain's avatar
lain committed
453
end