Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4057 lines
116KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
  5. use Pleroma.Web.ConnCase
  6. alias Ecto.Changeset
  7. alias Pleroma.Activity
  8. alias Pleroma.ActivityExpiration
  9. alias Pleroma.Config
  10. alias Pleroma.Notification
  11. alias Pleroma.Object
  12. alias Pleroma.Repo
  13. alias Pleroma.ScheduledActivity
  14. alias Pleroma.Tests.ObanHelpers
  15. alias Pleroma.User
  16. alias Pleroma.Web.ActivityPub.ActivityPub
  17. alias Pleroma.Web.CommonAPI
  18. alias Pleroma.Web.MastodonAPI.FilterView
  19. alias Pleroma.Web.OAuth.App
  20. alias Pleroma.Web.OAuth.Token
  21. alias Pleroma.Web.OStatus
  22. alias Pleroma.Web.Push
  23. import Pleroma.Factory
  24. import ExUnit.CaptureLog
  25. import Tesla.Mock
  26. import Swoosh.TestAssertions
  27. @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
  28. setup do
  29. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  30. :ok
  31. end
  32. clear_config([:instance, :public])
  33. clear_config([:rich_media, :enabled])
  34. test "the home timeline", %{conn: conn} do
  35. user = insert(:user)
  36. following = insert(:user)
  37. {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
  38. conn =
  39. conn
  40. |> assign(:user, user)
  41. |> get("/api/v1/timelines/home")
  42. assert Enum.empty?(json_response(conn, 200))
  43. {:ok, user} = User.follow(user, following)
  44. conn =
  45. build_conn()
  46. |> assign(:user, user)
  47. |> get("/api/v1/timelines/home")
  48. assert [%{"content" => "test"}] = json_response(conn, 200)
  49. end
  50. test "the public timeline", %{conn: conn} do
  51. following = insert(:user)
  52. capture_log(fn ->
  53. {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
  54. {:ok, [_activity]} =
  55. OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
  56. conn =
  57. conn
  58. |> get("/api/v1/timelines/public", %{"local" => "False"})
  59. assert length(json_response(conn, 200)) == 2
  60. conn =
  61. build_conn()
  62. |> get("/api/v1/timelines/public", %{"local" => "True"})
  63. assert [%{"content" => "test"}] = json_response(conn, 200)
  64. conn =
  65. build_conn()
  66. |> get("/api/v1/timelines/public", %{"local" => "1"})
  67. assert [%{"content" => "test"}] = json_response(conn, 200)
  68. end)
  69. end
  70. test "the public timeline when public is set to false", %{conn: conn} do
  71. Config.put([:instance, :public], false)
  72. assert conn
  73. |> get("/api/v1/timelines/public", %{"local" => "False"})
  74. |> json_response(403) == %{"error" => "This resource requires authentication."}
  75. end
  76. describe "posting statuses" do
  77. setup do
  78. user = insert(:user)
  79. conn =
  80. build_conn()
  81. |> assign(:user, user)
  82. [conn: conn]
  83. end
  84. test "posting a status", %{conn: conn} do
  85. idempotency_key = "Pikachu rocks!"
  86. conn_one =
  87. conn
  88. |> put_req_header("idempotency-key", idempotency_key)
  89. |> post("/api/v1/statuses", %{
  90. "status" => "cofe",
  91. "spoiler_text" => "2hu",
  92. "sensitive" => "false"
  93. })
  94. {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
  95. # Six hours
  96. assert ttl > :timer.seconds(6 * 60 * 60 - 1)
  97. assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
  98. json_response(conn_one, 200)
  99. assert Activity.get_by_id(id)
  100. conn_two =
  101. conn
  102. |> put_req_header("idempotency-key", idempotency_key)
  103. |> post("/api/v1/statuses", %{
  104. "status" => "cofe",
  105. "spoiler_text" => "2hu",
  106. "sensitive" => "false"
  107. })
  108. assert %{"id" => second_id} = json_response(conn_two, 200)
  109. assert id == second_id
  110. conn_three =
  111. conn
  112. |> post("/api/v1/statuses", %{
  113. "status" => "cofe",
  114. "spoiler_text" => "2hu",
  115. "sensitive" => "false"
  116. })
  117. assert %{"id" => third_id} = json_response(conn_three, 200)
  118. refute id == third_id
  119. # An activity that will expire:
  120. # 2 hours
  121. expires_in = 120 * 60
  122. conn_four =
  123. conn
  124. |> post("api/v1/statuses", %{
  125. "status" => "oolong",
  126. "expires_in" => expires_in
  127. })
  128. assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
  129. assert activity = Activity.get_by_id(fourth_id)
  130. assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
  131. estimated_expires_at =
  132. NaiveDateTime.utc_now()
  133. |> NaiveDateTime.add(expires_in)
  134. |> NaiveDateTime.truncate(:second)
  135. # This assert will fail if the test takes longer than a minute. I sure hope it never does:
  136. assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
  137. assert fourth_response["pleroma"]["expires_at"] ==
  138. NaiveDateTime.to_iso8601(expiration.scheduled_at)
  139. end
  140. test "replying to a status", %{conn: conn} do
  141. user = insert(:user)
  142. {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
  143. conn =
  144. conn
  145. |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
  146. assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
  147. activity = Activity.get_by_id(id)
  148. assert activity.data["context"] == replied_to.data["context"]
  149. assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
  150. end
  151. test "replying to a direct message with visibility other than direct", %{conn: conn} do
  152. user = insert(:user)
  153. {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
  154. Enum.each(["public", "private", "unlisted"], fn visibility ->
  155. conn =
  156. conn
  157. |> post("/api/v1/statuses", %{
  158. "status" => "@#{user.nickname} hey",
  159. "in_reply_to_id" => replied_to.id,
  160. "visibility" => visibility
  161. })
  162. assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
  163. end)
  164. end
  165. test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
  166. conn =
  167. conn
  168. |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
  169. assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
  170. assert Activity.get_by_id(id)
  171. end
  172. test "posting a sensitive status", %{conn: conn} do
  173. conn =
  174. conn
  175. |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
  176. assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
  177. assert Activity.get_by_id(id)
  178. end
  179. test "posting a fake status", %{conn: conn} do
  180. real_conn =
  181. conn
  182. |> post("/api/v1/statuses", %{
  183. "status" =>
  184. "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
  185. })
  186. real_status = json_response(real_conn, 200)
  187. assert real_status
  188. assert Object.get_by_ap_id(real_status["uri"])
  189. real_status =
  190. real_status
  191. |> Map.put("id", nil)
  192. |> Map.put("url", nil)
  193. |> Map.put("uri", nil)
  194. |> Map.put("created_at", nil)
  195. |> Kernel.put_in(["pleroma", "conversation_id"], nil)
  196. fake_conn =
  197. conn
  198. |> post("/api/v1/statuses", %{
  199. "status" =>
  200. "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
  201. "preview" => true
  202. })
  203. fake_status = json_response(fake_conn, 200)
  204. assert fake_status
  205. refute Object.get_by_ap_id(fake_status["uri"])
  206. fake_status =
  207. fake_status
  208. |> Map.put("id", nil)
  209. |> Map.put("url", nil)
  210. |> Map.put("uri", nil)
  211. |> Map.put("created_at", nil)
  212. |> Kernel.put_in(["pleroma", "conversation_id"], nil)
  213. assert real_status == fake_status
  214. end
  215. test "posting a status with OGP link preview", %{conn: conn} do
  216. Config.put([:rich_media, :enabled], true)
  217. conn =
  218. conn
  219. |> post("/api/v1/statuses", %{
  220. "status" => "https://example.com/ogp"
  221. })
  222. assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
  223. assert Activity.get_by_id(id)
  224. end
  225. test "posting a direct status", %{conn: conn} do
  226. user2 = insert(:user)
  227. content = "direct cofe @#{user2.nickname}"
  228. conn =
  229. conn
  230. |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
  231. assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
  232. assert activity = Activity.get_by_id(id)
  233. assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
  234. assert activity.data["to"] == [user2.ap_id]
  235. assert activity.data["cc"] == []
  236. end
  237. end
  238. describe "posting polls" do
  239. test "posting a poll", %{conn: conn} do
  240. user = insert(:user)
  241. time = NaiveDateTime.utc_now()
  242. conn =
  243. conn
  244. |> assign(:user, user)
  245. |> post("/api/v1/statuses", %{
  246. "status" => "Who is the #bestgrill?",
  247. "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
  248. })
  249. response = json_response(conn, 200)
  250. assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
  251. title in ["Rei", "Asuka", "Misato"]
  252. end)
  253. assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
  254. refute response["poll"]["expred"]
  255. end
  256. test "option limit is enforced", %{conn: conn} do
  257. user = insert(:user)
  258. limit = Config.get([:instance, :poll_limits, :max_options])
  259. conn =
  260. conn
  261. |> assign(:user, user)
  262. |> post("/api/v1/statuses", %{
  263. "status" => "desu~",
  264. "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
  265. })
  266. %{"error" => error} = json_response(conn, 422)
  267. assert error == "Poll can't contain more than #{limit} options"
  268. end
  269. test "option character limit is enforced", %{conn: conn} do
  270. user = insert(:user)
  271. limit = Config.get([:instance, :poll_limits, :max_option_chars])
  272. conn =
  273. conn
  274. |> assign(:user, user)
  275. |> post("/api/v1/statuses", %{
  276. "status" => "...",
  277. "poll" => %{
  278. "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
  279. "expires_in" => 1
  280. }
  281. })
  282. %{"error" => error} = json_response(conn, 422)
  283. assert error == "Poll options cannot be longer than #{limit} characters each"
  284. end
  285. test "minimal date limit is enforced", %{conn: conn} do
  286. user = insert(:user)
  287. limit = Config.get([:instance, :poll_limits, :min_expiration])
  288. conn =
  289. conn
  290. |> assign(:user, user)
  291. |> post("/api/v1/statuses", %{
  292. "status" => "imagine arbitrary limits",
  293. "poll" => %{
  294. "options" => ["this post was made by pleroma gang"],
  295. "expires_in" => limit - 1
  296. }
  297. })
  298. %{"error" => error} = json_response(conn, 422)
  299. assert error == "Expiration date is too soon"
  300. end
  301. test "maximum date limit is enforced", %{conn: conn} do
  302. user = insert(:user)
  303. limit = Config.get([:instance, :poll_limits, :max_expiration])
  304. conn =
  305. conn
  306. |> assign(:user, user)
  307. |> post("/api/v1/statuses", %{
  308. "status" => "imagine arbitrary limits",
  309. "poll" => %{
  310. "options" => ["this post was made by pleroma gang"],
  311. "expires_in" => limit + 1
  312. }
  313. })
  314. %{"error" => error} = json_response(conn, 422)
  315. assert error == "Expiration date is too far in the future"
  316. end
  317. end
  318. test "direct timeline", %{conn: conn} do
  319. user_one = insert(:user)
  320. user_two = insert(:user)
  321. {:ok, user_two} = User.follow(user_two, user_one)
  322. {:ok, direct} =
  323. CommonAPI.post(user_one, %{
  324. "status" => "Hi @#{user_two.nickname}!",
  325. "visibility" => "direct"
  326. })
  327. {:ok, _follower_only} =
  328. CommonAPI.post(user_one, %{
  329. "status" => "Hi @#{user_two.nickname}!",
  330. "visibility" => "private"
  331. })
  332. # Only direct should be visible here
  333. res_conn =
  334. conn
  335. |> assign(:user, user_two)
  336. |> get("api/v1/timelines/direct")
  337. [status] = json_response(res_conn, 200)
  338. assert %{"visibility" => "direct"} = status
  339. assert status["url"] != direct.data["id"]
  340. # User should be able to see their own direct message
  341. res_conn =
  342. build_conn()
  343. |> assign(:user, user_one)
  344. |> get("api/v1/timelines/direct")
  345. [status] = json_response(res_conn, 200)
  346. assert %{"visibility" => "direct"} = status
  347. # Both should be visible here
  348. res_conn =
  349. conn
  350. |> assign(:user, user_two)
  351. |> get("api/v1/timelines/home")
  352. [_s1, _s2] = json_response(res_conn, 200)
  353. # Test pagination
  354. Enum.each(1..20, fn _ ->
  355. {:ok, _} =
  356. CommonAPI.post(user_one, %{
  357. "status" => "Hi @#{user_two.nickname}!",
  358. "visibility" => "direct"
  359. })
  360. end)
  361. res_conn =
  362. conn
  363. |> assign(:user, user_two)
  364. |> get("api/v1/timelines/direct")
  365. statuses = json_response(res_conn, 200)
  366. assert length(statuses) == 20
  367. res_conn =
  368. conn
  369. |> assign(:user, user_two)
  370. |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
  371. [status] = json_response(res_conn, 200)
  372. assert status["url"] != direct.data["id"]
  373. end
  374. test "Conversations", %{conn: conn} do
  375. user_one = insert(:user)
  376. user_two = insert(:user)
  377. user_three = insert(:user)
  378. {:ok, user_two} = User.follow(user_two, user_one)
  379. {:ok, direct} =
  380. CommonAPI.post(user_one, %{
  381. "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
  382. "visibility" => "direct"
  383. })
  384. {:ok, _follower_only} =
  385. CommonAPI.post(user_one, %{
  386. "status" => "Hi @#{user_two.nickname}!",
  387. "visibility" => "private"
  388. })
  389. res_conn =
  390. conn
  391. |> assign(:user, user_one)
  392. |> get("/api/v1/conversations")
  393. assert response = json_response(res_conn, 200)
  394. assert [
  395. %{
  396. "id" => res_id,
  397. "accounts" => res_accounts,
  398. "last_status" => res_last_status,
  399. "unread" => unread
  400. }
  401. ] = response
  402. account_ids = Enum.map(res_accounts, & &1["id"])
  403. assert length(res_accounts) == 2
  404. assert user_two.id in account_ids
  405. assert user_three.id in account_ids
  406. assert is_binary(res_id)
  407. assert unread == true
  408. assert res_last_status["id"] == direct.id
  409. # Apparently undocumented API endpoint
  410. res_conn =
  411. conn
  412. |> assign(:user, user_one)
  413. |> post("/api/v1/conversations/#{res_id}/read")
  414. assert response = json_response(res_conn, 200)
  415. assert length(response["accounts"]) == 2
  416. assert response["last_status"]["id"] == direct.id
  417. assert response["unread"] == false
  418. # (vanilla) Mastodon frontend behaviour
  419. res_conn =
  420. conn
  421. |> assign(:user, user_one)
  422. |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
  423. assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
  424. end
  425. test "doesn't include DMs from blocked users", %{conn: conn} do
  426. blocker = insert(:user)
  427. blocked = insert(:user)
  428. user = insert(:user)
  429. {:ok, blocker} = User.block(blocker, blocked)
  430. {:ok, _blocked_direct} =
  431. CommonAPI.post(blocked, %{
  432. "status" => "Hi @#{blocker.nickname}!",
  433. "visibility" => "direct"
  434. })
  435. {:ok, direct} =
  436. CommonAPI.post(user, %{
  437. "status" => "Hi @#{blocker.nickname}!",
  438. "visibility" => "direct"
  439. })
  440. res_conn =
  441. conn
  442. |> assign(:user, user)
  443. |> get("api/v1/timelines/direct")
  444. [status] = json_response(res_conn, 200)
  445. assert status["id"] == direct.id
  446. end
  447. test "verify_credentials", %{conn: conn} do
  448. user = insert(:user)
  449. conn =
  450. conn
  451. |> assign(:user, user)
  452. |> get("/api/v1/accounts/verify_credentials")
  453. response = json_response(conn, 200)
  454. assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
  455. assert response["pleroma"]["chat_token"]
  456. assert id == to_string(user.id)
  457. end
  458. test "verify_credentials default scope unlisted", %{conn: conn} do
  459. user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
  460. conn =
  461. conn
  462. |> assign(:user, user)
  463. |> get("/api/v1/accounts/verify_credentials")
  464. assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
  465. assert id == to_string(user.id)
  466. end
  467. test "apps/verify_credentials", %{conn: conn} do
  468. token = insert(:oauth_token)
  469. conn =
  470. conn
  471. |> assign(:user, token.user)
  472. |> assign(:token, token)
  473. |> get("/api/v1/apps/verify_credentials")
  474. app = Repo.preload(token, :app).app
  475. expected = %{
  476. "name" => app.client_name,
  477. "website" => app.website,
  478. "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
  479. }
  480. assert expected == json_response(conn, 200)
  481. end
  482. test "user avatar can be set", %{conn: conn} do
  483. user = insert(:user)
  484. avatar_image = File.read!("test/fixtures/avatar_data_uri")
  485. conn =
  486. conn
  487. |> assign(:user, user)
  488. |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
  489. user = refresh_record(user)
  490. assert %{
  491. "name" => _,
  492. "type" => _,
  493. "url" => [
  494. %{
  495. "href" => _,
  496. "mediaType" => _,
  497. "type" => _
  498. }
  499. ]
  500. } = user.avatar
  501. assert %{"url" => _} = json_response(conn, 200)
  502. end
  503. test "user avatar can be reset", %{conn: conn} do
  504. user = insert(:user)
  505. conn =
  506. conn
  507. |> assign(:user, user)
  508. |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
  509. user = User.get_cached_by_id(user.id)
  510. assert user.avatar == nil
  511. assert %{"url" => nil} = json_response(conn, 200)
  512. end
  513. test "can set profile banner", %{conn: conn} do
  514. user = insert(:user)
  515. conn =
  516. conn
  517. |> assign(:user, user)
  518. |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
  519. user = refresh_record(user)
  520. assert user.info.banner["type"] == "Image"
  521. assert %{"url" => _} = json_response(conn, 200)
  522. end
  523. test "can reset profile banner", %{conn: conn} do
  524. user = insert(:user)
  525. conn =
  526. conn
  527. |> assign(:user, user)
  528. |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
  529. user = refresh_record(user)
  530. assert user.info.banner == %{}
  531. assert %{"url" => nil} = json_response(conn, 200)
  532. end
  533. test "background image can be set", %{conn: conn} do
  534. user = insert(:user)
  535. conn =
  536. conn
  537. |> assign(:user, user)
  538. |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
  539. user = refresh_record(user)
  540. assert user.info.background["type"] == "Image"
  541. assert %{"url" => _} = json_response(conn, 200)
  542. end
  543. test "background image can be reset", %{conn: conn} do
  544. user = insert(:user)
  545. conn =
  546. conn
  547. |> assign(:user, user)
  548. |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
  549. user = refresh_record(user)
  550. assert user.info.background == %{}
  551. assert %{"url" => nil} = json_response(conn, 200)
  552. end
  553. test "creates an oauth app", %{conn: conn} do
  554. user = insert(:user)
  555. app_attrs = build(:oauth_app)
  556. conn =
  557. conn
  558. |> assign(:user, user)
  559. |> post("/api/v1/apps", %{
  560. client_name: app_attrs.client_name,
  561. redirect_uris: app_attrs.redirect_uris
  562. })
  563. [app] = Repo.all(App)
  564. expected = %{
  565. "name" => app.client_name,
  566. "website" => app.website,
  567. "client_id" => app.client_id,
  568. "client_secret" => app.client_secret,
  569. "id" => app.id |> to_string(),
  570. "redirect_uri" => app.redirect_uris,
  571. "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
  572. }
  573. assert expected == json_response(conn, 200)
  574. end
  575. test "get a status", %{conn: conn} do
  576. activity = insert(:note_activity)
  577. conn =
  578. conn
  579. |> get("/api/v1/statuses/#{activity.id}")
  580. assert %{"id" => id} = json_response(conn, 200)
  581. assert id == to_string(activity.id)
  582. end
  583. test "get statuses by IDs", %{conn: conn} do
  584. %{id: id1} = insert(:note_activity)
  585. %{id: id2} = insert(:note_activity)
  586. query_string = "ids[]=#{id1}&ids[]=#{id2}"
  587. conn = get(conn, "/api/v1/statuses/?#{query_string}")
  588. assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
  589. end
  590. describe "deleting a status" do
  591. test "when you created it", %{conn: conn} do
  592. activity = insert(:note_activity)
  593. author = User.get_cached_by_ap_id(activity.data["actor"])
  594. conn =
  595. conn
  596. |> assign(:user, author)
  597. |> delete("/api/v1/statuses/#{activity.id}")
  598. assert %{} = json_response(conn, 200)
  599. refute Activity.get_by_id(activity.id)
  600. end
  601. test "when you didn't create it", %{conn: conn} do
  602. activity = insert(:note_activity)
  603. user = insert(:user)
  604. conn =
  605. conn
  606. |> assign(:user, user)
  607. |> delete("/api/v1/statuses/#{activity.id}")
  608. assert %{"error" => _} = json_response(conn, 403)
  609. assert Activity.get_by_id(activity.id) == activity
  610. end
  611. test "when you're an admin or moderator", %{conn: conn} do
  612. activity1 = insert(:note_activity)
  613. activity2 = insert(:note_activity)
  614. admin = insert(:user, info: %{is_admin: true})
  615. moderator = insert(:user, info: %{is_moderator: true})
  616. res_conn =
  617. conn
  618. |> assign(:user, admin)
  619. |> delete("/api/v1/statuses/#{activity1.id}")
  620. assert %{} = json_response(res_conn, 200)
  621. res_conn =
  622. conn
  623. |> assign(:user, moderator)
  624. |> delete("/api/v1/statuses/#{activity2.id}")
  625. assert %{} = json_response(res_conn, 200)
  626. refute Activity.get_by_id(activity1.id)
  627. refute Activity.get_by_id(activity2.id)
  628. end
  629. end
  630. describe "filters" do
  631. test "creating a filter", %{conn: conn} do
  632. user = insert(:user)
  633. filter = %Pleroma.Filter{
  634. phrase: "knights",
  635. context: ["home"]
  636. }
  637. conn =
  638. conn
  639. |> assign(:user, user)
  640. |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
  641. assert response = json_response(conn, 200)
  642. assert response["phrase"] == filter.phrase
  643. assert response["context"] == filter.context
  644. assert response["irreversible"] == false
  645. assert response["id"] != nil
  646. assert response["id"] != ""
  647. end
  648. test "fetching a list of filters", %{conn: conn} do
  649. user = insert(:user)
  650. query_one = %Pleroma.Filter{
  651. user_id: user.id,
  652. filter_id: 1,
  653. phrase: "knights",
  654. context: ["home"]
  655. }
  656. query_two = %Pleroma.Filter{
  657. user_id: user.id,
  658. filter_id: 2,
  659. phrase: "who",
  660. context: ["home"]
  661. }
  662. {:ok, filter_one} = Pleroma.Filter.create(query_one)
  663. {:ok, filter_two} = Pleroma.Filter.create(query_two)
  664. response =
  665. conn
  666. |> assign(:user, user)
  667. |> get("/api/v1/filters")
  668. |> json_response(200)
  669. assert response ==
  670. render_json(
  671. FilterView,
  672. "filters.json",
  673. filters: [filter_two, filter_one]
  674. )
  675. end
  676. test "get a filter", %{conn: conn} do
  677. user = insert(:user)
  678. query = %Pleroma.Filter{
  679. user_id: user.id,
  680. filter_id: 2,
  681. phrase: "knight",
  682. context: ["home"]
  683. }
  684. {:ok, filter} = Pleroma.Filter.create(query)
  685. conn =
  686. conn
  687. |> assign(:user, user)
  688. |> get("/api/v1/filters/#{filter.filter_id}")
  689. assert _response = json_response(conn, 200)
  690. end
  691. test "update a filter", %{conn: conn} do
  692. user = insert(:user)
  693. query = %Pleroma.Filter{
  694. user_id: user.id,
  695. filter_id: 2,
  696. phrase: "knight",
  697. context: ["home"]
  698. }
  699. {:ok, _filter} = Pleroma.Filter.create(query)
  700. new = %Pleroma.Filter{
  701. phrase: "nii",
  702. context: ["home"]
  703. }
  704. conn =
  705. conn
  706. |> assign(:user, user)
  707. |> put("/api/v1/filters/#{query.filter_id}", %{
  708. phrase: new.phrase,
  709. context: new.context
  710. })
  711. assert response = json_response(conn, 200)
  712. assert response["phrase"] == new.phrase
  713. assert response["context"] == new.context
  714. end
  715. test "delete a filter", %{conn: conn} do
  716. user = insert(:user)
  717. query = %Pleroma.Filter{
  718. user_id: user.id,
  719. filter_id: 2,
  720. phrase: "knight",
  721. context: ["home"]
  722. }
  723. {:ok, filter} = Pleroma.Filter.create(query)
  724. conn =
  725. conn
  726. |> assign(:user, user)
  727. |> delete("/api/v1/filters/#{filter.filter_id}")
  728. assert response = json_response(conn, 200)
  729. assert response == %{}
  730. end
  731. end
  732. describe "list timelines" do
  733. test "list timeline", %{conn: conn} do
  734. user = insert(:user)
  735. other_user = insert(:user)
  736. {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
  737. {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
  738. {:ok, list} = Pleroma.List.create("name", user)
  739. {:ok, list} = Pleroma.List.follow(list, other_user)
  740. conn =
  741. conn
  742. |> assign(:user, user)
  743. |> get("/api/v1/timelines/list/#{list.id}")
  744. assert [%{"id" => id}] = json_response(conn, 200)
  745. assert id == to_string(activity_two.id)
  746. end
  747. test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
  748. user = insert(:user)
  749. other_user = insert(:user)
  750. {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
  751. {:ok, _activity_two} =
  752. CommonAPI.post(other_user, %{
  753. "status" => "Marisa is cute.",
  754. "visibility" => "private"
  755. })
  756. {:ok, list} = Pleroma.List.create("name", user)
  757. {:ok, list} = Pleroma.List.follow(list, other_user)
  758. conn =
  759. conn
  760. |> assign(:user, user)
  761. |> get("/api/v1/timelines/list/#{list.id}")
  762. assert [%{"id" => id}] = json_response(conn, 200)
  763. assert id == to_string(activity_one.id)
  764. end
  765. end
  766. describe "notifications" do
  767. test "list of notifications", %{conn: conn} do
  768. user = insert(:user)
  769. other_user = insert(:user)
  770. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  771. {:ok, [_notification]} = Notification.create_notifications(activity)
  772. conn =
  773. conn
  774. |> assign(:user, user)
  775. |> get("/api/v1/notifications")
  776. expected_response =
  777. "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
  778. user.ap_id
  779. }\">@<span>#{user.nickname}</span></a></span>"
  780. assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
  781. assert response == expected_response
  782. end
  783. test "getting a single notification", %{conn: conn} do
  784. user = insert(:user)
  785. other_user = insert(:user)
  786. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  787. {:ok, [notification]} = Notification.create_notifications(activity)
  788. conn =
  789. conn
  790. |> assign(:user, user)
  791. |> get("/api/v1/notifications/#{notification.id}")
  792. expected_response =
  793. "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
  794. user.ap_id
  795. }\">@<span>#{user.nickname}</span></a></span>"
  796. assert %{"status" => %{"content" => response}} = json_response(conn, 200)
  797. assert response == expected_response
  798. end
  799. test "dismissing a single notification", %{conn: conn} do
  800. user = insert(:user)
  801. other_user = insert(:user)
  802. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  803. {:ok, [notification]} = Notification.create_notifications(activity)
  804. conn =
  805. conn
  806. |> assign(:user, user)
  807. |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
  808. assert %{} = json_response(conn, 200)
  809. end
  810. test "clearing all notifications", %{conn: conn} do
  811. user = insert(:user)
  812. other_user = insert(:user)
  813. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  814. {:ok, [_notification]} = Notification.create_notifications(activity)
  815. conn =
  816. conn
  817. |> assign(:user, user)
  818. |> post("/api/v1/notifications/clear")
  819. assert %{} = json_response(conn, 200)
  820. conn =
  821. build_conn()
  822. |> assign(:user, user)
  823. |> get("/api/v1/notifications")
  824. assert all = json_response(conn, 200)
  825. assert all == []
  826. end
  827. test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
  828. user = insert(:user)
  829. other_user = insert(:user)
  830. {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  831. {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  832. {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  833. {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  834. notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
  835. notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
  836. notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
  837. notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
  838. conn =
  839. conn
  840. |> assign(:user, user)
  841. # min_id
  842. conn_res =
  843. conn
  844. |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
  845. result = json_response(conn_res, 200)
  846. assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
  847. # since_id
  848. conn_res =
  849. conn
  850. |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
  851. result = json_response(conn_res, 200)
  852. assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
  853. # max_id
  854. conn_res =
  855. conn
  856. |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
  857. result = json_response(conn_res, 200)
  858. assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
  859. end
  860. test "filters notifications using exclude_types", %{conn: conn} do
  861. user = insert(:user)
  862. other_user = insert(:user)
  863. {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
  864. {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
  865. {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
  866. {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
  867. {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
  868. mention_notification_id =
  869. Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
  870. favorite_notification_id =
  871. Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
  872. reblog_notification_id =
  873. Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
  874. follow_notification_id =
  875. Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
  876. conn =
  877. conn
  878. |> assign(:user, user)
  879. conn_res =
  880. get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
  881. assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
  882. conn_res =
  883. get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
  884. assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
  885. conn_res =
  886. get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
  887. assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
  888. conn_res =
  889. get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
  890. assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
  891. end
  892. test "destroy multiple", %{conn: conn} do
  893. user = insert(:user)
  894. other_user = insert(:user)
  895. {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  896. {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
  897. {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
  898. {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
  899. notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
  900. notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
  901. notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
  902. notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
  903. conn =
  904. conn
  905. |> assign(:user, user)
  906. conn_res =
  907. conn
  908. |> get("/api/v1/notifications")
  909. result = json_response(conn_res, 200)
  910. assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
  911. conn2 =
  912. conn
  913. |> assign(:user, other_user)
  914. conn_res =
  915. conn2
  916. |> get("/api/v1/notifications")
  917. result = json_response(conn_res, 200)
  918. assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
  919. conn_destroy =
  920. conn
  921. |> delete("/api/v1/notifications/destroy_multiple", %{
  922. "ids" => [notification1_id, notification2_id]
  923. })
  924. assert json_response(conn_destroy, 200) == %{}
  925. conn_res =
  926. conn2
  927. |> get("/api/v1/notifications")
  928. result = json_response(conn_res, 200)
  929. assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
  930. end
  931. test "doesn't see notifications after muting user with notifications", %{conn: conn} do
  932. user = insert(:user)
  933. user2 = insert(:user)
  934. {:ok, _, _, _} = CommonAPI.follow(user, user2)
  935. {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
  936. conn = assign(conn, :user, user)
  937. conn = get(conn, "/api/v1/notifications")
  938. assert length(json_response(conn, 200)) == 1
  939. {:ok, user} = User.mute(user, user2)
  940. conn = assign(build_conn(), :user, user)
  941. conn = get(conn, "/api/v1/notifications")
  942. assert json_response(conn, 200) == []
  943. end
  944. test "see notifications after muting user without notifications", %{conn: conn} do
  945. user = insert(:user)
  946. user2 = insert(:user)
  947. {:ok, _, _, _} = CommonAPI.follow(user, user2)
  948. {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
  949. conn = assign(conn, :user, user)
  950. conn = get(conn, "/api/v1/notifications")
  951. assert length(json_response(conn, 200)) == 1
  952. {:ok, user} = User.mute(user, user2, false)
  953. conn = assign(build_conn(), :user, user)
  954. conn = get(conn, "/api/v1/notifications")
  955. assert length(json_response(conn, 200)) == 1
  956. end
  957. test "see notifications after muting user with notifications and with_muted parameter", %{
  958. conn: conn
  959. } do
  960. user = insert(:user)
  961. user2 = insert(:user)
  962. {:ok, _, _, _} = CommonAPI.follow(user, user2)
  963. {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
  964. conn = assign(conn, :user, user)
  965. conn = get(conn, "/api/v1/notifications")
  966. assert length(json_response(conn, 200)) == 1
  967. {:ok, user} = User.mute(user, user2)
  968. conn = assign(build_conn(), :user, user)
  969. conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
  970. assert length(json_response(conn, 200)) == 1
  971. end
  972. end
  973. describe "reblogging" do
  974. test "reblogs and returns the reblogged status", %{conn: conn} do
  975. activity = insert(:note_activity)
  976. user = insert(:user)
  977. conn =
  978. conn
  979. |> assign(:user, user)
  980. |> post("/api/v1/statuses/#{activity.id}/reblog")
  981. assert %{
  982. "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
  983. "reblogged" => true
  984. } = json_response(conn, 200)
  985. assert to_string(activity.id) == id
  986. end
  987. test "reblogged status for another user", %{conn: conn} do
  988. activity = insert(:note_activity)
  989. user1 = insert(:user)
  990. user2 = insert(:user)
  991. user3 = insert(:user)
  992. CommonAPI.favorite(activity.id, user2)
  993. {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
  994. {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
  995. {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
  996. conn_res =
  997. conn
  998. |> assign(:user, user3)
  999. |> get("/api/v1/statuses/#{reblog_activity1.id}")
  1000. assert %{
  1001. "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
  1002. "reblogged" => false,
  1003. "favourited" => false,
  1004. "bookmarked" => false
  1005. } = json_response(conn_res, 200)
  1006. conn_res =
  1007. conn
  1008. |> assign(:user, user2)
  1009. |> get("/api/v1/statuses/#{reblog_activity1.id}")
  1010. assert %{
  1011. "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
  1012. "reblogged" => true,
  1013. "favourited" => true,
  1014. "bookmarked" => true
  1015. } = json_response(conn_res, 200)
  1016. assert to_string(activity.id) == id
  1017. end
  1018. test "returns 400 error when activity is not exist", %{conn: conn} do
  1019. user = insert(:user)
  1020. conn =
  1021. conn
  1022. |> assign(:user, user)
  1023. |> post("/api/v1/statuses/foo/reblog")
  1024. assert json_response(conn, 400) == %{"error" => "Could not repeat"}
  1025. end
  1026. end
  1027. describe "unreblogging" do
  1028. test "unreblogs and returns the unreblogged status", %{conn: conn} do
  1029. activity = insert(:note_activity)
  1030. user = insert(:user)
  1031. {:ok, _, _} = CommonAPI.repeat(activity.id, user)
  1032. conn =
  1033. conn
  1034. |> assign(:user, user)
  1035. |> post("/api/v1/statuses/#{activity.id}/unreblog")
  1036. assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
  1037. assert to_string(activity.id) == id
  1038. end
  1039. test "returns 400 error when activity is not exist", %{conn: conn} do
  1040. user = insert(:user)
  1041. conn =
  1042. conn
  1043. |> assign(:user, user)
  1044. |> post("/api/v1/statuses/foo/unreblog")
  1045. assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
  1046. end
  1047. end
  1048. describe "favoriting" do
  1049. test "favs a status and returns it", %{conn: conn} do
  1050. activity = insert(:note_activity)
  1051. user = insert(:user)
  1052. conn =
  1053. conn
  1054. |> assign(:user, user)
  1055. |> post("/api/v1/statuses/#{activity.id}/favourite")
  1056. assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
  1057. json_response(conn, 200)
  1058. assert to_string(activity.id) == id
  1059. end
  1060. test "returns 400 error for a wrong id", %{conn: conn} do
  1061. user = insert(:user)
  1062. conn =
  1063. conn
  1064. |> assign(:user, user)
  1065. |> post("/api/v1/statuses/1/favourite")
  1066. assert json_response(conn, 400) == %{"error" => "Could not favorite"}
  1067. end
  1068. end
  1069. describe "unfavoriting" do
  1070. test "unfavorites a status and returns it", %{conn: conn} do
  1071. activity = insert(:note_activity)
  1072. user = insert(:user)
  1073. {:ok, _, _} = CommonAPI.favorite(activity.id, user)
  1074. conn =
  1075. conn
  1076. |> assign(:user, user)
  1077. |> post("/api/v1/statuses/#{activity.id}/unfavourite")
  1078. assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
  1079. json_response(conn, 200)
  1080. assert to_string(activity.id) == id
  1081. end
  1082. test "returns 400 error for a wrong id", %{conn: conn} do
  1083. user = insert(:user)
  1084. conn =
  1085. conn
  1086. |> assign(:user, user)
  1087. |> post("/api/v1/statuses/1/unfavourite")
  1088. assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
  1089. end
  1090. end
  1091. describe "user timelines" do
  1092. test "gets a users statuses", %{conn: conn} do
  1093. user_one = insert(:user)
  1094. user_two = insert(:user)
  1095. user_three = insert(:user)
  1096. {:ok, user_three} = User.follow(user_three, user_one)
  1097. {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
  1098. {:ok, direct_activity} =
  1099. CommonAPI.post(user_one, %{
  1100. "status" => "Hi, @#{user_two.nickname}.",
  1101. "visibility" => "direct"
  1102. })
  1103. {:ok, private_activity} =
  1104. CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
  1105. resp =
  1106. conn
  1107. |> get("/api/v1/accounts/#{user_one.id}/statuses")
  1108. assert [%{"id" => id}] = json_response(resp, 200)
  1109. assert id == to_string(activity.id)
  1110. resp =
  1111. conn
  1112. |> assign(:user, user_two)
  1113. |> get("/api/v1/accounts/#{user_one.id}/statuses")
  1114. assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
  1115. assert id_one == to_string(direct_activity.id)
  1116. assert id_two == to_string(activity.id)
  1117. resp =
  1118. conn
  1119. |> assign(:user, user_three)
  1120. |> get("/api/v1/accounts/#{user_one.id}/statuses")
  1121. assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
  1122. assert id_one == to_string(private_activity.id)
  1123. assert id_two == to_string(activity.id)
  1124. end
  1125. test "unimplemented pinned statuses feature", %{conn: conn} do
  1126. note = insert(:note_activity)
  1127. user = User.get_cached_by_ap_id(note.data["actor"])
  1128. conn =
  1129. conn
  1130. |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
  1131. assert json_response(conn, 200) == []
  1132. end
  1133. test "gets an users media", %{conn: conn} do
  1134. note = insert(:note_activity)
  1135. user = User.get_cached_by_ap_id(note.data["actor"])
  1136. file = %Plug.Upload{
  1137. content_type: "image/jpg",
  1138. path: Path.absname("test/fixtures/image.jpg"),
  1139. filename: "an_image.jpg"
  1140. }
  1141. {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
  1142. {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
  1143. conn =
  1144. conn
  1145. |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
  1146. assert [%{"id" => id}] = json_response(conn, 200)
  1147. assert id == to_string(image_post.id)
  1148. conn =
  1149. build_conn()
  1150. |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
  1151. assert [%{"id" => id}] = json_response(conn, 200)
  1152. assert id == to_string(image_post.id)
  1153. end
  1154. test "gets a user's statuses without reblogs", %{conn: conn} do
  1155. user = insert(:user)
  1156. {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
  1157. {:ok, _, _} = CommonAPI.repeat(post.id, user)
  1158. conn =
  1159. conn
  1160. |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
  1161. assert [%{"id" => id}] = json_response(conn, 200)
  1162. assert id == to_string(post.id)
  1163. conn =
  1164. conn
  1165. |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
  1166. assert [%{"id" => id}] = json_response(conn, 200)
  1167. assert id == to_string(post.id)
  1168. end
  1169. test "filters user's statuses by a hashtag", %{conn: conn} do
  1170. user = insert(:user)
  1171. {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
  1172. {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
  1173. conn =
  1174. conn
  1175. |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
  1176. assert [%{"id" => id}] = json_response(conn, 200)
  1177. assert id == to_string(post.id)
  1178. end
  1179. end
  1180. describe "user relationships" do
  1181. test "returns the relationships for the current user", %{conn: conn} do
  1182. user = insert(:user)
  1183. other_user = insert(:user)
  1184. {:ok, user} = User.follow(user, other_user)
  1185. conn =
  1186. conn
  1187. |> assign(:user, user)
  1188. |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
  1189. assert [relationship] = json_response(conn, 200)
  1190. assert to_string(other_user.id) == relationship["id"]
  1191. end
  1192. end
  1193. describe "media upload" do
  1194. setup do
  1195. user = insert(:user)
  1196. conn =
  1197. build_conn()
  1198. |> assign(:user, user)
  1199. image = %Plug.Upload{
  1200. content_type: "image/jpg",
  1201. path: Path.absname("test/fixtures/image.jpg"),
  1202. filename: "an_image.jpg"
  1203. }
  1204. [conn: conn, image: image]
  1205. end
  1206. clear_config([:media_proxy])
  1207. clear_config([Pleroma.Upload])
  1208. test "returns uploaded image", %{conn: conn, image: image} do
  1209. desc = "Description of the image"
  1210. media =
  1211. conn
  1212. |> post("/api/v1/media", %{"file" => image, "description" => desc})
  1213. |> json_response(:ok)
  1214. assert media["type"] == "image"
  1215. assert media["description"] == desc
  1216. assert media["id"]
  1217. object = Repo.get(Object, media["id"])
  1218. assert object.data["actor"] == User.ap_id(conn.assigns[:user])
  1219. end
  1220. end
  1221. describe "locked accounts" do
  1222. test "/api/v1/follow_requests works" do
  1223. user = insert(:user, %{info: %User.Info{locked: true}})
  1224. other_user = insert(:user)
  1225. {:ok, _activity} = ActivityPub.follow(other_user, user)
  1226. user = User.get_cached_by_id(user.id)
  1227. other_user = User.get_cached_by_id(other_user.id)
  1228. assert User.following?(other_user, user) == false
  1229. conn =
  1230. build_conn()
  1231. |> assign(:user, user)
  1232. |> get("/api/v1/follow_requests")
  1233. assert [relationship] = json_response(conn, 200)
  1234. assert to_string(other_user.id) == relationship["id"]
  1235. end
  1236. test "/api/v1/follow_requests/:id/authorize works" do
  1237. user = insert(:user, %{info: %User.Info{locked: true}})
  1238. other_user = insert(:user)
  1239. {:ok, _activity} = ActivityPub.follow(other_user, user)
  1240. user = User.get_cached_by_id(user.id)
  1241. other_user = User.get_cached_by_id(other_user.id)
  1242. assert User.following?(other_user, user) == false
  1243. conn =
  1244. build_conn()
  1245. |> assign(:user, user)
  1246. |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
  1247. assert relationship = json_response(conn, 200)
  1248. assert to_string(other_user.id) == relationship["id"]
  1249. user = User.get_cached_by_id(user.id)
  1250. other_user = User.get_cached_by_id(other_user.id)
  1251. assert User.following?(other_user, user) == true
  1252. end
  1253. test "verify_credentials", %{conn: conn} do
  1254. user = insert(:user, %{info: %User.Info{default_scope: "private"}})
  1255. conn =
  1256. conn
  1257. |> assign(:user, user)
  1258. |> get("/api/v1/accounts/verify_credentials")
  1259. assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
  1260. assert id == to_string(user.id)
  1261. end
  1262. test "/api/v1/follow_requests/:id/reject works" do
  1263. user = insert(:user, %{info: %User.Info{locked: true}})
  1264. other_user = insert(:user)
  1265. {:ok, _activity} = ActivityPub.follow(other_user, user)
  1266. user = User.get_cached_by_id(user.id)
  1267. conn =
  1268. build_conn()
  1269. |> assign(:user, user)
  1270. |> post("/api/v1/follow_requests/#{other_user.id}/reject")
  1271. assert relationship = json_response(conn, 200)
  1272. assert to_string(other_user.id) == relationship["id"]
  1273. user = User.get_cached_by_id(user.id)
  1274. other_user = User.get_cached_by_id(other_user.id)
  1275. assert User.following?(other_user, user) == false
  1276. end
  1277. end
  1278. describe "account fetching" do
  1279. test "works by id" do
  1280. user = insert(:user)
  1281. conn =
  1282. build_conn()
  1283. |> get("/api/v1/accounts/#{user.id}")
  1284. assert %{"id" => id} = json_response(conn, 200)
  1285. assert id == to_string(user.id)
  1286. conn =
  1287. build_conn()
  1288. |> get("/api/v1/accounts/-1")
  1289. assert %{"error" => "Can't find user"} = json_response(conn, 404)
  1290. end
  1291. test "works by nickname" do
  1292. user = insert(:user)
  1293. conn =
  1294. build_conn()
  1295. |> get("/api/v1/accounts/#{user.nickname}")
  1296. assert %{"id" => id} = json_response(conn, 200)
  1297. assert id == user.id
  1298. end
  1299. test "works by nickname for remote users" do
  1300. limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
  1301. Pleroma.Config.put([:instance, :limit_to_local_content], false)
  1302. user = insert(:user, nickname: "user@example.com", local: false)
  1303. conn =
  1304. build_conn()
  1305. |> get("/api/v1/accounts/#{user.nickname}")
  1306. Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
  1307. assert %{"id" => id} = json_response(conn, 200)
  1308. assert id == user.id
  1309. end
  1310. test "respects limit_to_local_content == :all for remote user nicknames" do
  1311. limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
  1312. Pleroma.Config.put([:instance, :limit_to_local_content], :all)
  1313. user = insert(:user, nickname: "user@example.com", local: false)
  1314. conn =
  1315. build_conn()
  1316. |> get("/api/v1/accounts/#{user.nickname}")
  1317. Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
  1318. assert json_response(conn, 404)
  1319. end
  1320. test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
  1321. limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
  1322. Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
  1323. user = insert(:user, nickname: "user@example.com", local: false)
  1324. reading_user = insert(:user)
  1325. conn =
  1326. build_conn()
  1327. |> get("/api/v1/accounts/#{user.nickname}")
  1328. assert json_response(conn, 404)
  1329. conn =
  1330. build_conn()
  1331. |> assign(:user, reading_user)
  1332. |> get("/api/v1/accounts/#{user.nickname}")
  1333. Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
  1334. assert %{"id" => id} = json_response(conn, 200)
  1335. assert id == user.id
  1336. end
  1337. end
  1338. test "mascot upload", %{conn: conn} do
  1339. user = insert(:user)
  1340. non_image_file = %Plug.Upload{
  1341. content_type: "audio/mpeg",
  1342. path: Path.absname("test/fixtures/sound.mp3"),
  1343. filename: "sound.mp3"
  1344. }
  1345. conn =
  1346. conn
  1347. |> assign(:user, user)
  1348. |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
  1349. assert json_response(conn, 415)
  1350. file = %Plug.Upload{
  1351. content_type: "image/jpg",
  1352. path: Path.absname("test/fixtures/image.jpg"),
  1353. filename: "an_image.jpg"
  1354. }
  1355. conn =
  1356. build_conn()
  1357. |> assign(:user, user)
  1358. |> put("/api/v1/pleroma/mascot", %{"file" => file})
  1359. assert %{"id" => _, "type" => image} = json_response(conn, 200)
  1360. end
  1361. test "mascot retrieving", %{conn: conn} do
  1362. user = insert(:user)
  1363. # When user hasn't set a mascot, we should just get pleroma tan back
  1364. conn =
  1365. conn
  1366. |> assign(:user, user)
  1367. |> get("/api/v1/pleroma/mascot")
  1368. assert %{"url" => url} = json_response(conn, 200)
  1369. assert url =~ "pleroma-fox-tan-smol"
  1370. # When a user sets their mascot, we should get that back
  1371. file = %Plug.Upload{
  1372. content_type: "image/jpg",
  1373. path: Path.absname("test/fixtures/image.jpg"),
  1374. filename: "an_image.jpg"
  1375. }
  1376. conn =
  1377. build_conn()
  1378. |> assign(:user, user)
  1379. |> put("/api/v1/pleroma/mascot", %{"file" => file})
  1380. assert json_response(conn, 200)
  1381. user = User.get_cached_by_id(user.id)
  1382. conn =
  1383. build_conn()
  1384. |> assign(:user, user)
  1385. |> get("/api/v1/pleroma/mascot")
  1386. assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
  1387. assert url =~ "an_image"
  1388. end
  1389. test "hashtag timeline", %{conn: conn} do
  1390. following = insert(:user)
  1391. capture_log(fn ->
  1392. {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
  1393. {:ok, [_activity]} =
  1394. OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
  1395. nconn =
  1396. conn
  1397. |> get("/api/v1/timelines/tag/2hu")
  1398. assert [%{"id" => id}] = json_response(nconn, 200)
  1399. assert id == to_string(activity.id)
  1400. # works for different capitalization too
  1401. nconn =
  1402. conn
  1403. |> get("/api/v1/timelines/tag/2HU")
  1404. assert [%{"id" => id}] = json_response(nconn, 200)
  1405. assert id == to_string(activity.id)
  1406. end)
  1407. end
  1408. test "multi-hashtag timeline", %{conn: conn} do
  1409. user = insert(:user)
  1410. {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
  1411. {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
  1412. {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
  1413. any_test =
  1414. conn
  1415. |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
  1416. [status_none, status_test1, status_test] = json_response(any_test, 200)
  1417. assert to_string(activity_test.id) == status_test["id"]
  1418. assert to_string(activity_test1.id) == status_test1["id"]
  1419. assert to_string(activity_none.id) == status_none["id"]
  1420. restricted_test =
  1421. conn
  1422. |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
  1423. assert [status_test1] == json_response(restricted_test, 200)
  1424. all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
  1425. assert [status_none] == json_response(all_test, 200)
  1426. end
  1427. test "getting followers", %{conn: conn} do
  1428. user = insert(:user)
  1429. other_user = insert(:user)
  1430. {:ok, user} = User.follow(user, other_user)
  1431. conn =
  1432. conn
  1433. |> get("/api/v1/accounts/#{other_user.id}/followers")
  1434. assert [%{"id" => id}] = json_response(conn, 200)
  1435. assert id == to_string(user.id)
  1436. end
  1437. test "getting followers, hide_followers", %{conn: conn} do
  1438. user = insert(:user)
  1439. other_user = insert(:user, %{info: %{hide_followers: true}})
  1440. {:ok, _user} = User.follow(user, other_user)
  1441. conn =
  1442. conn
  1443. |> get("/api/v1/accounts/#{other_user.id}/followers")
  1444. assert [] == json_response(conn, 200)
  1445. end
  1446. test "getting followers, hide_followers, same user requesting", %{conn: conn} do
  1447. user = insert(:user)
  1448. other_user = insert(:user, %{info: %{hide_followers: true}})
  1449. {:ok, _user} = User.follow(user, other_user)
  1450. conn =
  1451. conn
  1452. |> assign(:user, other_user)
  1453. |> get("/api/v1/accounts/#{other_user.id}/followers")
  1454. refute [] == json_response(conn, 200)
  1455. end
  1456. test "getting followers, pagination", %{conn: conn} do
  1457. user = insert(:user)
  1458. follower1 = insert(:user)
  1459. follower2 = insert(:user)
  1460. follower3 = insert(:user)
  1461. {:ok, _} = User.follow(follower1, user)
  1462. {:ok, _} = User.follow(follower2, user)
  1463. {:ok, _} = User.follow(follower3, user)
  1464. conn =
  1465. conn
  1466. |> assign(:user, user)
  1467. res_conn =
  1468. conn
  1469. |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
  1470. assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
  1471. assert id3 == follower3.id
  1472. assert id2 == follower2.id
  1473. res_conn =
  1474. conn
  1475. |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
  1476. assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
  1477. assert id2 == follower2.id
  1478. assert id1 == follower1.id
  1479. res_conn =
  1480. conn
  1481. |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
  1482. assert [%{"id" => id2}] = json_response(res_conn, 200)
  1483. assert id2 == follower2.id
  1484. assert [link_header] = get_resp_header(res_conn, "link")
  1485. assert link_header =~ ~r/min_id=#{follower2.id}/
  1486. assert link_header =~ ~r/max_id=#{follower2.id}/
  1487. end
  1488. test "getting following", %{conn: conn} do
  1489. user = insert(:user)
  1490. other_user = insert(:user)
  1491. {:ok, user} = User.follow(user, other_user)
  1492. conn =
  1493. conn
  1494. |> get("/api/v1/accounts/#{user.id}/following")
  1495. assert [%{"id" => id}] = json_response(conn, 200)
  1496. assert id == to_string(other_user.id)
  1497. end
  1498. test "getting following, hide_follows", %{conn: conn} do
  1499. user = insert(:user, %{info: %{hide_follows: true}})
  1500. other_user = insert(:user)
  1501. {:ok, user} = User.follow(user, other_user)
  1502. conn =
  1503. conn
  1504. |> get("/api/v1/accounts/#{user.id}/following")
  1505. assert [] == json_response(conn, 200)
  1506. end
  1507. test "getting following, hide_follows, same user requesting", %{conn: conn} do
  1508. user = insert(:user, %{info: %{hide_follows: true}})
  1509. other_user = insert(:user)
  1510. {:ok, user} = User.follow(user, other_user)
  1511. conn =
  1512. conn
  1513. |> assign(:user, user)
  1514. |> get("/api/v1/accounts/#{user.id}/following")
  1515. refute [] == json_response(conn, 200)
  1516. end
  1517. test "getting following, pagination", %{conn: conn} do
  1518. user = insert(:user)
  1519. following1 = insert(:user)
  1520. following2 = insert(:user)
  1521. following3 = insert(:user)
  1522. {:ok, _} = User.follow(user, following1)
  1523. {:ok, _} = User.follow(user, following2)
  1524. {:ok, _} = User.follow(user, following3)
  1525. conn =
  1526. conn
  1527. |> assign(:user, user)
  1528. res_conn =
  1529. conn
  1530. |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
  1531. assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
  1532. assert id3 == following3.id
  1533. assert id2 == following2.id
  1534. res_conn =
  1535. conn
  1536. |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
  1537. assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
  1538. assert id2 == following2.id
  1539. assert id1 == following1.id
  1540. res_conn =
  1541. conn
  1542. |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
  1543. assert [%{"id" => id2}] = json_response(res_conn, 200)
  1544. assert id2 == following2.id
  1545. assert [link_header] = get_resp_header(res_conn, "link")
  1546. assert link_header =~ ~r/min_id=#{following2.id}/
  1547. assert link_header =~ ~r/max_id=#{following2.id}/
  1548. end
  1549. test "following / unfollowing a user", %{conn: conn} do
  1550. user = insert(:user)
  1551. other_user = insert(:user)
  1552. conn =
  1553. conn
  1554. |> assign(:user, user)
  1555. |> post("/api/v1/accounts/#{other_user.id}/follow")
  1556. assert %{"id" => _id, "following" => true} = json_response(conn, 200)
  1557. user = User.get_cached_by_id(user.id)
  1558. conn =
  1559. build_conn()
  1560. |> assign(:user, user)
  1561. |> post("/api/v1/accounts/#{other_user.id}/unfollow")
  1562. assert %{"id" => _id, "following" => false} = json_response(conn, 200)
  1563. user = User.get_cached_by_id(user.id)
  1564. conn =
  1565. build_conn()
  1566. |> assign(:user, user)
  1567. |> post("/api/v1/follows", %{"uri" => other_user.nickname})
  1568. assert %{"id" => id} = json_response(conn, 200)
  1569. assert id == to_string(other_user.id)
  1570. end
  1571. test "following without reblogs" do
  1572. follower = insert(:user)
  1573. followed = insert(:user)
  1574. other_user = insert(:user)
  1575. conn =
  1576. build_conn()
  1577. |> assign(:user, follower)
  1578. |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
  1579. assert %{"showing_reblogs" => false} = json_response(conn, 200)
  1580. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
  1581. {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
  1582. conn =
  1583. build_conn()
  1584. |> assign(:user, User.get_cached_by_id(follower.id))
  1585. |> get("/api/v1/timelines/home")
  1586. assert [] == json_response(conn, 200)
  1587. conn =
  1588. build_conn()
  1589. |> assign(:user, follower)
  1590. |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
  1591. assert %{"showing_reblogs" => true} = json_response(conn, 200)
  1592. conn =
  1593. build_conn()
  1594. |> assign(:user, User.get_cached_by_id(follower.id))
  1595. |> get("/api/v1/timelines/home")
  1596. expected_activity_id = reblog.id
  1597. assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
  1598. end
  1599. test "following / unfollowing errors" do
  1600. user = insert(:user)
  1601. conn =
  1602. build_conn()
  1603. |> assign(:user, user)
  1604. # self follow
  1605. conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
  1606. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1607. # self unfollow
  1608. user = User.get_cached_by_id(user.id)
  1609. conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
  1610. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1611. # self follow via uri
  1612. user = User.get_cached_by_id(user.id)
  1613. conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
  1614. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1615. # follow non existing user
  1616. conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
  1617. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1618. # follow non existing user via uri
  1619. conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
  1620. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1621. # unfollow non existing user
  1622. conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
  1623. assert %{"error" => "Record not found"} = json_response(conn_res, 404)
  1624. end
  1625. describe "mute/unmute" do
  1626. test "with notifications", %{conn: conn} do
  1627. user = insert(:user)
  1628. other_user = insert(:user)
  1629. conn =
  1630. conn
  1631. |> assign(:user, user)
  1632. |> post("/api/v1/accounts/#{other_user.id}/mute")
  1633. response = json_response(conn, 200)
  1634. assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
  1635. user = User.get_cached_by_id(user.id)
  1636. conn =
  1637. build_conn()
  1638. |> assign(:user, user)
  1639. |> post("/api/v1/accounts/#{other_user.id}/unmute")
  1640. response = json_response(conn, 200)
  1641. assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
  1642. end
  1643. test "without notifications", %{conn: conn} do
  1644. user = insert(:user)
  1645. other_user = insert(:user)
  1646. conn =
  1647. conn
  1648. |> assign(:user, user)
  1649. |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
  1650. response = json_response(conn, 200)
  1651. assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
  1652. user = User.get_cached_by_id(user.id)
  1653. conn =
  1654. build_conn()
  1655. |> assign(:user, user)
  1656. |> post("/api/v1/accounts/#{other_user.id}/unmute")
  1657. response = json_response(conn, 200)
  1658. assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
  1659. end
  1660. end
  1661. test "subscribing / unsubscribing to a user", %{conn: conn} do
  1662. user = insert(:user)
  1663. subscription_target = insert(:user)
  1664. conn =
  1665. conn
  1666. |> assign(:user, user)
  1667. |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
  1668. assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
  1669. conn =
  1670. build_conn()
  1671. |> assign(:user, user)
  1672. |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
  1673. assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
  1674. end
  1675. test "getting a list of mutes", %{conn: conn} do
  1676. user = insert(:user)
  1677. other_user = insert(:user)
  1678. {:ok, user} = User.mute(user, other_user)
  1679. conn =
  1680. conn
  1681. |> assign(:user, user)
  1682. |> get("/api/v1/mutes")
  1683. other_user_id = to_string(other_user.id)
  1684. assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
  1685. end
  1686. test "blocking / unblocking a user", %{conn: conn} do
  1687. user = insert(:user)
  1688. other_user = insert(:user)
  1689. conn =
  1690. conn
  1691. |> assign(:user, user)
  1692. |> post("/api/v1/accounts/#{other_user.id}/block")
  1693. assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
  1694. user = User.get_cached_by_id(user.id)
  1695. conn =
  1696. build_conn()
  1697. |> assign(:user, user)
  1698. |> post("/api/v1/accounts/#{other_user.id}/unblock")
  1699. assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
  1700. end
  1701. test "getting a list of blocks", %{conn: conn} do
  1702. user = insert(:user)
  1703. other_user = insert(:user)
  1704. {:ok, user} = User.block(user, other_user)
  1705. conn =
  1706. conn
  1707. |> assign(:user, user)
  1708. |> get("/api/v1/blocks")
  1709. other_user_id = to_string(other_user.id)
  1710. assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
  1711. end
  1712. test "blocking / unblocking a domain", %{conn: conn} do
  1713. user = insert(:user)
  1714. other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
  1715. conn =
  1716. conn
  1717. |> assign(:user, user)
  1718. |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
  1719. assert %{} = json_response(conn, 200)
  1720. user = User.get_cached_by_ap_id(user.ap_id)
  1721. assert User.blocks?(user, other_user)
  1722. conn =
  1723. build_conn()
  1724. |> assign(:user, user)
  1725. |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
  1726. assert %{} = json_response(conn, 200)
  1727. user = User.get_cached_by_ap_id(user.ap_id)
  1728. refute User.blocks?(user, other_user)
  1729. end
  1730. test "getting a list of domain blocks", %{conn: conn} do
  1731. user = insert(:user)
  1732. {:ok, user} = User.block_domain(user, "bad.site")
  1733. {:ok, user} = User.block_domain(user, "even.worse.site")
  1734. conn =
  1735. conn
  1736. |> assign(:user, user)
  1737. |> get("/api/v1/domain_blocks")
  1738. domain_blocks = json_response(conn, 200)
  1739. assert "bad.site" in domain_blocks
  1740. assert "even.worse.site" in domain_blocks
  1741. end
  1742. test "unimplemented follow_requests, blocks, domain blocks" do
  1743. user = insert(:user)
  1744. ["blocks", "domain_blocks", "follow_requests"]
  1745. |> Enum.each(fn endpoint ->
  1746. conn =
  1747. build_conn()
  1748. |> assign(:user, user)
  1749. |> get("/api/v1/#{endpoint}")
  1750. assert [] = json_response(conn, 200)
  1751. end)
  1752. end
  1753. test "returns the favorites of a user", %{conn: conn} do
  1754. user = insert(:user)
  1755. other_user = insert(:user)
  1756. {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
  1757. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
  1758. {:ok, _, _} = CommonAPI.favorite(activity.id, user)
  1759. first_conn =
  1760. conn
  1761. |> assign(:user, user)
  1762. |> get("/api/v1/favourites")
  1763. assert [status] = json_response(first_conn, 200)
  1764. assert status["id"] == to_string(activity.id)
  1765. assert [{"link", _link_header}] =
  1766. Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
  1767. # Honours query params
  1768. {:ok, second_activity} =
  1769. CommonAPI.post(other_user, %{
  1770. "status" =>
  1771. "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
  1772. })
  1773. {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
  1774. last_like = status["id"]
  1775. second_conn =
  1776. conn
  1777. |> assign(:user, user)
  1778. |> get("/api/v1/favourites?since_id=#{last_like}")
  1779. assert [second_status] = json_response(second_conn, 200)
  1780. assert second_status["id"] == to_string(second_activity.id)
  1781. third_conn =
  1782. conn
  1783. |> assign(:user, user)
  1784. |> get("/api/v1/favourites?limit=0")
  1785. assert [] = json_response(third_conn, 200)
  1786. end
  1787. describe "getting favorites timeline of specified user" do
  1788. setup do
  1789. [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
  1790. [current_user: current_user, user: user]
  1791. end
  1792. test "returns list of statuses favorited by specified user", %{
  1793. conn: conn,
  1794. current_user: current_user,
  1795. user: user
  1796. } do
  1797. [activity | _] = insert_pair(:note_activity)
  1798. CommonAPI.favorite(activity.id, user)
  1799. response =
  1800. conn
  1801. |> assign(:user, current_user)
  1802. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1803. |> json_response(:ok)
  1804. [like] = response
  1805. assert length(response) == 1
  1806. assert like["id"] == activity.id
  1807. end
  1808. test "returns favorites for specified user_id when user is not logged in", %{
  1809. conn: conn,
  1810. user: user
  1811. } do
  1812. activity = insert(:note_activity)
  1813. CommonAPI.favorite(activity.id, user)
  1814. response =
  1815. conn
  1816. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1817. |> json_response(:ok)
  1818. assert length(response) == 1
  1819. end
  1820. test "returns favorited DM only when user is logged in and he is one of recipients", %{
  1821. conn: conn,
  1822. current_user: current_user,
  1823. user: user
  1824. } do
  1825. {:ok, direct} =
  1826. CommonAPI.post(current_user, %{
  1827. "status" => "Hi @#{user.nickname}!",
  1828. "visibility" => "direct"
  1829. })
  1830. CommonAPI.favorite(direct.id, user)
  1831. response =
  1832. conn
  1833. |> assign(:user, current_user)
  1834. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1835. |> json_response(:ok)
  1836. assert length(response) == 1
  1837. anonymous_response =
  1838. conn
  1839. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1840. |> json_response(:ok)
  1841. assert Enum.empty?(anonymous_response)
  1842. end
  1843. test "does not return others' favorited DM when user is not one of recipients", %{
  1844. conn: conn,
  1845. current_user: current_user,
  1846. user: user
  1847. } do
  1848. user_two = insert(:user)
  1849. {:ok, direct} =
  1850. CommonAPI.post(user_two, %{
  1851. "status" => "Hi @#{user.nickname}!",
  1852. "visibility" => "direct"
  1853. })
  1854. CommonAPI.favorite(direct.id, user)
  1855. response =
  1856. conn
  1857. |> assign(:user, current_user)
  1858. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1859. |> json_response(:ok)
  1860. assert Enum.empty?(response)
  1861. end
  1862. test "paginates favorites using since_id and max_id", %{
  1863. conn: conn,
  1864. current_user: current_user,
  1865. user: user
  1866. } do
  1867. activities = insert_list(10, :note_activity)
  1868. Enum.each(activities, fn activity ->
  1869. CommonAPI.favorite(activity.id, user)
  1870. end)
  1871. third_activity = Enum.at(activities, 2)
  1872. seventh_activity = Enum.at(activities, 6)
  1873. response =
  1874. conn
  1875. |> assign(:user, current_user)
  1876. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
  1877. since_id: third_activity.id,
  1878. max_id: seventh_activity.id
  1879. })
  1880. |> json_response(:ok)
  1881. assert length(response) == 3
  1882. refute third_activity in response
  1883. refute seventh_activity in response
  1884. end
  1885. test "limits favorites using limit parameter", %{
  1886. conn: conn,
  1887. current_user: current_user,
  1888. user: user
  1889. } do
  1890. 7
  1891. |> insert_list(:note_activity)
  1892. |> Enum.each(fn activity ->
  1893. CommonAPI.favorite(activity.id, user)
  1894. end)
  1895. response =
  1896. conn
  1897. |> assign(:user, current_user)
  1898. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
  1899. |> json_response(:ok)
  1900. assert length(response) == 3
  1901. end
  1902. test "returns empty response when user does not have any favorited statuses", %{
  1903. conn: conn,
  1904. current_user: current_user,
  1905. user: user
  1906. } do
  1907. response =
  1908. conn
  1909. |> assign(:user, current_user)
  1910. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1911. |> json_response(:ok)
  1912. assert Enum.empty?(response)
  1913. end
  1914. test "returns 404 error when specified user is not exist", %{conn: conn} do
  1915. conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
  1916. assert json_response(conn, 404) == %{"error" => "Record not found"}
  1917. end
  1918. test "returns 403 error when user has hidden own favorites", %{
  1919. conn: conn,
  1920. current_user: current_user
  1921. } do
  1922. user = insert(:user, %{info: %{hide_favorites: true}})
  1923. activity = insert(:note_activity)
  1924. CommonAPI.favorite(activity.id, user)
  1925. conn =
  1926. conn
  1927. |> assign(:user, current_user)
  1928. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1929. assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
  1930. end
  1931. test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
  1932. user = insert(:user)
  1933. activity = insert(:note_activity)
  1934. CommonAPI.favorite(activity.id, user)
  1935. conn =
  1936. conn
  1937. |> assign(:user, current_user)
  1938. |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
  1939. assert user.info.hide_favorites
  1940. assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
  1941. end
  1942. end
  1943. test "get instance information", %{conn: conn} do
  1944. conn = get(conn, "/api/v1/instance")
  1945. assert result = json_response(conn, 200)
  1946. email = Config.get([:instance, :email])
  1947. # Note: not checking for "max_toot_chars" since it's optional
  1948. assert %{
  1949. "uri" => _,
  1950. "title" => _,
  1951. "description" => _,
  1952. "version" => _,
  1953. "email" => from_config_email,
  1954. "urls" => %{
  1955. "streaming_api" => _
  1956. },
  1957. "stats" => _,
  1958. "thumbnail" => _,
  1959. "languages" => _,
  1960. "registrations" => _,
  1961. "poll_limits" => _
  1962. } = result
  1963. assert email == from_config_email
  1964. end
  1965. test "get instance stats", %{conn: conn} do
  1966. user = insert(:user, %{local: true})
  1967. user2 = insert(:user, %{local: true})
  1968. {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
  1969. insert(:user, %{local: false, nickname: "u@peer1.com"})
  1970. insert(:user, %{local: false, nickname: "u@peer2.com"})
  1971. {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
  1972. # Stats should count users with missing or nil `info.deactivated` value
  1973. user = User.get_cached_by_id(user.id)
  1974. info_change = Changeset.change(user.info, %{deactivated: nil})
  1975. {:ok, _user} =
  1976. user
  1977. |> Changeset.change()
  1978. |> Changeset.put_embed(:info, info_change)
  1979. |> User.update_and_set_cache()
  1980. Pleroma.Stats.force_update()
  1981. conn = get(conn, "/api/v1/instance")
  1982. assert result = json_response(conn, 200)
  1983. stats = result["stats"]
  1984. assert stats
  1985. assert stats["user_count"] == 1
  1986. assert stats["status_count"] == 1
  1987. assert stats["domain_count"] == 2
  1988. end
  1989. test "get peers", %{conn: conn} do
  1990. insert(:user, %{local: false, nickname: "u@peer1.com"})
  1991. insert(:user, %{local: false, nickname: "u@peer2.com"})
  1992. Pleroma.Stats.force_update()
  1993. conn = get(conn, "/api/v1/instance/peers")
  1994. assert result = json_response(conn, 200)
  1995. assert ["peer1.com", "peer2.com"] == Enum.sort(result)
  1996. end
  1997. test "put settings", %{conn: conn} do
  1998. user = insert(:user)
  1999. conn =
  2000. conn
  2001. |> assign(:user, user)
  2002. |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
  2003. assert _result = json_response(conn, 200)
  2004. user = User.get_cached_by_ap_id(user.ap_id)
  2005. assert user.info.settings == %{"programming" => "socks"}
  2006. end
  2007. describe "pinned statuses" do
  2008. setup do
  2009. user = insert(:user)
  2010. {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
  2011. [user: user, activity: activity]
  2012. end
  2013. clear_config([:instance, :max_pinned_statuses]) do
  2014. Config.put([:instance, :max_pinned_statuses], 1)
  2015. end
  2016. test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
  2017. {:ok, _} = CommonAPI.pin(activity.id, user)
  2018. result =
  2019. conn
  2020. |> assign(:user, user)
  2021. |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
  2022. |> json_response(200)
  2023. id_str = to_string(activity.id)
  2024. assert [%{"id" => ^id_str, "pinned" => true}] = result
  2025. end
  2026. test "pin status", %{conn: conn, user: user, activity: activity} do
  2027. id_str = to_string(activity.id)
  2028. assert %{"id" => ^id_str, "pinned" => true} =
  2029. conn
  2030. |> assign(:user, user)
  2031. |> post("/api/v1/statuses/#{activity.id}/pin")
  2032. |> json_response(200)
  2033. assert [%{"id" => ^id_str, "pinned" => true}] =
  2034. conn
  2035. |> assign(:user, user)
  2036. |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
  2037. |> json_response(200)
  2038. end
  2039. test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
  2040. {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
  2041. conn =
  2042. conn
  2043. |> assign(:user, user)
  2044. |> post("/api/v1/statuses/#{dm.id}/pin")
  2045. assert json_response(conn, 400) == %{"error" => "Could not pin"}
  2046. end
  2047. test "unpin status", %{conn: conn, user: user, activity: activity} do
  2048. {:ok, _} = CommonAPI.pin(activity.id, user)
  2049. id_str = to_string(activity.id)
  2050. user = refresh_record(user)
  2051. assert %{"id" => ^id_str, "pinned" => false} =
  2052. conn
  2053. |> assign(:user, user)
  2054. |> post("/api/v1/statuses/#{activity.id}/unpin")
  2055. |> json_response(200)
  2056. assert [] =
  2057. conn
  2058. |> assign(:user, user)
  2059. |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
  2060. |> json_response(200)
  2061. end
  2062. test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
  2063. conn =
  2064. conn
  2065. |> assign(:user, user)
  2066. |> post("/api/v1/statuses/1/unpin")
  2067. assert json_response(conn, 400) == %{"error" => "Could not unpin"}
  2068. end
  2069. test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
  2070. {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
  2071. id_str_one = to_string(activity_one.id)
  2072. assert %{"id" => ^id_str_one, "pinned" => true} =
  2073. conn
  2074. |> assign(:user, user)
  2075. |> post("/api/v1/statuses/#{id_str_one}/pin")
  2076. |> json_response(200)
  2077. user = refresh_record(user)
  2078. assert %{"error" => "You have already pinned the maximum number of statuses"} =
  2079. conn
  2080. |> assign(:user, user)
  2081. |> post("/api/v1/statuses/#{activity_two.id}/pin")
  2082. |> json_response(400)
  2083. end
  2084. end
  2085. describe "cards" do
  2086. setup do
  2087. Config.put([:rich_media, :enabled], true)
  2088. user = insert(:user)
  2089. %{user: user}
  2090. end
  2091. test "returns rich-media card", %{conn: conn, user: user} do
  2092. {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
  2093. card_data = %{
  2094. "image" => "http://ia.media-imdb.com/images/rock.jpg",
  2095. "provider_name" => "example.com",
  2096. "provider_url" => "https://example.com",
  2097. "title" => "The Rock",
  2098. "type" => "link",
  2099. "url" => "https://example.com/ogp",
  2100. "description" =>
  2101. "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
  2102. "pleroma" => %{
  2103. "opengraph" => %{
  2104. "image" => "http://ia.media-imdb.com/images/rock.jpg",
  2105. "title" => "The Rock",
  2106. "type" => "video.movie",
  2107. "url" => "https://example.com/ogp",
  2108. "description" =>
  2109. "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
  2110. }
  2111. }
  2112. }
  2113. response =
  2114. conn
  2115. |> get("/api/v1/statuses/#{activity.id}/card")
  2116. |> json_response(200)
  2117. assert response == card_data
  2118. # works with private posts
  2119. {:ok, activity} =
  2120. CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
  2121. response_two =
  2122. conn
  2123. |> assign(:user, user)
  2124. |> get("/api/v1/statuses/#{activity.id}/card")
  2125. |> json_response(200)
  2126. assert response_two == card_data
  2127. end
  2128. test "replaces missing description with an empty string", %{conn: conn, user: user} do
  2129. {:ok, activity} =
  2130. CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
  2131. response =
  2132. conn
  2133. |> get("/api/v1/statuses/#{activity.id}/card")
  2134. |> json_response(:ok)
  2135. assert response == %{
  2136. "type" => "link",
  2137. "title" => "Pleroma",
  2138. "description" => "",
  2139. "image" => nil,
  2140. "provider_name" => "example.com",
  2141. "provider_url" => "https://example.com",
  2142. "url" => "https://example.com/ogp-missing-data",
  2143. "pleroma" => %{
  2144. "opengraph" => %{
  2145. "title" => "Pleroma",
  2146. "type" => "website",
  2147. "url" => "https://example.com/ogp-missing-data"
  2148. }
  2149. }
  2150. }
  2151. end
  2152. end
  2153. test "bookmarks" do
  2154. user = insert(:user)
  2155. for_user = insert(:user)
  2156. {:ok, activity1} =
  2157. CommonAPI.post(user, %{
  2158. "status" => "heweoo?"
  2159. })
  2160. {:ok, activity2} =
  2161. CommonAPI.post(user, %{
  2162. "status" => "heweoo!"
  2163. })
  2164. response1 =
  2165. build_conn()
  2166. |> assign(:user, for_user)
  2167. |> post("/api/v1/statuses/#{activity1.id}/bookmark")
  2168. assert json_response(response1, 200)["bookmarked"] == true
  2169. response2 =
  2170. build_conn()
  2171. |> assign(:user, for_user)
  2172. |> post("/api/v1/statuses/#{activity2.id}/bookmark")
  2173. assert json_response(response2, 200)["bookmarked"] == true
  2174. bookmarks =
  2175. build_conn()
  2176. |> assign(:user, for_user)
  2177. |> get("/api/v1/bookmarks")
  2178. assert [json_response(response2, 200), json_response(response1, 200)] ==
  2179. json_response(bookmarks, 200)
  2180. response1 =
  2181. build_conn()
  2182. |> assign(:user, for_user)
  2183. |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
  2184. assert json_response(response1, 200)["bookmarked"] == false
  2185. bookmarks =
  2186. build_conn()
  2187. |> assign(:user, for_user)
  2188. |> get("/api/v1/bookmarks")
  2189. assert [json_response(response2, 200)] == json_response(bookmarks, 200)
  2190. end
  2191. describe "conversation muting" do
  2192. setup do
  2193. post_user = insert(:user)
  2194. user = insert(:user)
  2195. {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
  2196. [user: user, activity: activity]
  2197. end
  2198. test "mute conversation", %{conn: conn, user: user, activity: activity} do
  2199. id_str = to_string(activity.id)
  2200. assert %{"id" => ^id_str, "muted" => true} =
  2201. conn
  2202. |> assign(:user, user)
  2203. |> post("/api/v1/statuses/#{activity.id}/mute")
  2204. |> json_response(200)
  2205. end
  2206. test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
  2207. {:ok, _} = CommonAPI.add_mute(user, activity)
  2208. conn =
  2209. conn
  2210. |> assign(:user, user)
  2211. |> post("/api/v1/statuses/#{activity.id}/mute")
  2212. assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
  2213. end
  2214. test "unmute conversation", %{conn: conn, user: user, activity: activity} do
  2215. {:ok, _} = CommonAPI.add_mute(user, activity)
  2216. id_str = to_string(activity.id)
  2217. user = refresh_record(user)
  2218. assert %{"id" => ^id_str, "muted" => false} =
  2219. conn
  2220. |> assign(:user, user)
  2221. |> post("/api/v1/statuses/#{activity.id}/unmute")
  2222. |> json_response(200)
  2223. end
  2224. end
  2225. describe "reports" do
  2226. setup do
  2227. reporter = insert(:user)
  2228. target_user = insert(:user)
  2229. {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
  2230. [reporter: reporter, target_user: target_user, activity: activity]
  2231. end
  2232. test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
  2233. assert %{"action_taken" => false, "id" => _} =
  2234. conn
  2235. |> assign(:user, reporter)
  2236. |> post("/api/v1/reports", %{"account_id" => target_user.id})
  2237. |> json_response(200)
  2238. end
  2239. test "submit a report with statuses and comment", %{
  2240. conn: conn,
  2241. reporter: reporter,
  2242. target_user: target_user,
  2243. activity: activity
  2244. } do
  2245. assert %{"action_taken" => false, "id" => _} =
  2246. conn
  2247. |> assign(:user, reporter)
  2248. |> post("/api/v1/reports", %{
  2249. "account_id" => target_user.id,
  2250. "status_ids" => [activity.id],
  2251. "comment" => "bad status!",
  2252. "forward" => "false"
  2253. })
  2254. |> json_response(200)
  2255. end
  2256. test "account_id is required", %{
  2257. conn: conn,
  2258. reporter: reporter,
  2259. activity: activity
  2260. } do
  2261. assert %{"error" => "Valid `account_id` required"} =
  2262. conn
  2263. |> assign(:user, reporter)
  2264. |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
  2265. |> json_response(400)
  2266. end
  2267. test "comment must be up to the size specified in the config", %{
  2268. conn: conn,
  2269. reporter: reporter,
  2270. target_user: target_user
  2271. } do
  2272. max_size = Config.get([:instance, :max_report_comment_size], 1000)
  2273. comment = String.pad_trailing("a", max_size + 1, "a")
  2274. error = %{"error" => "Comment must be up to #{max_size} characters"}
  2275. assert ^error =
  2276. conn
  2277. |> assign(:user, reporter)
  2278. |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
  2279. |> json_response(400)
  2280. end
  2281. test "returns error when account is not exist", %{
  2282. conn: conn,
  2283. reporter: reporter,
  2284. activity: activity
  2285. } do
  2286. conn =
  2287. conn
  2288. |> assign(:user, reporter)
  2289. |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
  2290. assert json_response(conn, 400) == %{"error" => "Account not found"}
  2291. end
  2292. end
  2293. describe "link headers" do
  2294. test "preserves parameters in link headers", %{conn: conn} do
  2295. user = insert(:user)
  2296. other_user = insert(:user)
  2297. {:ok, activity1} =
  2298. CommonAPI.post(other_user, %{
  2299. "status" => "hi @#{user.nickname}",
  2300. "visibility" => "public"
  2301. })
  2302. {:ok, activity2} =
  2303. CommonAPI.post(other_user, %{
  2304. "status" => "hi @#{user.nickname}",
  2305. "visibility" => "public"
  2306. })
  2307. notification1 = Repo.get_by(Notification, activity_id: activity1.id)
  2308. notification2 = Repo.get_by(Notification, activity_id: activity2.id)
  2309. conn =
  2310. conn
  2311. |> assign(:user, user)
  2312. |> get("/api/v1/notifications", %{media_only: true})
  2313. assert [link_header] = get_resp_header(conn, "link")
  2314. assert link_header =~ ~r/media_only=true/
  2315. assert link_header =~ ~r/min_id=#{notification2.id}/
  2316. assert link_header =~ ~r/max_id=#{notification1.id}/
  2317. end
  2318. end
  2319. test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
  2320. # Need to set an old-style integer ID to reproduce the problem
  2321. # (these are no longer assigned to new accounts but were preserved
  2322. # for existing accounts during the migration to flakeIDs)
  2323. user_one = insert(:user, %{id: 1212})
  2324. user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
  2325. resp_one =
  2326. conn
  2327. |> get("/api/v1/accounts/#{user_one.id}")
  2328. resp_two =
  2329. conn
  2330. |> get("/api/v1/accounts/#{user_two.nickname}")
  2331. resp_three =
  2332. conn
  2333. |> get("/api/v1/accounts/#{user_two.id}")
  2334. acc_one = json_response(resp_one, 200)
  2335. acc_two = json_response(resp_two, 200)
  2336. acc_three = json_response(resp_three, 200)
  2337. refute acc_one == acc_two
  2338. assert acc_two == acc_three
  2339. end
  2340. describe "custom emoji" do
  2341. test "with tags", %{conn: conn} do
  2342. [emoji | _body] =
  2343. conn
  2344. |> get("/api/v1/custom_emojis")
  2345. |> json_response(200)
  2346. assert Map.has_key?(emoji, "shortcode")
  2347. assert Map.has_key?(emoji, "static_url")
  2348. assert Map.has_key?(emoji, "tags")
  2349. assert is_list(emoji["tags"])
  2350. assert Map.has_key?(emoji, "category")
  2351. assert Map.has_key?(emoji, "url")
  2352. assert Map.has_key?(emoji, "visible_in_picker")
  2353. end
  2354. end
  2355. describe "index/2 redirections" do
  2356. setup %{conn: conn} do
  2357. session_opts = [
  2358. store: :cookie,
  2359. key: "_test",
  2360. signing_salt: "cooldude"
  2361. ]
  2362. conn =
  2363. conn
  2364. |> Plug.Session.call(Plug.Session.init(session_opts))
  2365. |> fetch_session()
  2366. test_path = "/web/statuses/test"
  2367. %{conn: conn, path: test_path}
  2368. end
  2369. test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
  2370. conn = get(conn, path)
  2371. assert conn.status == 302
  2372. assert redirected_to(conn) == "/web/login"
  2373. end
  2374. test "redirects not logged-in users to the login page on private instances", %{
  2375. conn: conn,
  2376. path: path
  2377. } do
  2378. Config.put([:instance, :public], false)
  2379. conn = get(conn, path)
  2380. assert conn.status == 302
  2381. assert redirected_to(conn) == "/web/login"
  2382. end
  2383. test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
  2384. token = insert(:oauth_token)
  2385. conn =
  2386. conn
  2387. |> assign(:user, token.user)
  2388. |> put_session(:oauth_token, token.token)
  2389. |> get(path)
  2390. assert conn.status == 200
  2391. end
  2392. test "saves referer path to session", %{conn: conn, path: path} do
  2393. conn = get(conn, path)
  2394. return_to = Plug.Conn.get_session(conn, :return_to)
  2395. assert return_to == path
  2396. end
  2397. test "redirects to the saved path after log in", %{conn: conn, path: path} do
  2398. app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
  2399. auth = insert(:oauth_authorization, app: app)
  2400. conn =
  2401. conn
  2402. |> put_session(:return_to, path)
  2403. |> get("/web/login", %{code: auth.token})
  2404. assert conn.status == 302
  2405. assert redirected_to(conn) == path
  2406. end
  2407. test "redirects to the getting-started page when referer is not present", %{conn: conn} do
  2408. app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
  2409. auth = insert(:oauth_authorization, app: app)
  2410. conn = get(conn, "/web/login", %{code: auth.token})
  2411. assert conn.status == 302
  2412. assert redirected_to(conn) == "/web/getting-started"
  2413. end
  2414. end
  2415. describe "scheduled activities" do
  2416. test "creates a scheduled activity", %{conn: conn} do
  2417. user = insert(:user)
  2418. scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
  2419. conn =
  2420. conn
  2421. |> assign(:user, user)
  2422. |> post("/api/v1/statuses", %{
  2423. "status" => "scheduled",
  2424. "scheduled_at" => scheduled_at
  2425. })
  2426. assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
  2427. assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
  2428. assert [] == Repo.all(Activity)
  2429. end
  2430. test "creates a scheduled activity with a media attachment", %{conn: conn} do
  2431. user = insert(:user)
  2432. scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
  2433. file = %Plug.Upload{
  2434. content_type: "image/jpg",
  2435. path: Path.absname("test/fixtures/image.jpg"),
  2436. filename: "an_image.jpg"
  2437. }
  2438. {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
  2439. conn =
  2440. conn
  2441. |> assign(:user, user)
  2442. |> post("/api/v1/statuses", %{
  2443. "media_ids" => [to_string(upload.id)],
  2444. "status" => "scheduled",
  2445. "scheduled_at" => scheduled_at
  2446. })
  2447. assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
  2448. assert %{"type" => "image"} = media_attachment
  2449. end
  2450. test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
  2451. %{conn: conn} do
  2452. user = insert(:user)
  2453. scheduled_at =
  2454. NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
  2455. conn =
  2456. conn
  2457. |> assign(:user, user)
  2458. |> post("/api/v1/statuses", %{
  2459. "status" => "not scheduled",
  2460. "scheduled_at" => scheduled_at
  2461. })
  2462. assert %{"content" => "not scheduled"} = json_response(conn, 200)
  2463. assert [] == Repo.all(ScheduledActivity)
  2464. end
  2465. test "returns error when daily user limit is exceeded", %{conn: conn} do
  2466. user = insert(:user)
  2467. today =
  2468. NaiveDateTime.utc_now()
  2469. |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
  2470. |> NaiveDateTime.to_iso8601()
  2471. attrs = %{params: %{}, scheduled_at: today}
  2472. {:ok, _} = ScheduledActivity.create(user, attrs)
  2473. {:ok, _} = ScheduledActivity.create(user, attrs)
  2474. conn =
  2475. conn
  2476. |> assign(:user, user)
  2477. |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
  2478. assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
  2479. end
  2480. test "returns error when total user limit is exceeded", %{conn: conn} do
  2481. user = insert(:user)
  2482. today =
  2483. NaiveDateTime.utc_now()
  2484. |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
  2485. |> NaiveDateTime.to_iso8601()
  2486. tomorrow =
  2487. NaiveDateTime.utc_now()
  2488. |> NaiveDateTime.add(:timer.hours(36), :millisecond)
  2489. |> NaiveDateTime.to_iso8601()
  2490. attrs = %{params: %{}, scheduled_at: today}
  2491. {:ok, _} = ScheduledActivity.create(user, attrs)
  2492. {:ok, _} = ScheduledActivity.create(user, attrs)
  2493. {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
  2494. conn =
  2495. conn
  2496. |> assign(:user, user)
  2497. |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
  2498. assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
  2499. end
  2500. test "shows scheduled activities", %{conn: conn} do
  2501. user = insert(:user)
  2502. scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
  2503. scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
  2504. scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
  2505. scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
  2506. conn =
  2507. conn
  2508. |> assign(:user, user)
  2509. # min_id
  2510. conn_res =
  2511. conn
  2512. |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
  2513. result = json_response(conn_res, 200)
  2514. assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
  2515. # since_id
  2516. conn_res =
  2517. conn
  2518. |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
  2519. result = json_response(conn_res, 200)
  2520. assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
  2521. # max_id
  2522. conn_res =
  2523. conn
  2524. |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
  2525. result = json_response(conn_res, 200)
  2526. assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
  2527. end
  2528. test "shows a scheduled activity", %{conn: conn} do
  2529. user = insert(:user)
  2530. scheduled_activity = insert(:scheduled_activity, user: user)
  2531. res_conn =
  2532. conn
  2533. |> assign(:user, user)
  2534. |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
  2535. assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
  2536. assert scheduled_activity_id == scheduled_activity.id |> to_string()
  2537. res_conn =
  2538. conn
  2539. |> assign(:user, user)
  2540. |> get("/api/v1/scheduled_statuses/404")
  2541. assert %{"error" => "Record not found"} = json_response(res_conn, 404)
  2542. end
  2543. test "updates a scheduled activity", %{conn: conn} do
  2544. user = insert(:user)
  2545. scheduled_activity = insert(:scheduled_activity, user: user)
  2546. new_scheduled_at =
  2547. NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
  2548. res_conn =
  2549. conn
  2550. |> assign(:user, user)
  2551. |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
  2552. scheduled_at: new_scheduled_at
  2553. })
  2554. assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
  2555. assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
  2556. res_conn =
  2557. conn
  2558. |> assign(:user, user)
  2559. |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
  2560. assert %{"error" => "Record not found"} = json_response(res_conn, 404)
  2561. end
  2562. test "deletes a scheduled activity", %{conn: conn} do
  2563. user = insert(:user)
  2564. scheduled_activity = insert(:scheduled_activity, user: user)
  2565. res_conn =
  2566. conn
  2567. |> assign(:user, user)
  2568. |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
  2569. assert %{} = json_response(res_conn, 200)
  2570. assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
  2571. res_conn =
  2572. conn
  2573. |> assign(:user, user)
  2574. |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
  2575. assert %{"error" => "Record not found"} = json_response(res_conn, 404)
  2576. end
  2577. end
  2578. test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
  2579. user1 = insert(:user)
  2580. user2 = insert(:user)
  2581. user3 = insert(:user)
  2582. {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
  2583. # Reply to status from another user
  2584. conn1 =
  2585. conn
  2586. |> assign(:user, user2)
  2587. |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
  2588. assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
  2589. activity = Activity.get_by_id_with_object(id)
  2590. assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
  2591. assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
  2592. # Reblog from the third user
  2593. conn2 =
  2594. conn
  2595. |> assign(:user, user3)
  2596. |> post("/api/v1/statuses/#{activity.id}/reblog")
  2597. assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
  2598. json_response(conn2, 200)
  2599. assert to_string(activity.id) == id
  2600. # Getting third user status
  2601. conn3 =
  2602. conn
  2603. |> assign(:user, user3)
  2604. |> get("api/v1/timelines/home")
  2605. [reblogged_activity] = json_response(conn3, 200)
  2606. assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
  2607. replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
  2608. assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
  2609. end
  2610. describe "create account by app" do
  2611. test "Account registration via Application", %{conn: conn} do
  2612. conn =
  2613. conn
  2614. |> post("/api/v1/apps", %{
  2615. client_name: "client_name",
  2616. redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
  2617. scopes: "read, write, follow"
  2618. })
  2619. %{
  2620. "client_id" => client_id,
  2621. "client_secret" => client_secret,
  2622. "id" => _,
  2623. "name" => "client_name",
  2624. "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
  2625. "vapid_key" => _,
  2626. "website" => nil
  2627. } = json_response(conn, 200)
  2628. conn =
  2629. conn
  2630. |> post("/oauth/token", %{
  2631. grant_type: "client_credentials",
  2632. client_id: client_id,
  2633. client_secret: client_secret
  2634. })
  2635. assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
  2636. json_response(conn, 200)
  2637. assert token
  2638. token_from_db = Repo.get_by(Token, token: token)
  2639. assert token_from_db
  2640. assert refresh
  2641. assert scope == "read write follow"
  2642. conn =
  2643. build_conn()
  2644. |> put_req_header("authorization", "Bearer " <> token)
  2645. |> post("/api/v1/accounts", %{
  2646. username: "lain",
  2647. email: "lain@example.org",
  2648. password: "PlzDontHackLain",
  2649. agreement: true
  2650. })
  2651. %{
  2652. "access_token" => token,
  2653. "created_at" => _created_at,
  2654. "scope" => _scope,
  2655. "token_type" => "Bearer"
  2656. } = json_response(conn, 200)
  2657. token_from_db = Repo.get_by(Token, token: token)
  2658. assert token_from_db
  2659. token_from_db = Repo.preload(token_from_db, :user)
  2660. assert token_from_db.user
  2661. assert token_from_db.user.info.confirmation_pending
  2662. end
  2663. test "rate limit", %{conn: conn} do
  2664. app_token = insert(:oauth_token, user: nil)
  2665. conn =
  2666. put_req_header(conn, "authorization", "Bearer " <> app_token.token)
  2667. |> Map.put(:remote_ip, {15, 15, 15, 15})
  2668. for i <- 1..5 do
  2669. conn =
  2670. conn
  2671. |> post("/api/v1/accounts", %{
  2672. username: "#{i}lain",
  2673. email: "#{i}lain@example.org",
  2674. password: "PlzDontHackLain",
  2675. agreement: true
  2676. })
  2677. %{
  2678. "access_token" => token,
  2679. "created_at" => _created_at,
  2680. "scope" => _scope,
  2681. "token_type" => "Bearer"
  2682. } = json_response(conn, 200)
  2683. token_from_db = Repo.get_by(Token, token: token)
  2684. assert token_from_db
  2685. token_from_db = Repo.preload(token_from_db, :user)
  2686. assert token_from_db.user
  2687. assert token_from_db.user.info.confirmation_pending
  2688. end
  2689. conn =
  2690. conn
  2691. |> post("/api/v1/accounts", %{
  2692. username: "6lain",
  2693. email: "6lain@example.org",
  2694. password: "PlzDontHackLain",
  2695. agreement: true
  2696. })
  2697. assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
  2698. end
  2699. end
  2700. describe "GET /api/v1/polls/:id" do
  2701. test "returns poll entity for object id", %{conn: conn} do
  2702. user = insert(:user)
  2703. {:ok, activity} =
  2704. CommonAPI.post(user, %{
  2705. "status" => "Pleroma does",
  2706. "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
  2707. })
  2708. object = Object.normalize(activity)
  2709. conn =
  2710. conn
  2711. |> assign(:user, user)
  2712. |> get("/api/v1/polls/#{object.id}")
  2713. response = json_response(conn, 200)
  2714. id = to_string(object.id)
  2715. assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
  2716. end
  2717. test "does not expose polls for private statuses", %{conn: conn} do
  2718. user = insert(:user)
  2719. other_user = insert(:user)
  2720. {:ok, activity} =
  2721. CommonAPI.post(user, %{
  2722. "status" => "Pleroma does",
  2723. "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
  2724. "visibility" => "private"
  2725. })
  2726. object = Object.normalize(activity)
  2727. conn =
  2728. conn
  2729. |> assign(:user, other_user)
  2730. |> get("/api/v1/polls/#{object.id}")
  2731. assert json_response(conn, 404)
  2732. end
  2733. end
  2734. describe "POST /api/v1/polls/:id/votes" do
  2735. test "votes are added to the poll", %{conn: conn} do
  2736. user = insert(:user)
  2737. other_user = insert(:user)
  2738. {:ok, activity} =
  2739. CommonAPI.post(user, %{
  2740. "status" => "A very delicious sandwich",
  2741. "poll" => %{
  2742. "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
  2743. "expires_in" => 20,
  2744. "multiple" => true
  2745. }
  2746. })
  2747. object = Object.normalize(activity)
  2748. conn =
  2749. conn
  2750. |> assign(:user, other_user)
  2751. |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
  2752. assert json_response(conn, 200)
  2753. object = Object.get_by_id(object.id)
  2754. assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
  2755. total_items == 1
  2756. end)
  2757. end
  2758. test "author can't vote", %{conn: conn} do
  2759. user = insert(:user)
  2760. {:ok, activity} =
  2761. CommonAPI.post(user, %{
  2762. "status" => "Am I cute?",
  2763. "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
  2764. })
  2765. object = Object.normalize(activity)
  2766. assert conn
  2767. |> assign(:user, user)
  2768. |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
  2769. |> json_response(422) == %{"error" => "Poll's author can't vote"}
  2770. object = Object.get_by_id(object.id)
  2771. refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
  2772. end
  2773. test "does not allow multiple choices on a single-choice question", %{conn: conn} do
  2774. user = insert(:user)
  2775. other_user = insert(:user)
  2776. {:ok, activity} =
  2777. CommonAPI.post(user, %{
  2778. "status" => "The glass is",
  2779. "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
  2780. })
  2781. object = Object.normalize(activity)
  2782. assert conn
  2783. |> assign(:user, other_user)
  2784. |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
  2785. |> json_response(422) == %{"error" => "Too many choices"}
  2786. object = Object.get_by_id(object.id)
  2787. refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
  2788. total_items == 1
  2789. end)
  2790. end
  2791. test "does not allow choice index to be greater than options count", %{conn: conn} do
  2792. user = insert(:user)
  2793. other_user = insert(:user)
  2794. {:ok, activity} =
  2795. CommonAPI.post(user, %{
  2796. "status" => "Am I cute?",
  2797. "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
  2798. })
  2799. object = Object.normalize(activity)
  2800. conn =
  2801. conn
  2802. |> assign(:user, other_user)
  2803. |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
  2804. assert json_response(conn, 422) == %{"error" => "Invalid indices"}
  2805. end
  2806. test "returns 404 error when object is not exist", %{conn: conn} do
  2807. user = insert(:user)
  2808. conn =
  2809. conn
  2810. |> assign(:user, user)
  2811. |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
  2812. assert json_response(conn, 404) == %{"error" => "Record not found"}
  2813. end
  2814. test "returns 404 when poll is private and not available for user", %{conn: conn} do
  2815. user = insert(:user)
  2816. other_user = insert(:user)
  2817. {:ok, activity} =
  2818. CommonAPI.post(user, %{
  2819. "status" => "Am I cute?",
  2820. "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
  2821. "visibility" => "private"
  2822. })
  2823. object = Object.normalize(activity)
  2824. conn =
  2825. conn
  2826. |> assign(:user, other_user)
  2827. |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
  2828. assert json_response(conn, 404) == %{"error" => "Record not found"}
  2829. end
  2830. end
  2831. describe "GET /api/v1/statuses/:id/favourited_by" do
  2832. setup do
  2833. user = insert(:user)
  2834. {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
  2835. conn =
  2836. build_conn()
  2837. |> assign(:user, user)
  2838. [conn: conn, activity: activity, user: user]
  2839. end
  2840. test "returns users who have favorited the status", %{conn: conn, activity: activity} do
  2841. other_user = insert(:user)
  2842. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  2843. response =
  2844. conn
  2845. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2846. |> json_response(:ok)
  2847. [%{"id" => id}] = response
  2848. assert id == other_user.id
  2849. end
  2850. test "returns empty array when status has not been favorited yet", %{
  2851. conn: conn,
  2852. activity: activity
  2853. } do
  2854. response =
  2855. conn
  2856. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2857. |> json_response(:ok)
  2858. assert Enum.empty?(response)
  2859. end
  2860. test "does not return users who have favorited the status but are blocked", %{
  2861. conn: %{assigns: %{user: user}} = conn,
  2862. activity: activity
  2863. } do
  2864. other_user = insert(:user)
  2865. {:ok, user} = User.block(user, other_user)
  2866. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  2867. response =
  2868. conn
  2869. |> assign(:user, user)
  2870. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2871. |> json_response(:ok)
  2872. assert Enum.empty?(response)
  2873. end
  2874. test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
  2875. other_user = insert(:user)
  2876. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  2877. response =
  2878. conn
  2879. |> assign(:user, nil)
  2880. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2881. |> json_response(:ok)
  2882. [%{"id" => id}] = response
  2883. assert id == other_user.id
  2884. end
  2885. test "requires authentification for private posts", %{conn: conn, user: user} do
  2886. other_user = insert(:user)
  2887. {:ok, activity} =
  2888. CommonAPI.post(user, %{
  2889. "status" => "@#{other_user.nickname} wanna get some #cofe together?",
  2890. "visibility" => "direct"
  2891. })
  2892. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  2893. conn
  2894. |> assign(:user, nil)
  2895. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2896. |> json_response(404)
  2897. response =
  2898. build_conn()
  2899. |> assign(:user, other_user)
  2900. |> get("/api/v1/statuses/#{activity.id}/favourited_by")
  2901. |> json_response(200)
  2902. [%{"id" => id}] = response
  2903. assert id == other_user.id
  2904. end
  2905. end
  2906. describe "GET /api/v1/statuses/:id/reblogged_by" do
  2907. setup do
  2908. user = insert(:user)
  2909. {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
  2910. conn =
  2911. build_conn()
  2912. |> assign(:user, user)
  2913. [conn: conn, activity: activity, user: user]
  2914. end
  2915. test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
  2916. other_user = insert(:user)
  2917. {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
  2918. response =
  2919. conn
  2920. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2921. |> json_response(:ok)
  2922. [%{"id" => id}] = response
  2923. assert id == other_user.id
  2924. end
  2925. test "returns empty array when status has not been reblogged yet", %{
  2926. conn: conn,
  2927. activity: activity
  2928. } do
  2929. response =
  2930. conn
  2931. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2932. |> json_response(:ok)
  2933. assert Enum.empty?(response)
  2934. end
  2935. test "does not return users who have reblogged the status but are blocked", %{
  2936. conn: %{assigns: %{user: user}} = conn,
  2937. activity: activity
  2938. } do
  2939. other_user = insert(:user)
  2940. {:ok, user} = User.block(user, other_user)
  2941. {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
  2942. response =
  2943. conn
  2944. |> assign(:user, user)
  2945. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2946. |> json_response(:ok)
  2947. assert Enum.empty?(response)
  2948. end
  2949. test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
  2950. other_user = insert(:user)
  2951. {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
  2952. response =
  2953. conn
  2954. |> assign(:user, nil)
  2955. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2956. |> json_response(:ok)
  2957. [%{"id" => id}] = response
  2958. assert id == other_user.id
  2959. end
  2960. test "requires authentification for private posts", %{conn: conn, user: user} do
  2961. other_user = insert(:user)
  2962. {:ok, activity} =
  2963. CommonAPI.post(user, %{
  2964. "status" => "@#{other_user.nickname} wanna get some #cofe together?",
  2965. "visibility" => "direct"
  2966. })
  2967. conn
  2968. |> assign(:user, nil)
  2969. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2970. |> json_response(404)
  2971. response =
  2972. build_conn()
  2973. |> assign(:user, other_user)
  2974. |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
  2975. |> json_response(200)
  2976. assert [] == response
  2977. end
  2978. end
  2979. describe "POST /auth/password, with valid parameters" do
  2980. setup %{conn: conn} do
  2981. user = insert(:user)
  2982. conn = post(conn, "/auth/password?email=#{user.email}")
  2983. %{conn: conn, user: user}
  2984. end
  2985. test "it returns 204", %{conn: conn} do
  2986. assert json_response(conn, :no_content)
  2987. end
  2988. test "it creates a PasswordResetToken record for user", %{user: user} do
  2989. token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
  2990. assert token_record
  2991. end
  2992. test "it sends an email to user", %{user: user} do
  2993. ObanHelpers.perform_all()
  2994. token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
  2995. email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
  2996. notify_email = Config.get([:instance, :notify_email])
  2997. instance_name = Config.get([:instance, :name])
  2998. assert_email_sent(
  2999. from: {instance_name, notify_email},
  3000. to: {user.name, user.email},
  3001. html_body: email.html_body
  3002. )
  3003. end
  3004. end
  3005. describe "POST /auth/password, with invalid parameters" do
  3006. setup do
  3007. user = insert(:user)
  3008. {:ok, user: user}
  3009. end
  3010. test "it returns 404 when user is not found", %{conn: conn, user: user} do
  3011. conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
  3012. assert conn.status == 404
  3013. assert conn.resp_body == ""
  3014. end
  3015. test "it returns 400 when user is not local", %{conn: conn, user: user} do
  3016. {:ok, user} = Repo.update(Changeset.change(user, local: false))
  3017. conn = post(conn, "/auth/password?email=#{user.email}")
  3018. assert conn.status == 400
  3019. assert conn.resp_body == ""
  3020. end
  3021. end
  3022. describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
  3023. setup do
  3024. user = insert(:user)
  3025. info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
  3026. {:ok, user} =
  3027. user
  3028. |> Changeset.change()
  3029. |> Changeset.put_embed(:info, info_change)
  3030. |> Repo.update()
  3031. assert user.info.confirmation_pending
  3032. [user: user]
  3033. end
  3034. clear_config([:instance, :account_activation_required]) do
  3035. Config.put([:instance, :account_activation_required], true)
  3036. end
  3037. test "resend account confirmation email", %{conn: conn, user: user} do
  3038. conn
  3039. |> assign(:user, user)
  3040. |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
  3041. |> json_response(:no_content)
  3042. ObanHelpers.perform_all()
  3043. email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
  3044. notify_email = Config.get([:instance, :notify_email])
  3045. instance_name = Config.get([:instance, :name])
  3046. assert_email_sent(
  3047. from: {instance_name, notify_email},
  3048. to: {user.name, user.email},
  3049. html_body: email.html_body
  3050. )
  3051. end
  3052. end
  3053. describe "GET /api/v1/suggestions" do
  3054. setup do
  3055. user = insert(:user)
  3056. other_user = insert(:user)
  3057. host = Config.get([Pleroma.Web.Endpoint, :url, :host])
  3058. url500 = "http://test500?#{host}&#{user.nickname}"
  3059. url200 = "http://test200?#{host}&#{user.nickname}"
  3060. mock(fn
  3061. %{method: :get, url: ^url500} ->
  3062. %Tesla.Env{status: 500, body: "bad request"}
  3063. %{method: :get, url: ^url200} ->
  3064. %Tesla.Env{
  3065. status: 200,
  3066. body:
  3067. ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
  3068. other_user.ap_id
  3069. }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
  3070. }
  3071. end)
  3072. [user: user, other_user: other_user]
  3073. end
  3074. clear_config(:suggestions)
  3075. test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
  3076. Config.put([:suggestions, :enabled], false)
  3077. res =
  3078. conn
  3079. |> assign(:user, user)
  3080. |> get("/api/v1/suggestions")
  3081. |> json_response(200)
  3082. assert res == []
  3083. end
  3084. test "returns error", %{conn: conn, user: user} do
  3085. Config.put([:suggestions, :enabled], true)
  3086. Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
  3087. assert capture_log(fn ->
  3088. res =
  3089. conn
  3090. |> assign(:user, user)
  3091. |> get("/api/v1/suggestions")
  3092. |> json_response(500)
  3093. assert res == "Something went wrong"
  3094. end) =~ "Could not retrieve suggestions"
  3095. end
  3096. test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
  3097. Config.put([:suggestions, :enabled], true)
  3098. Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
  3099. res =
  3100. conn
  3101. |> assign(:user, user)
  3102. |> get("/api/v1/suggestions")
  3103. |> json_response(200)
  3104. assert res == [
  3105. %{
  3106. "acct" => "yj455",
  3107. "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
  3108. "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
  3109. "id" => 0
  3110. },
  3111. %{
  3112. "acct" => other_user.ap_id,
  3113. "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
  3114. "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
  3115. "id" => other_user.id
  3116. }
  3117. ]
  3118. end
  3119. end
  3120. end