X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=test%2Fweb%2Fmastodon_api%2Fcontrollers%2Ftimeline_controller_test.exs;h=97b1c3e66c35b06d82d1488d1b3b89b0a6f19f6f;hb=0883a706dc376fdfb7de9df1366803e87c8e7c98;hp=61b6cea7547c49ec2d90a115b8cf2e4abd248e3f;hpb=5c6c3026ea7bd20cb2c10f6e2e34a54734d2509e;p=akkoma diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 61b6cea75..f069390c1 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do @@ -12,54 +12,44 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do alias Pleroma.User alias Pleroma.Web.CommonAPI - clear_config([:instance, :public]) - setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok end describe "home" do - test "the home timeline", %{conn: conn} do - user = insert(:user) - following = insert(:user) - - {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/home") + setup do: oauth_access(["read:statuses"]) - assert Enum.empty?(json_response(conn, :ok)) + test "does NOT embed account/pleroma/relationship in statuses", %{ + user: user, + conn: conn + } do + other_user = insert(:user) - {:ok, user} = User.follow(user, following) + {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - conn = - build_conn() + response = + conn |> assign(:user, user) |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) - assert [%{"content" => "test"}] = json_response(conn, :ok) + assert Enum.all?(response, fn n -> + get_in(n, ["account", "pleroma", "relationship"]) == %{} + end) end - test "the home timeline when the direct messages are excluded", %{conn: conn} do - user = insert(:user) - {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) - {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do + {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - {:ok, unlisted_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) + {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - {:ok, private_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) + conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct") - assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"]) + assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"]) assert public_activity.id in status_ids assert unlisted_activity.id in status_ids assert private_activity.id in status_ids @@ -70,48 +60,177 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do describe "public" do @tag capture_log: true test "the public timeline", %{conn: conn} do - following = insert(:user) + user = insert(:user) - {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) _activity = insert(:note_activity, local: false) - conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) + conn = get(conn, "/api/v1/timelines/public?local=False") - assert length(json_response(conn, :ok)) == 2 + assert length(json_response_and_validate_schema(conn, :ok)) == 2 - conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) + conn = get(build_conn(), "/api/v1/timelines/public?local=True") - assert [%{"content" => "test"}] = json_response(conn, :ok) + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) - conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) + conn = get(build_conn(), "/api/v1/timelines/public?local=1") - assert [%{"content" => "test"}] = json_response(conn, :ok) - end + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + + # does not contain repeats + {:ok, _} = CommonAPI.repeat(activity.id, user) - test "the public timeline when public is set to false", %{conn: conn} do - Config.put([:instance, :public], false) + conn = get(build_conn(), "/api/v1/timelines/public?local=true") - assert %{"error" => "This resource requires authentication."} == - conn - |> get("/api/v1/timelines/public", %{"local" => "False"}) - |> json_response(:forbidden) + assert [_] = json_response_and_validate_schema(conn, :ok) end test "the public timeline includes only public statuses for an authenticated user" do - user = insert(:user) + %{user: user, conn: conn} = oauth_access(["read:statuses"]) - conn = - build_conn() - |> assign(:user, user) + {:ok, _activity} = CommonAPI.post(user, %{status: "test"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) + + res_conn = get(conn, "/api/v1/timelines/public") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + end + + test "doesn't return replies if follower is posting with blocked user" do + %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) + [blockee, friend] = insert_list(2, :user) + {:ok, blocker} = User.follow(blocker, friend) + {:ok, _} = User.block(blocker, blockee) + + conn = assign(conn, :user, blocker) + + {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + {:ok, reply_from_blockee} = + CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + + {:ok, _reply_from_friend} = + CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) res_conn = get(conn, "/api/v1/timelines/public") - assert length(json_response(res_conn, 200)) == 1 + [%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200) + end + + test "doesn't return replies if follow is posting with users from blocked domain" do + %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) + friend = insert(:user) + blockee = insert(:user, ap_id: "https://example.com/users/blocked") + {:ok, blocker} = User.follow(blocker, friend) + {:ok, blocker} = User.block_domain(blocker, "example.com") + + conn = assign(conn, :user, blocker) + + {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) + + {:ok, reply_from_blockee} = + CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + + {:ok, _reply_from_friend} = + CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + + res_conn = get(conn, "/api/v1/timelines/public") + + activities = json_response_and_validate_schema(res_conn, 200) + [%{"id" => ^activity_id}] = activities + end + end + + defp local_and_remote_activities do + insert(:note_activity) + insert(:note_activity, local: false) + :ok + end + + describe "public with restrict unauthenticated timeline for local and federated timelines" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + end + + test "if user is authenticated" do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + end + + describe "public with restrict unauthenticated timeline for local" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + + test "if user is authenticated", %{conn: _conn} do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + end + + describe "public with restrict unauthenticated timeline for remote" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + end + + test "if user is authenticated", %{conn: _conn} do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -124,23 +243,25 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) {:ok, _follower_only} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "private" + status: "Hi @#{user_two.nickname}!", + visibility: "private" }) - # Only direct should be visible here - res_conn = + conn_user_two = conn |> assign(:user, user_two) - |> get("api/v1/timelines/direct") + |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) + + # Only direct should be visible here + res_conn = get(conn_user_two, "api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + assert [status] = json_response_and_validate_schema(res_conn, :ok) assert %{"visibility" => "direct"} = status assert status["url"] != direct.data["id"] @@ -149,136 +270,126 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do res_conn = build_conn() |> assign(:user, user_one) + |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) |> get("api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + [status] = json_response_and_validate_schema(res_conn, :ok) assert %{"visibility" => "direct"} = status # Both should be visible here - res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/home") + res_conn = get(conn_user_two, "api/v1/timelines/home") - [_s1, _s2] = json_response(res_conn, :ok) + [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok) # Test pagination Enum.each(1..20, fn _ -> {:ok, _} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) end) - res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/direct") + res_conn = get(conn_user_two, "api/v1/timelines/direct") - statuses = json_response(res_conn, :ok) + statuses = json_response_and_validate_schema(res_conn, :ok) assert length(statuses) == 20 - res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) + max_id = List.last(statuses)["id"] - [status] = json_response(res_conn, :ok) + res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") + + assert [status] = json_response_and_validate_schema(res_conn, :ok) assert status["url"] != direct.data["id"] end - test "doesn't include DMs from blocked users", %{conn: conn} do - blocker = insert(:user) + test "doesn't include DMs from blocked users" do + %{user: blocker, conn: conn} = oauth_access(["read:statuses"]) blocked = insert(:user) - user = insert(:user) - {:ok, blocker} = User.block(blocker, blocked) + other_user = insert(:user) + {:ok, _user_relationship} = User.block(blocker, blocked) {:ok, _blocked_direct} = CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}!", + visibility: "direct" }) {:ok, direct} = - CommonAPI.post(user, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" + CommonAPI.post(other_user, %{ + status: "Hi @#{blocker.nickname}!", + visibility: "direct" }) - res_conn = - conn - |> assign(:user, user) - |> get("api/v1/timelines/direct") + res_conn = get(conn, "api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + [status] = json_response_and_validate_schema(res_conn, :ok) assert status["id"] == direct.id end end describe "list" do - test "list timeline", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:lists"]) + + test "list timeline", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) - {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) + {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/list/#{list.id}") + conn = get(conn, "/api/v1/timelines/list/#{list.id}") - assert [%{"id" => id}] = json_response(conn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) assert id == to_string(activity_two.id) end - test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do - user = insert(:user) + test "list timeline does not leak non-public statuses for unfollowed users", %{ + user: user, + conn: conn + } do other_user = insert(:user) - {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) {:ok, _activity_two} = CommonAPI.post(other_user, %{ - "status" => "Marisa is cute.", - "visibility" => "private" + status: "Marisa is cute.", + visibility: "private" }) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/list/#{list.id}") + conn = get(conn, "/api/v1/timelines/list/#{list.id}") - assert [%{"id" => id}] = json_response(conn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) assert id == to_string(activity_one.id) end end describe "hashtag" do + setup do: oauth_access(["n/a"]) + @tag capture_log: true test "hashtag timeline", %{conn: conn} do following = insert(:user) - {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) + {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) nconn = get(conn, "/api/v1/timelines/tag/2hu") - assert [%{"id" => id}] = json_response(nconn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) assert id == to_string(activity.id) # works for different capitalization too nconn = get(conn, "/api/v1/timelines/tag/2HU") - assert [%{"id" => id}] = json_response(nconn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) assert id == to_string(activity.id) end @@ -286,26 +397,25 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "multi-hashtag timeline", %{conn: conn} do user = insert(:user) - {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) - {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) - {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) + {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"}) + {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"}) + {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"}) - any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]}) + any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1") - [status_none, status_test1, status_test] = json_response(any_test, :ok) + [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok) assert to_string(activity_test.id) == status_test["id"] assert to_string(activity_test1.id) == status_test1["id"] assert to_string(activity_none.id) == status_none["id"] - restricted_test = - get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) + restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none") - assert [status_test1] == json_response(restricted_test, :ok) + assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok) - all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) + all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none") - assert [status_none] == json_response(all_test, :ok) + assert [status_none] == json_response_and_validate_schema(all_test, :ok) end end end