diff options
Diffstat (limited to 'test')
42 files changed, 1857 insertions, 264 deletions
| diff --git a/test/fixtures/bridgy/actor.json b/test/fixtures/bridgy/actor.json new file mode 100644 index 000000000..5b2d8982b --- /dev/null +++ b/test/fixtures/bridgy/actor.json @@ -0,0 +1,80 @@ +{ +  "id": "https://fed.brid.gy/jk.nipponalba.scot", +  "url": "https://fed.brid.gy/r/https://jk.nipponalba.scot", +  "urls": [ +    { +      "value": "https://jk.nipponalba.scot" +    }, +    { +      "value": "https://social.nipponalba.scot/jk" +    }, +    { +      "value": "https://px.nipponalba.scot/jk" +    } +  ], +  "@context": "https://www.w3.org/ns/activitystreams", +  "type": "Person", +  "name": "J K 🇯🇵🏴", +  "image": [ +    { +      "url": "https://jk.nipponalba.scot/images/profile.jpg", +      "type": "Image", +      "name": "profile picture" +    } +  ], +  "tag": [ +    { +      "type": "Tag", +      "name": "Craft Beer" +    }, +    { +      "type": "Tag", +      "name": "Single Malt Whisky" +    }, +    { +      "type": "Tag", +      "name": "Homebrewing" +    }, +    { +      "type": "Tag", +      "name": "Scottish Politics" +    }, +    { +      "type": "Tag", +      "name": "Scottish History" +    }, +    { +      "type": "Tag", +      "name": "Japanese History" +    }, +    { +      "type": "Tag", +      "name": "Tech" +    }, +    { +      "type": "Tag", +      "name": "Veganism" +    }, +    { +      "type": "Tag", +      "name": "Cooking" +    } +  ], +  "icon": [ +    { +      "url": "https://jk.nipponalba.scot/images/profile.jpg", +      "type": "Image", +      "name": "profile picture" +    } +  ], +  "preferredUsername": "jk.nipponalba.scot", +  "summary": "", +  "publicKey": { +    "id": "jk.nipponalba.scot", +    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdarxwzxnNbJ2hneWOYHkYJowk\npyigQtxlUd0VjgSQHwxU9kWqfbrHBVADyTtcqi/4dAzQd3UnCI1TPNnn4LPZY9PW\noiWd3Zl1/EfLFxO7LU9GS7fcSLQkyj5JNhSlN3I8QPudZbybrgRDVZYooDe1D+52\n5KLGqC2ajrIVOiDRTQIDAQAB\n-----END PUBLIC KEY-----" +  }, +  "inbox": "https://fed.brid.gy/jk.nipponalba.scot/inbox", +  "outbox": "https://fed.brid.gy/jk.nipponalba.scot/outbox", +  "following": "https://fed.brid.gy/jk.nipponalba.scot/following", +  "followers": "https://fed.brid.gy/jk.nipponalba.scot/followers" +} diff --git a/test/fixtures/config/temp.exported_from_db.secret.exs b/test/fixtures/config/temp.exported_from_db.secret.exs new file mode 100644 index 000000000..64bee7f32 --- /dev/null +++ b/test/fixtures/config/temp.exported_from_db.secret.exs @@ -0,0 +1,5 @@ +use Mix.Config + +config :pleroma, exported_config_merged: true + +config :pleroma, :first_setting, key: "new value" diff --git a/test/fixtures/mastodon/collections/featured.json b/test/fixtures/mastodon/collections/featured.json new file mode 100644 index 000000000..56f8f56fa --- /dev/null +++ b/test/fixtures/mastodon/collections/featured.json @@ -0,0 +1,39 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    "https://{{domain}}/schemas/litepub-0.1.jsonld", +    { +      "@language": "und" +    } +  ], +  "id": "https://{{domain}}/users/{{nickname}}/collections/featured", +  "orderedItems": [ +    { +      "@context": [ +        "https://www.w3.org/ns/activitystreams", +        "https://{{domain}}/schemas/litepub-0.1.jsonld", +        { +          "@language": "und" +        } +      ], +      "actor": "https://{{domain}}/users/{{nickname}}", +      "attachment": [], +      "attributedTo": "https://{{domain}}/users/{{nickname}}", +      "cc": [ +        "https://{{domain}}/users/{{nickname}}/followers" +      ], +      "content": "", +      "id": "https://{{domain}}/objects/{{object_id}}", +      "published": "2021-02-12T15:13:43.915429Z", +      "sensitive": false, +      "source": "", +      "summary": "", +      "tag": [], +      "to": [ +        "https://www.w3.org/ns/activitystreams#Public" +      ], +      "type": "Note" +    } +  ], +  "type": "OrderedCollection" +} diff --git a/test/fixtures/statuses/masto-note.json b/test/fixtures/statuses/masto-note.json new file mode 100644 index 000000000..6b96de473 --- /dev/null +++ b/test/fixtures/statuses/masto-note.json @@ -0,0 +1,47 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    { +      "ostatus": "http://ostatus.org#", +      "atomUri": "ostatus:atomUri", +      "inReplyToAtomUri": "ostatus:inReplyToAtomUri", +      "conversation": "ostatus:conversation", +      "sensitive": "as:sensitive", +      "toot": "http://joinmastodon.org/ns#", +      "votersCount": "toot:votersCount" +    } +  ], +  "id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}", +  "type": "Note", +  "summary": null, +  "inReplyTo": null, +  "published": "2021-02-24T12:40:49Z", +  "url": "https://example.com/@{{nickname}}/{{status_id}}", +  "attributedTo": "https://example.com/users/{{nickname}}", +  "to": [ +    "https://www.w3.org/ns/activitystreams#Public" +  ], +  "cc": [ +    "https://example.com/users/{{nickname}}/followers" +  ], +  "sensitive": false, +  "atomUri": "https://example.com/users/{{nickname}}/statuses/{{status_id}}", +  "inReplyToAtomUri": null, +  "conversation": "tag:example.com,2021-02-24:objectId=15:objectType=Conversation", +  "content": "<p></p>", +  "contentMap": { +    "en": "<p></p>" +  }, +  "attachment": [], +  "tag": [], +  "replies": { +    "id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies", +    "type": "Collection", +    "first": { +      "type": "CollectionPage", +      "next": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies?only_other_accounts=true&page=true", +      "partOf": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies", +      "items": [] +    } +  } +} diff --git a/test/fixtures/statuses/note.json b/test/fixtures/statuses/note.json new file mode 100644 index 000000000..41735cbc5 --- /dev/null +++ b/test/fixtures/statuses/note.json @@ -0,0 +1,27 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    "https://example.com/schemas/litepub-0.1.jsonld", +    { +      "@language": "und" +    } +  ], +  "actor": "https://example.com/users/{{nickname}}", +  "attachment": [], +  "attributedTo": "https://example.com/users/{{nickname}}", +  "cc": [ +    "https://example.com/users/{{nickname}}/followers" +  ], +  "content": "Content", +  "context": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f", +  "conversation": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f", +  "id": "https://example.com/objects/{{object_id}}", +  "published": "2019-12-15T22:00:05.279583Z", +  "sensitive": false, +  "summary": "", +  "tag": [], +  "to": [ +    "https://www.w3.org/ns/activitystreams#Public" +  ], +  "type": "Note" +} diff --git a/test/fixtures/tesla_mock/emoji-in-summary.json b/test/fixtures/tesla_mock/emoji-in-summary.json new file mode 100644 index 000000000..f77c6e2e8 --- /dev/null +++ b/test/fixtures/tesla_mock/emoji-in-summary.json @@ -0,0 +1,49 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    "https://patch.cx/schemas/litepub-0.1.jsonld", +    { +      "@language": "und" +    } +  ], +  "actor": "https://patch.cx/users/rin", +  "attachment": [], +  "attributedTo": "https://patch.cx/users/rin", +  "cc": [ +    "https://patch.cx/users/rin/followers" +  ], +  "content": ":joker_disapprove: <br><br>just grabbing a test fixture, nevermind me", +  "context": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326", +  "conversation": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326", +  "id": "https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", +  "published": "2021-03-22T16:54:46.461939Z", +  "sensitive": null, +  "source": ":joker_disapprove: \r\n\r\njust grabbing a test fixture, nevermind me", +  "summary": ":joker_smile: ", +  "tag": [ +    { +      "icon": { +        "type": "Image", +        "url": "https://patch.cx/emoji/custom/joker_disapprove.png" +      }, +      "id": "https://patch.cx/emoji/custom/joker_disapprove.png", +      "name": ":joker_disapprove:", +      "type": "Emoji", +      "updated": "1970-01-01T00:00:00Z" +    }, +    { +      "icon": { +        "type": "Image", +        "url": "https://patch.cx/emoji/custom/joker_smile.png" +      }, +      "id": "https://patch.cx/emoji/custom/joker_smile.png", +      "name": ":joker_smile:", +      "type": "Emoji", +      "updated": "1970-01-01T00:00:00Z" +    } +  ], +  "to": [ +    "https://www.w3.org/ns/activitystreams#Public" +  ], +  "type": "Note" +} diff --git a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta deleted file mode 100644 index 45d260e55..000000000 --- a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> -    <Link rel="lrdd" template="https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /> -</XRD> diff --git a/test/fixtures/users_mock/masto_featured.json b/test/fixtures/users_mock/masto_featured.json new file mode 100644 index 000000000..646a343ad --- /dev/null +++ b/test/fixtures/users_mock/masto_featured.json @@ -0,0 +1,18 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    { +      "ostatus": "http://ostatus.org#", +      "atomUri": "ostatus:atomUri", +      "inReplyToAtomUri": "ostatus:inReplyToAtomUri", +      "conversation": "ostatus:conversation", +      "sensitive": "as:sensitive", +      "toot": "http://joinmastodon.org/ns#", +      "votersCount": "toot:votersCount" +    } +  ], +  "id": "https://{{domain}}/users/{{nickname}}/collections/featured", +  "type": "OrderedCollection", +  "totalItems": 0, +  "orderedItems": [] +} diff --git a/test/fixtures/users_mock/user.json b/test/fixtures/users_mock/user.json new file mode 100644 index 000000000..c722a1145 --- /dev/null +++ b/test/fixtures/users_mock/user.json @@ -0,0 +1,42 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    "https://example.com/schemas/litepub-0.1.jsonld", +    { +      "@language": "und" +    } +  ], +  "attachment": [], +  "endpoints": { +    "oauthAuthorizationEndpoint": "https://example.com/oauth/authorize", +    "oauthRegistrationEndpoint": "https://example.com/api/v1/apps", +    "oauthTokenEndpoint": "https://example.com/oauth/token", +    "sharedInbox": "https://example.com/inbox" +  }, +  "followers": "https://example.com/users/{{nickname}}/followers", +  "following": "https://example.com/users/{{nickname}}/following", +  "icon": { +    "type": "Image", +    "url": "https://example.com/media/4e914f5b84e4a259a3f6c2d2edc9ab642f2ab05f3e3d9c52c81fc2d984b3d51e.jpg" +  }, +  "id": "https://example.com/users/{{nickname}}", +  "image": { +    "type": "Image", +    "url": "https://example.com/media/f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg?name=f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg" +  }, +  "inbox": "https://example.com/users/{{nickname}}/inbox", +  "manuallyApprovesFollowers": false, +  "name": "{{nickname}}", +  "outbox": "https://example.com/users/{{nickname}}/outbox", +  "preferredUsername": "{{nickname}}", +  "publicKey": { +    "id": "https://example.com/users/{{nickname}}#main-key", +    "owner": "https://example.com/users/{{nickname}}", +    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DLtwGXNZElJyxFGfcVc\nXANhaMadj/iYYQwZjOJTV9QsbtiNBeIK54PJrYuU0/0YIdrvS1iqheX5IwXRhcwa\nhm3ZyLz7XeN9st7FBni4BmZMBtMpxAuYuu5p/jbWy13qAiYOhPreCx0wrWgm/lBD\n9mkgaxIxPooBE0S4ZWEJIDIV1Vft3AWcRUyWW1vIBK0uZzs6GYshbQZB952S0yo4\nFzI1hABGHncH8UvuFauh4EZ8tY7/X5I0pGRnDOcRN1dAht5w5yTA+6r5kebiFQjP\nIzN/eCO/a9Flrj9YGW7HDNtjSOH0A31PLRGlJtJO3yK57dnf5ppyCZGfL4emShQo\ncQIDAQAB\n-----END PUBLIC KEY-----\n\n" +  }, +  "featured": "https://example.com/users/{{nickname}}/collections/featured", +  "summary": "your friendly neighborhood pleroma developer<br>I like cute things and distributed systems, and really hate delete and redrafts", +  "tag": [], +  "type": "Person", +  "url": "https://example.com/users/{{nickname}}" +} diff --git a/test/mix/tasks/pleroma/config_test.exs b/test/mix/tasks/pleroma/config_test.exs index 21f8f2286..3ed1e94b8 100644 --- a/test/mix/tasks/pleroma/config_test.exs +++ b/test/mix/tasks/pleroma/config_test.exs @@ -200,6 +200,44 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do      end    end +  describe "migrate_from_db/1" do +    setup do: clear_config(:configurable_from_database, true) + +    setup do +      insert_config_record(:pleroma, :setting_first, key: "value", key2: ["Activity"]) +      insert_config_record(:pleroma, :setting_second, key: "value2", key2: [Repo]) +      insert_config_record(:quack, :level, :info) + +      path = "test/instance_static" +      file_path = Path.join(path, "temp.exported_from_db.secret.exs") + +      on_exit(fn -> File.rm!(file_path) end) + +      [file_path: file_path] +    end + +    test "with path parameter", %{file_path: file_path} do +      MixTask.run(["migrate_from_db", "--env", "temp", "--path", Path.dirname(file_path)]) + +      file = File.read!(file_path) +      assert file =~ "config :pleroma, :setting_first," +      assert file =~ "config :pleroma, :setting_second," +      assert file =~ "config :quack, :level, :info" +    end + +    test "release", %{file_path: file_path} do +      clear_config(:release, true) +      clear_config(:config_path, file_path) + +      MixTask.run(["migrate_from_db", "--env", "temp"]) + +      file = File.read!(file_path) +      assert file =~ "config :pleroma, :setting_first," +      assert file =~ "config :pleroma, :setting_second," +      assert file =~ "config :quack, :level, :info" +    end +  end +    describe "operations on database config" do      setup do: clear_config(:configurable_from_database, true) diff --git a/test/pleroma/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs index 6b848e04d..9c8e5d932 100644 --- a/test/pleroma/activity/ir/topics_test.exs +++ b/test/pleroma/activity/ir/topics_test.exs @@ -11,6 +11,8 @@ defmodule Pleroma.Activity.Ir.TopicsTest do    require Pleroma.Constants +  import Mock +    describe "poll answer" do      test "produce no topics" do        activity = %Activity{object: %Object{data: %{"type" => "Answer"}}} @@ -77,14 +79,13 @@ defmodule Pleroma.Activity.Ir.TopicsTest do        refute Enum.member?(topics, "public:local:media")      end -    test "converts tags to hash tags", %{activity: %{object: %{data: data} = object} = activity} do -      tagged_data = Map.put(data, "tag", ["foo", "bar"]) -      activity = %{activity | object: %{object | data: tagged_data}} - -      topics = Topics.get_activity_topics(activity) +    test "converts tags to hash tags", %{activity: activity} do +      with_mock(Object, [:passthrough], hashtags: fn _ -> ["foo", "bar"] end) do +        topics = Topics.get_activity_topics(activity) -      assert Enum.member?(topics, "hashtag:foo") -      assert Enum.member?(topics, "hashtag:bar") +        assert Enum.member?(topics, "hashtag:foo") +        assert Enum.member?(topics, "hashtag:bar") +      end      end      test "only converts strings to hash tags", %{ diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs index 390a06344..962bc7e45 100644 --- a/test/pleroma/activity_test.exs +++ b/test/pleroma/activity_test.exs @@ -254,4 +254,26 @@ defmodule Pleroma.ActivityTest do      assert %{id: ^id} = Activity.get_by_object_ap_id_with_object(obj_id)    end + +  test "add_by_params_query/3" do +    user = insert(:user) + +    note = insert(:note_activity, user: user) + +    insert(:add_activity, user: user, note: note) +    insert(:add_activity, user: user, note: note) +    insert(:add_activity, user: user) + +    assert Repo.aggregate(Activity, :count, :id) == 4 + +    add_query = +      Activity.add_by_params_query(note.data["object"], user.ap_id, user.featured_address) + +    assert Repo.aggregate(add_query, :count, :id) == 2 + +    Repo.delete_all(add_query) +    assert Repo.aggregate(add_query, :count, :id) == 0 + +    assert Repo.aggregate(Activity, :count, :id) == 2 +  end  end diff --git a/test/pleroma/application_requirements_test.exs b/test/pleroma/application_requirements_test.exs index 683ac8c96..a54c37968 100644 --- a/test/pleroma/application_requirements_test.exs +++ b/test/pleroma/application_requirements_test.exs @@ -35,13 +35,13 @@ defmodule Pleroma.ApplicationRequirementsTest do      setup do: clear_config([:welcome])      setup do: clear_config([Pleroma.Emails.Mailer]) -    test "raises if welcome email enabled but mail disabled" do +    test "warns if welcome email enabled but mail disabled" do        clear_config([:welcome, :email, :enabled], true)        clear_config([Pleroma.Emails.Mailer, :enabled], false) -      assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn -> -        capture_log(&Pleroma.ApplicationRequirements.verify!/0) -      end +      assert capture_log(fn -> +               assert Pleroma.ApplicationRequirements.verify!() == :ok +             end) =~ "Welcome emails will NOT be sent"      end    end @@ -57,15 +57,13 @@ defmodule Pleroma.ApplicationRequirementsTest do      setup do: clear_config([:instance, :account_activation_required]) -    test "raises if account confirmation is required but mailer isn't enable" do +    test "warns if account confirmation is required but mailer isn't enabled" do        clear_config([:instance, :account_activation_required], true)        clear_config([Pleroma.Emails.Mailer, :enabled], false) -      assert_raise Pleroma.ApplicationRequirements.VerifyError, -                   "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails.", -                   fn -> -                     capture_log(&Pleroma.ApplicationRequirements.verify!/0) -                   end +      assert capture_log(fn -> +               assert Pleroma.ApplicationRequirements.verify!() == :ok +             end) =~ "Users will NOT be able to confirm their accounts"      end      test "doesn't do anything if account confirmation is disabled" do diff --git a/test/pleroma/config/release_runtime_provider_test.exs b/test/pleroma/config/release_runtime_provider_test.exs new file mode 100644 index 000000000..6578d3268 --- /dev/null +++ b/test/pleroma/config/release_runtime_provider_test.exs @@ -0,0 +1,45 @@ +defmodule Pleroma.Config.ReleaseRuntimeProviderTest do +  use ExUnit.Case, async: true + +  alias Pleroma.Config.ReleaseRuntimeProvider + +  describe "load/2" do +    test "loads release defaults config and warns about non-existent runtime config" do +      ExUnit.CaptureIO.capture_io(fn -> +        merged = ReleaseRuntimeProvider.load([], []) +        assert merged == Pleroma.Config.Holder.release_defaults() +      end) =~ +        "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" +    end + +    test "merged runtime config" do +      merged = +        ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs") + +      assert merged[:pleroma][:first_setting] == [key: "value", key2: [Pleroma.Repo]] +      assert merged[:pleroma][:second_setting] == [key: "value2", key2: ["Activity"]] +    end + +    test "merged exported config" do +      ExUnit.CaptureIO.capture_io(fn -> +        merged = +          ReleaseRuntimeProvider.load([], +            exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" +          ) + +        assert merged[:pleroma][:exported_config_merged] +      end) =~ +        "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" +    end + +    test "runtime config is merged with exported config" do +      merged = +        ReleaseRuntimeProvider.load([], +          config_path: "test/fixtures/config/temp.secret.exs", +          exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" +        ) + +      assert merged[:pleroma][:first_setting] == [key2: [Pleroma.Repo], key: "new value"] +    end +  end +end diff --git a/test/pleroma/earmark_renderer_test.exs b/test/pleroma/earmark_renderer_test.exs deleted file mode 100644 index 776bc496a..000000000 --- a/test/pleroma/earmark_renderer_test.exs +++ /dev/null @@ -1,79 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.EarmarkRendererTest do -  use Pleroma.DataCase, async: true - -  test "Paragraph" do -    code = ~s[Hello\n\nWorld!] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<p>Hello</p><p>World!</p>" -  end - -  test "raw HTML" do -    code = ~s[<a href="http://example.org/">OwO</a><!-- what's this?-->] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<p>#{code}</p>" -  end - -  test "rulers" do -    code = ~s[before\n\n-----\n\nafter] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<p>before</p><hr /><p>after</p>" -  end - -  test "headings" do -    code = ~s[# h1\n## h2\n### h3\n] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<h1>h1</h1><h2>h2</h2><h3>h3</h3>] -  end - -  test "blockquote" do -    code = ~s[> whoms't are you quoting?] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<blockquote><p>whoms’t are you quoting?</p></blockquote>" -  end - -  test "code" do -    code = ~s[`mix`] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<p><code class="inline">mix</code></p>] - -    code = ~s[``mix``] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<p><code class="inline">mix</code></p>] - -    code = ~s[```\nputs "Hello World"\n```] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<pre><code class="">puts "Hello World"</code></pre>] -  end - -  test "lists" do -    code = ~s[- one\n- two\n- three\n- four] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>" - -    code = ~s[1. one\n2. two\n3. three\n4. four\n] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<ol><li>one</li><li>two</li><li>three</li><li>four</li></ol>" -  end - -  test "delegated renderers" do -    code = ~s[a<br/>b] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == "<p>#{code}</p>" - -    code = ~s[*aaaa~*] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<p><em>aaaa~</em></p>] - -    code = ~s[**aaaa~**] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<p><strong>aaaa~</strong></p>] - -    # strikethrought -    code = ~s[<del>aaaa~</del>] -    result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) -    assert result == ~s[<p><del>aaaa~</del></p>] -  end -end diff --git a/test/pleroma/hashtag_test.exs b/test/pleroma/hashtag_test.exs new file mode 100644 index 000000000..0264dea0b --- /dev/null +++ b/test/pleroma/hashtag_test.exs @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HashtagTest do +  use Pleroma.DataCase + +  alias Pleroma.Hashtag + +  describe "changeset validations" do +    test "ensure non-blank :name" do +      changeset = Hashtag.changeset(%Hashtag{}, %{name: ""}) + +      assert {:name, {"can't be blank", [validation: :required]}} in changeset.errors +    end +  end +end diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index db7678d5d..8320660a5 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -5,10 +5,13 @@  defmodule Pleroma.ObjectTest do    use Pleroma.DataCase    use Oban.Testing, repo: Pleroma.Repo +    import ExUnit.CaptureLog    import Pleroma.Factory    import Tesla.Mock +    alias Pleroma.Activity +  alias Pleroma.Hashtag    alias Pleroma.Object    alias Pleroma.Repo    alias Pleroma.Tests.ObanHelpers @@ -417,4 +420,28 @@ defmodule Pleroma.ObjectTest do        assert updated_object.data["like_count"] == 1      end    end + +  describe ":hashtags association" do +    test "Hashtag records are created with Object record and updated on its change" do +      user = insert(:user) + +      {:ok, %{object: object}} = +        CommonAPI.post(user, %{status: "some text #hashtag1 #hashtag2 ..."}) + +      assert [%Hashtag{name: "hashtag1"}, %Hashtag{name: "hashtag2"}] = +               Enum.sort_by(object.hashtags, & &1.name) + +      {:ok, object} = Object.update_data(object, %{"tag" => []}) + +      assert [] = object.hashtags + +      object = Object.get_by_id(object.id) |> Repo.preload(:hashtags) +      assert [] = object.hashtags + +      {:ok, object} = Object.update_data(object, %{"tag" => ["abc", "def"]}) + +      assert [%Hashtag{name: "abc"}, %Hashtag{name: "def"}] = +               Enum.sort_by(object.hashtags, & &1.name) +    end +  end  end diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 6f5bcab57..c6b631499 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -572,6 +572,24 @@ defmodule Pleroma.UserTest do        )      end +    test "it fails gracefully with invalid email config" do +      cng = User.register_changeset(%User{}, @full_user_data) + +      # Disable the mailer but enable all the things that want to send emails +      clear_config([Pleroma.Emails.Mailer, :enabled], false) +      clear_config([:instance, :account_activation_required], true) +      clear_config([:instance, :account_approval_required], true) +      clear_config([:welcome, :email, :enabled], true) +      clear_config([:welcome, :email, :sender], "lain@lain.com") + +      # The user is still created +      assert {:ok, %User{nickname: "nick"}} = User.register(cng) + +      # No emails are sent +      ObanHelpers.perform_all() +      refute_email_sent() +    end +      test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do        clear_config([:instance, :account_activation_required], true) @@ -2338,4 +2356,49 @@ defmodule Pleroma.UserTest do      assert User.active_user_count(6) == 3      assert User.active_user_count(1) == 1    end + +  describe "pins" do +    setup do +      user = insert(:user) + +      [user: user, object_id: object_id_from_created_activity(user)] +    end + +    test "unique pins", %{user: user, object_id: object_id} do +      assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} = +               User.add_pinned_object_id(user, object_id) + +      assert Enum.count(pins) == 1 + +      assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} = +               User.add_pinned_object_id(updated_user, object_id) + +      assert pinned_at1 == pinned_at2 + +      assert Enum.count(pins) == 1 +    end + +    test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do +      clear_config([:instance, :max_pinned_statuses], 1) +      {:ok, updated} = User.add_pinned_object_id(user, object_id) + +      object_id2 = object_id_from_created_activity(user) + +      {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2) +      assert Keyword.has_key?(errors, :pinned_objects) +    end + +    test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do +      assert {:ok, updated} = User.add_pinned_object_id(user, object_id) + +      {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id) +      assert after_remove.pinned_objects == %{} +    end +  end + +  defp object_id_from_created_activity(user) do +    %{id: id} = insert(:note_activity, user: user) +    %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id) +    object_id +  end  end diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 19e04d472..cea4b3a97 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -636,6 +636,186 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        |> post("/inbox", non_create_data)        |> json_response(400)      end + +    test "accepts Add/Remove activities", %{conn: conn} do +      object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + +      status = +        File.read!("test/fixtures/statuses/note.json") +        |> String.replace("{{nickname}}", "lain") +        |> String.replace("{{object_id}}", object_id) + +      object_url = "https://example.com/objects/#{object_id}" + +      user = +        File.read!("test/fixtures/users_mock/user.json") +        |> String.replace("{{nickname}}", "lain") + +      actor = "https://example.com/users/lain" + +      Tesla.Mock.mock(fn +        %{ +          method: :get, +          url: ^object_url +        } -> +          %Tesla.Env{ +            status: 200, +            body: status, +            headers: [{"content-type", "application/activity+json"}] +          } + +        %{ +          method: :get, +          url: ^actor +        } -> +          %Tesla.Env{ +            status: 200, +            body: user, +            headers: [{"content-type", "application/activity+json"}] +          } + +        %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> +          %Tesla.Env{ +            status: 200, +            body: +              "test/fixtures/users_mock/masto_featured.json" +              |> File.read!() +              |> String.replace("{{domain}}", "example.com") +              |> String.replace("{{nickname}}", "lain"), +            headers: [{"content-type", "application/activity+json"}] +          } +      end) + +      data = %{ +        "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f", +        "actor" => actor, +        "object" => object_url, +        "target" => "https://example.com/users/lain/collections/featured", +        "type" => "Add", +        "to" => [Pleroma.Constants.as_public()] +      } + +      assert "ok" == +               conn +               |> assign(:valid_signature, true) +               |> put_req_header("content-type", "application/activity+json") +               |> post("/inbox", data) +               |> json_response(200) + +      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) +      assert Activity.get_by_ap_id(data["id"]) +      user = User.get_cached_by_ap_id(data["actor"]) +      assert user.pinned_objects[data["object"]] + +      data = %{ +        "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423d", +        "actor" => actor, +        "object" => object_url, +        "target" => "https://example.com/users/lain/collections/featured", +        "type" => "Remove", +        "to" => [Pleroma.Constants.as_public()] +      } + +      assert "ok" == +               conn +               |> assign(:valid_signature, true) +               |> put_req_header("content-type", "application/activity+json") +               |> post("/inbox", data) +               |> json_response(200) + +      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) +      user = refresh_record(user) +      refute user.pinned_objects[data["object"]] +    end + +    test "mastodon pin/unpin", %{conn: conn} do +      status_id = "105786274556060421" + +      status = +        File.read!("test/fixtures/statuses/masto-note.json") +        |> String.replace("{{nickname}}", "lain") +        |> String.replace("{{status_id}}", status_id) + +      status_url = "https://example.com/users/lain/statuses/#{status_id}" + +      user = +        File.read!("test/fixtures/users_mock/user.json") +        |> String.replace("{{nickname}}", "lain") + +      actor = "https://example.com/users/lain" + +      Tesla.Mock.mock(fn +        %{ +          method: :get, +          url: ^status_url +        } -> +          %Tesla.Env{ +            status: 200, +            body: status, +            headers: [{"content-type", "application/activity+json"}] +          } + +        %{ +          method: :get, +          url: ^actor +        } -> +          %Tesla.Env{ +            status: 200, +            body: user, +            headers: [{"content-type", "application/activity+json"}] +          } + +        %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> +          %Tesla.Env{ +            status: 200, +            body: +              "test/fixtures/users_mock/masto_featured.json" +              |> File.read!() +              |> String.replace("{{domain}}", "example.com") +              |> String.replace("{{nickname}}", "lain"), +            headers: [{"content-type", "application/activity+json"}] +          } +      end) + +      data = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "actor" => actor, +        "object" => status_url, +        "target" => "https://example.com/users/lain/collections/featured", +        "type" => "Add" +      } + +      assert "ok" == +               conn +               |> assign(:valid_signature, true) +               |> put_req_header("content-type", "application/activity+json") +               |> post("/inbox", data) +               |> json_response(200) + +      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) +      assert Activity.get_by_object_ap_id_with_object(data["object"]) +      user = User.get_cached_by_ap_id(data["actor"]) +      assert user.pinned_objects[data["object"]] + +      data = %{ +        "actor" => actor, +        "object" => status_url, +        "target" => "https://example.com/users/lain/collections/featured", +        "type" => "Remove" +      } + +      assert "ok" == +               conn +               |> assign(:valid_signature, true) +               |> put_req_header("content-type", "application/activity+json") +               |> post("/inbox", data) +               |> json_response(200) + +      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) +      assert Activity.get_by_object_ap_id_with_object(data["object"]) +      user = refresh_record(user) +      refute user.pinned_objects[data["object"]] +    end    end    describe "/users/:nickname/inbox" do @@ -1772,4 +1952,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        |> json_response(403)      end    end + +  test "pinned collection", %{conn: conn} do +    clear_config([:instance, :max_pinned_statuses], 2) +    user = insert(:user) +    objects = insert_list(2, :note, user: user) + +    Enum.reduce(objects, user, fn %{data: %{"id" => object_id}}, user -> +      {:ok, updated} = User.add_pinned_object_id(user, object_id) +      updated +    end) + +    %{nickname: nickname, featured_address: featured_address, pinned_objects: pinned_objects} = +      refresh_record(user) + +    %{"id" => ^featured_address, "orderedItems" => items} = +      conn +      |> get("/users/#{nickname}/collections/featured") +      |> json_response(200) + +    object_ids = Enum.map(items, & &1["id"]) + +    assert Enum.all?(pinned_objects, fn {obj_id, _} -> +             obj_id in object_ids +           end) +  end  end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index f4023856c..64e12066e 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -208,37 +208,173 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        assert user.name == "Bernie2020 group"        assert user.actor_type == "Group"      end + +    test "works for bridgy actors" do +      user_id = "https://fed.brid.gy/jk.nipponalba.scot" + +      Tesla.Mock.mock(fn +        %{method: :get, url: ^user_id} -> +          %Tesla.Env{ +            status: 200, +            body: File.read!("test/fixtures/bridgy/actor.json"), +            headers: [{"content-type", "application/activity+json"}] +          } +      end) + +      {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) + +      assert user.actor_type == "Person" + +      assert user.avatar == %{ +               "type" => "Image", +               "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}] +             } + +      assert user.banner == %{ +               "type" => "Image", +               "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}] +             } +    end + +    test "fetches user featured collection" do +      ap_id = "https://example.com/users/lain" + +      featured_url = "https://example.com/users/lain/collections/featured" + +      user_data = +        "test/fixtures/users_mock/user.json" +        |> File.read!() +        |> String.replace("{{nickname}}", "lain") +        |> Jason.decode!() +        |> Map.put("featured", featured_url) +        |> Jason.encode!() + +      object_id = Ecto.UUID.generate() + +      featured_data = +        "test/fixtures/mastodon/collections/featured.json" +        |> File.read!() +        |> String.replace("{{domain}}", "example.com") +        |> String.replace("{{nickname}}", "lain") +        |> String.replace("{{object_id}}", object_id) + +      object_url = "https://example.com/objects/#{object_id}" + +      object_data = +        "test/fixtures/statuses/note.json" +        |> File.read!() +        |> String.replace("{{object_id}}", object_id) +        |> String.replace("{{nickname}}", "lain") + +      Tesla.Mock.mock(fn +        %{ +          method: :get, +          url: ^ap_id +        } -> +          %Tesla.Env{ +            status: 200, +            body: user_data, +            headers: [{"content-type", "application/activity+json"}] +          } + +        %{ +          method: :get, +          url: ^featured_url +        } -> +          %Tesla.Env{ +            status: 200, +            body: featured_data, +            headers: [{"content-type", "application/activity+json"}] +          } +      end) + +      Tesla.Mock.mock_global(fn +        %{ +          method: :get, +          url: ^object_url +        } -> +          %Tesla.Env{ +            status: 200, +            body: object_data, +            headers: [{"content-type", "application/activity+json"}] +          } +      end) + +      {:ok, user} = ActivityPub.make_user_from_ap_id(ap_id) +      Process.sleep(50) + +      assert user.featured_address == featured_url +      assert Map.has_key?(user.pinned_objects, object_url) + +      in_db = Pleroma.User.get_by_ap_id(ap_id) +      assert in_db.featured_address == featured_url +      assert Map.has_key?(user.pinned_objects, object_url) + +      assert %{data: %{"id" => ^object_url}} = Object.get_by_ap_id(object_url) +    end    end    test "it fetches the appropriate tag-restricted posts" do      user = insert(:user) -    {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) +    {:ok, status_one} = CommonAPI.post(user, %{status: ". #TEST"})      {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) -    {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) +    {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #Reject"}) -    fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) +    {:ok, status_four} = CommonAPI.post(user, %{status: ". #Any1 #any2"}) +    {:ok, status_five} = CommonAPI.post(user, %{status: ". #Any2 #any1"}) -    fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) +    for hashtag_timeline_strategy <- [:enabled, :disabled] do +      clear_config([:features, :improved_hashtag_timeline], hashtag_timeline_strategy) -    fetch_three = -      ActivityPub.fetch_activities([], %{ -        type: "Create", -        tag: ["test", "essais"], -        tag_reject: ["reject"] -      }) +      fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) -    fetch_four = -      ActivityPub.fetch_activities([], %{ -        type: "Create", -        tag: ["test"], -        tag_all: ["test", "reject"] -      }) +      fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["TEST", "essais"]}) + +      fetch_three = +        ActivityPub.fetch_activities([], %{ +          type: "Create", +          tag: ["test", "Essais"], +          tag_reject: ["reject"] +        }) + +      fetch_four = +        ActivityPub.fetch_activities([], %{ +          type: "Create", +          tag: ["test"], +          tag_all: ["test", "REJECT"] +        }) + +      # Testing that deduplication (if needed) is done on DB (not Ecto) level; :limit is important +      fetch_five = +        ActivityPub.fetch_activities([], %{ +          type: "Create", +          tag: ["ANY1", "any2"], +          limit: 2 +        }) + +      fetch_six = +        ActivityPub.fetch_activities([], %{ +          type: "Create", +          tag: ["any1", "Any2"], +          tag_all: [], +          tag_reject: [] +        }) + +      # Regression test: passing empty lists as filter options shouldn't affect the results +      assert fetch_five == fetch_six -    assert fetch_one == [status_one, status_three] -    assert fetch_two == [status_one, status_two, status_three] -    assert fetch_three == [status_one, status_two] -    assert fetch_four == [status_three] +      [fetch_one, fetch_two, fetch_three, fetch_four, fetch_five] = +        Enum.map([fetch_one, fetch_two, fetch_three, fetch_four, fetch_five], fn statuses -> +          Enum.map(statuses, fn s -> Repo.preload(s, object: :hashtags) end) +        end) + +      assert fetch_one == [status_one, status_three] +      assert fetch_two == [status_one, status_two, status_three] +      assert fetch_three == [status_one, status_two] +      assert fetch_four == [status_three] +      assert fetch_five == [status_four, status_five] +    end    end    describe "insertion" do diff --git a/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs new file mode 100644 index 000000000..a61562558 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs @@ -0,0 +1,126 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do +  use Pleroma.DataCase, async: true + +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy + +  import Pleroma.Factory + +  describe "FollowBotPolicy" do +    test "follows remote users" do +      bot = insert(:user, actor_type: "Service") +      remote_user = insert(:user, local: false) +      clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => [remote_user.follower_address], +        "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +        "type" => "Create", +        "object" => %{ +          "content" => "Test post", +          "type" => "Note", +          "attributedTo" => remote_user.ap_id, +          "inReplyTo" => nil +        }, +        "actor" => remote_user.ap_id +      } + +      refute User.following?(bot, remote_user) + +      assert User.get_follow_requests(remote_user) |> length == 0 + +      FollowBotPolicy.filter(message) + +      assert User.get_follow_requests(remote_user) |> length == 1 +    end + +    test "does not follow users with #nobot in bio" do +      bot = insert(:user, actor_type: "Service") +      remote_user = insert(:user, %{local: false, bio: "go away bots! #nobot"}) +      clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => [remote_user.follower_address], +        "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +        "type" => "Create", +        "object" => %{ +          "content" => "I don't like follow bots", +          "type" => "Note", +          "attributedTo" => remote_user.ap_id, +          "inReplyTo" => nil +        }, +        "actor" => remote_user.ap_id +      } + +      refute User.following?(bot, remote_user) + +      assert User.get_follow_requests(remote_user) |> length == 0 + +      FollowBotPolicy.filter(message) + +      assert User.get_follow_requests(remote_user) |> length == 0 +    end + +    test "does not follow local users" do +      bot = insert(:user, actor_type: "Service") +      local_user = insert(:user, local: true) +      clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => [local_user.follower_address], +        "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +        "type" => "Create", +        "object" => %{ +          "content" => "Hi I'm a local user", +          "type" => "Note", +          "attributedTo" => local_user.ap_id, +          "inReplyTo" => nil +        }, +        "actor" => local_user.ap_id +      } + +      refute User.following?(bot, local_user) + +      assert User.get_follow_requests(local_user) |> length == 0 + +      FollowBotPolicy.filter(message) + +      assert User.get_follow_requests(local_user) |> length == 0 +    end + +    test "does not follow users requiring follower approval" do +      bot = insert(:user, actor_type: "Service") +      remote_user = insert(:user, %{local: false, is_locked: true}) +      clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + +      message = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => [remote_user.follower_address], +        "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +        "type" => "Create", +        "object" => %{ +          "content" => "I don't like randos following me", +          "type" => "Note", +          "attributedTo" => remote_user.ap_id, +          "inReplyTo" => nil +        }, +        "actor" => remote_user.ap_id +      } + +      refute User.following?(bot, remote_user) + +      assert User.get_follow_requests(remote_user) |> length == 0 + +      FollowBotPolicy.filter(message) + +      assert User.get_follow_requests(remote_user) |> length == 0 +    end +  end +end diff --git a/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs new file mode 100644 index 000000000..13415bb79 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do +  use Oban.Testing, repo: Pleroma.Repo +  use Pleroma.DataCase + +  alias Pleroma.Web.ActivityPub.Transmogrifier +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "it sets the sensitive property with relevant hashtags" do +    user = insert(:user) + +    {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) +    {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + +    assert modified["object"]["sensitive"] +  end + +  test "it doesn't sets the sensitive property with irrelevant hashtags" do +    user = insert(:user) + +    {:ok, activity} = CommonAPI.post(user, %{status: "#cofe hey"}) +    {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + +    refute modified["object"]["sensitive"] +  end +end diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs index f48e5b39b..5c0aff26e 100644 --- a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs @@ -75,10 +75,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do        local_message = build_local_message()        assert SimplePolicy.filter(media_message) == -               {:ok, -                media_message -                |> put_in(["object", "tag"], ["foo", "nsfw"]) -                |> put_in(["object", "sensitive"], true)} +               {:ok, put_in(media_message, ["object", "sensitive"], true)}        assert SimplePolicy.filter(local_message) == {:ok, local_message}      end @@ -89,10 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do        local_message = build_local_message()        assert SimplePolicy.filter(media_message) == -               {:ok, -                media_message -                |> put_in(["object", "tag"], ["foo", "nsfw"]) -                |> put_in(["object", "sensitive"], true)} +               {:ok, put_in(media_message, ["object", "sensitive"], true)}        assert SimplePolicy.filter(local_message) == {:ok, local_message}      end diff --git a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs index 66e98b7ee..faaadff79 100644 --- a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs @@ -114,7 +114,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do        except_message = %{          "actor" => actor.ap_id,          "type" => "Create", -        "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true} +        "object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true}        }        assert TagPolicy.filter(message) == {:ok, except_message} diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 7c1eef7e0..61d308b97 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -68,7 +68,12 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do        clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy])        expected = %{ -        mrf_policies: ["NoOpPolicy"], +        mrf_policies: ["NoOpPolicy", "HashtagPolicy"], +        mrf_hashtag: %{ +          federated_timeline_removal: [], +          reject: [], +          sensitive: ["nsfw"] +        },          exclusions: false        } @@ -79,8 +84,13 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do        clear_config([:mrf, :policies], [MRFModuleMock])        expected = %{ -        mrf_policies: ["MRFModuleMock"], +        mrf_policies: ["MRFModuleMock", "HashtagPolicy"],          mrf_module_mock: "some config data", +        mrf_hashtag: %{ +          federated_timeline_removal: [], +          reject: [], +          sensitive: ["nsfw"] +        },          exclusions: false        } diff --git a/test/pleroma/web/activity_pub/pipeline_test.exs b/test/pleroma/web/activity_pub/pipeline_test.exs index 52fa933ee..e606fa3d1 100644 --- a/test/pleroma/web/activity_pub/pipeline_test.exs +++ b/test/pleroma/web/activity_pub/pipeline_test.exs @@ -25,9 +25,6 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do        MRFMock        |> expect(:pipeline_filter, fn o, m -> {:ok, o, m} end) -      ActivityPubMock -      |> expect(:persist, fn o, m -> {:ok, o, m} end) -        SideEffectsMock        |> expect(:handle, fn o, m -> {:ok, o, m} end)        |> expect(:handle_after_transaction, fn m -> m end) @@ -42,6 +39,9 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do        activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} +      ActivityPubMock +      |> expect(:persist, fn _, m -> {:ok, activity, m} end) +        FederatorMock        |> expect(:publish, fn ^activity_with_object -> :ok end) @@ -50,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do        assert {:ok, ^activity, ^meta} =                 Pleroma.Web.ActivityPub.Pipeline.common_pipeline( -                 activity, +                 activity.data,                   meta                 )      end @@ -59,6 +59,9 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do        activity = insert(:note_activity)        meta = [local: true] +      ActivityPubMock +      |> expect(:persist, fn _, m -> {:ok, activity, m} end) +        FederatorMock        |> expect(:publish, fn ^activity -> :ok end) @@ -66,29 +69,35 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do        |> expect(:get, fn [:instance, :federating] -> true end)        assert {:ok, ^activity, ^meta} = -               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) +               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta)      end      test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do        activity = insert(:note_activity)        meta = [local: false] +      ActivityPubMock +      |> expect(:persist, fn _, m -> {:ok, activity, m} end) +        ConfigMock        |> expect(:get, fn [:instance, :federating] -> true end)        assert {:ok, ^activity, ^meta} = -               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) +               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta)      end      test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do        activity = insert(:note_activity)        meta = [local: true] +      ActivityPubMock +      |> expect(:persist, fn _, m -> {:ok, activity, m} end) +        ConfigMock        |> expect(:get, fn [:instance, :federating] -> false end)        assert {:ok, ^activity, ^meta} = -               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) +               Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta)      end    end  end diff --git a/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs new file mode 100644 index 000000000..fc7757125 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs @@ -0,0 +1,172 @@ +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AddRemoveHandlingTest do +  use Oban.Testing, repo: Pleroma.Repo +  use Pleroma.DataCase, async: true + +  require Pleroma.Constants + +  import Pleroma.Factory + +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  test "it accepts Add/Remove activities" do +    user = +      "test/fixtures/users_mock/user.json" +      |> File.read!() +      |> String.replace("{{nickname}}", "lain") + +    object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + +    object = +      "test/fixtures/statuses/note.json" +      |> File.read!() +      |> String.replace("{{nickname}}", "lain") +      |> String.replace("{{object_id}}", object_id) + +    object_url = "https://example.com/objects/#{object_id}" + +    actor = "https://example.com/users/lain" + +    Tesla.Mock.mock(fn +      %{ +        method: :get, +        url: ^actor +      } -> +        %Tesla.Env{ +          status: 200, +          body: user, +          headers: [{"content-type", "application/activity+json"}] +        } + +      %{ +        method: :get, +        url: ^object_url +      } -> +        %Tesla.Env{ +          status: 200, +          body: object, +          headers: [{"content-type", "application/activity+json"}] +        } + +      %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> +        %Tesla.Env{ +          status: 200, +          body: +            "test/fixtures/users_mock/masto_featured.json" +            |> File.read!() +            |> String.replace("{{domain}}", "example.com") +            |> String.replace("{{nickname}}", "lain"), +          headers: [{"content-type", "application/activity+json"}] +        } +    end) + +    message = %{ +      "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f", +      "actor" => actor, +      "object" => object_url, +      "target" => "https://example.com/users/lain/collections/featured", +      "type" => "Add", +      "to" => [Pleroma.Constants.as_public()], +      "cc" => ["https://example.com/users/lain/followers"] +    } + +    assert {:ok, activity} = Transmogrifier.handle_incoming(message) +    assert activity.data == message +    user = User.get_cached_by_ap_id(actor) +    assert user.pinned_objects[object_url] + +    remove = %{ +      "id" => "http://localhost:400/objects/d61d6733-e256-4fe1-ab13-1e369789423d", +      "actor" => actor, +      "object" => object_url, +      "target" => "https://example.com/users/lain/collections/featured", +      "type" => "Remove", +      "to" => [Pleroma.Constants.as_public()], +      "cc" => ["https://example.com/users/lain/followers"] +    } + +    assert {:ok, activity} = Transmogrifier.handle_incoming(remove) +    assert activity.data == remove + +    user = refresh_record(user) +    refute user.pinned_objects[object_url] +  end + +  test "Add/Remove activities for remote users without featured address" do +    user = insert(:user, local: false, domain: "example.com") + +    user = +      user +      |> Ecto.Changeset.change(featured_address: nil) +      |> Repo.update!() + +    %{host: host} = URI.parse(user.ap_id) + +    user_data = +      "test/fixtures/users_mock/user.json" +      |> File.read!() +      |> String.replace("{{nickname}}", user.nickname) + +    object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + +    object = +      "test/fixtures/statuses/note.json" +      |> File.read!() +      |> String.replace("{{nickname}}", user.nickname) +      |> String.replace("{{object_id}}", object_id) + +    object_url = "https://#{host}/objects/#{object_id}" + +    actor = "https://#{host}/users/#{user.nickname}" + +    featured = "https://#{host}/users/#{user.nickname}/collections/featured" + +    Tesla.Mock.mock(fn +      %{ +        method: :get, +        url: ^actor +      } -> +        %Tesla.Env{ +          status: 200, +          body: user_data, +          headers: [{"content-type", "application/activity+json"}] +        } + +      %{ +        method: :get, +        url: ^object_url +      } -> +        %Tesla.Env{ +          status: 200, +          body: object, +          headers: [{"content-type", "application/activity+json"}] +        } + +      %{method: :get, url: ^featured} -> +        %Tesla.Env{ +          status: 200, +          body: +            "test/fixtures/users_mock/masto_featured.json" +            |> File.read!() +            |> String.replace("{{domain}}", "#{host}") +            |> String.replace("{{nickname}}", user.nickname), +          headers: [{"content-type", "application/activity+json"}] +        } +    end) + +    message = %{ +      "id" => "https://#{host}/objects/d61d6733-e256-4fe1-ab13-1e369789423f", +      "actor" => actor, +      "object" => object_url, +      "target" => "https://#{host}/users/#{user.nickname}/collections/featured", +      "type" => "Add", +      "to" => [Pleroma.Constants.as_public()], +      "cc" => ["https://#{host}/users/#{user.nickname}/followers"] +    } + +    assert {:ok, activity} = Transmogrifier.handle_incoming(message) +    assert activity.data == message +    user = User.get_cached_by_ap_id(actor) +    assert user.pinned_objects[object_url] +  end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 31586abc9..deb956410 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -39,7 +39,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do        {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)        object = Object.normalize(data["object"], fetch: false) -      assert "test" in object.data["tag"] +      assert "test" in Object.tags(object) +      assert Object.hashtags(object) == ["test"]      end      test "it cleans up incoming notices which are not really DMs" do @@ -220,7 +221,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do        {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)        object = Object.normalize(data["object"], fetch: false) -      assert Enum.at(object.data["tag"], 2) == "moo" +      assert Enum.at(Object.tags(object), 2) == "moo" +      assert Object.hashtags(object) == ["moo"]      end      test "it works for incoming notices with contentMap" do diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 211e535a5..4c3fcb44a 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -153,15 +153,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        end      end -    test "it adds the sensitive property" do -      user = insert(:user) - -      {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) -      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - -      assert modified["object"]["sensitive"] -    end -      test "it adds the json-ld context and the conversation property" do        user = insert(:user) diff --git a/test/pleroma/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs index 578a4c914..c39c1b1e1 100644 --- a/test/pleroma/web/admin_api/controllers/config_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/config_controller_test.exs @@ -1410,6 +1410,82 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do                 "need_reboot" => false               }      end + +    test "custom instance thumbnail", %{conn: conn} do +      clear_config([:instance]) + +      params = %{ +        "group" => ":pleroma", +        "key" => ":instance", +        "value" => [ +          %{ +            "tuple" => [ +              ":instance_thumbnail", +              "https://example.com/media/new_thumbnail.jpg" +            ] +          } +        ] +      } + +      res = +        assert conn +               |> put_req_header("content-type", "application/json") +               |> post("/api/pleroma/admin/config", %{"configs" => [params]}) +               |> json_response_and_validate_schema(200) + +      assert res == %{ +               "configs" => [ +                 %{ +                   "db" => [":instance_thumbnail"], +                   "group" => ":pleroma", +                   "key" => ":instance", +                   "value" => params["value"] +                 } +               ], +               "need_reboot" => false +             } + +      _res = +        assert conn +               |> get("/api/v1/instance") +               |> json_response_and_validate_schema(200) + +      assert res = %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"} +    end + +    test "Concurrent Limiter", %{conn: conn} do +      clear_config([ConcurrentLimiter]) + +      params = %{ +        "group" => ":pleroma", +        "key" => "ConcurrentLimiter", +        "value" => [ +          %{ +            "tuple" => [ +              "Pleroma.Web.RichMedia.Helpers", +              [ +                %{"tuple" => [":max_running", 6]}, +                %{"tuple" => [":max_waiting", 6]} +              ] +            ] +          }, +          %{ +            "tuple" => [ +              "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy", +              [ +                %{"tuple" => [":max_running", 7]}, +                %{"tuple" => [":max_waiting", 7]} +              ] +            ] +          } +        ] +      } + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/pleroma/admin/config", %{"configs" => [params]}) +             |> json_response_and_validate_schema(200) +    end    end    describe "GET /api/pleroma/admin/config/descriptions" do diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index beb8a5d58..31319b5e5 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -44,7 +44,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") -    assert json_response(conn, 200) +    assert json_response_and_validate_schema(conn, 200)    end    test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", @@ -67,7 +67,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, good_token)          |> get(url) -      assert json_response(conn, 200) +      assert json_response_and_validate_schema(conn, 200)      end      for good_token <- [good_token1, good_token2, good_token3] do @@ -87,7 +87,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, bad_token)          |> get(url) -      assert json_response(conn, :forbidden) +      assert json_response_and_validate_schema(conn, :forbidden)      end    end @@ -131,7 +131,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          assert ModerationLog.get_log_entry_message(log_entry) ==                   "@#{admin.nickname} deleted users: @#{user.nickname}" -        assert json_response(conn, 200) == [user.nickname] +        assert json_response_and_validate_schema(conn, 200) == [user.nickname]          user = Repo.get(User, user.id)          refute user.is_active @@ -152,28 +152,30 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        user_one = insert(:user)        user_two = insert(:user) -      conn = +      response =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> delete("/api/pleroma/admin/users", %{            nicknames: [user_one.nickname, user_two.nickname]          }) +        |> json_response_and_validate_schema(200)        log_entry = Repo.one(ModerationLog)        assert ModerationLog.get_log_entry_message(log_entry) ==                 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}" -      response = json_response(conn, 200)        assert response -- [user_one.nickname, user_two.nickname] == []      end    end    describe "/api/pleroma/admin/users" do      test "Create", %{conn: conn} do -      conn = +      response =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -188,8 +190,9 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do              }            ]          }) +        |> json_response_and_validate_schema(200) +        |> Enum.map(&Map.get(&1, "type")) -      response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))        assert response == ["success", "success"]        log_entry = Repo.one(ModerationLog) @@ -203,6 +206,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -213,7 +217,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -232,6 +236,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -242,7 +247,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -261,6 +266,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -276,7 +282,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -307,7 +313,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") -      assert user_response(user) == json_response(conn, 200) +      assert user_response(user) == json_response_and_validate_schema(conn, 200)      end      test "when the user doesn't exist", %{conn: conn} do @@ -315,7 +321,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") -      assert %{"error" => "Not found"} == json_response(conn, 404) +      assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)      end    end @@ -326,6 +332,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn        |> put_req_header("accept", "application/json") +      |> put_req_header("content-type", "application/json")        |> post("/api/pleroma/admin/users/follow", %{          "follower" => follower.nickname,          "followed" => user.nickname @@ -352,6 +359,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn        |> put_req_header("accept", "application/json") +      |> put_req_header("content-type", "application/json")        |> post("/api/pleroma/admin/users/unfollow", %{          "follower" => follower.nickname,          "followed" => user.nickname @@ -395,7 +403,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 3,                 "page_size" => 50,                 "users" => users @@ -410,7 +418,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users1} =                 conn                 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users1) == 10        assert service1 not in users1 @@ -418,7 +426,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users2} =                 conn                 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users2) == 10        assert service1 not in users2 @@ -426,7 +434,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users3} =                 conn                 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users3) == 6        assert service1 not in users3 @@ -437,7 +445,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?page=2") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => [] @@ -449,7 +457,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=bo") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user, %{"local" => true})] @@ -462,7 +470,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=domain.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -475,7 +483,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -488,7 +496,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?name=display") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -501,7 +509,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -514,7 +522,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") -      assert json_response(conn1, 200) == %{ +      assert json_response_and_validate_schema(conn1, 200) == %{                 "count" => 2,                 "page_size" => 1,                 "users" => [user_response(user)] @@ -522,7 +530,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") -      assert json_response(conn2, 200) == %{ +      assert json_response_and_validate_schema(conn2, 200) == %{                 "count" => 2,                 "page_size" => 1,                 "users" => [user_response(user2)] @@ -542,7 +550,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, token)          |> get("/api/pleroma/admin/users?query=bo&filters=local") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -570,7 +578,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 3,                 "page_size" => 50,                 "users" => users @@ -587,7 +595,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        result =          conn          |> get("/api/pleroma/admin/users?filters=unconfirmed") -        |> json_response(200) +        |> json_response_and_validate_schema(200)        users =          Enum.map([old_user, sad_user], fn user -> @@ -620,7 +628,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          )        ] -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => users @@ -647,7 +655,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => users @@ -661,7 +669,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [ @@ -682,8 +690,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Person"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Person"]}) +        |> json_response_and_validate_schema(200)        users =          [ @@ -705,8 +713,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Person", "Service"]}) +        |> json_response_and_validate_schema(200)        users =          [ @@ -728,8 +736,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Service"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Service"]}) +        |> json_response_and_validate_schema(200)        users = [user_response(user_service, %{"actor_type" => "Service"})] @@ -751,7 +759,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => users @@ -776,7 +784,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do                   %{"id" => ^admin_id},                   %{"id" => ^user_id}                 ] -             } = json_response(conn, 200) +             } = json_response_and_validate_schema(conn, 200)      end      test "it works with multiple filters" do @@ -793,7 +801,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, token)          |> get("/api/pleroma/admin/users?filters=deactivated,external") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -805,7 +813,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [ @@ -820,13 +828,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_active: false)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/activate",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_active"]) == [true, true]      log_entry = Repo.one(ModerationLog) @@ -840,13 +849,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_active: true)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/deactivate",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_active"]) == [false, false]      log_entry = Repo.one(ModerationLog) @@ -860,13 +870,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_approved: false)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/approve",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_approved"]) == [true, true]      log_entry = Repo.one(ModerationLog) @@ -878,9 +889,12 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do    test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do      user = insert(:user) -    conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") +    conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") -    assert json_response(conn, 200) == +    assert json_response_and_validate_schema(conn, 200) ==               user_response(                 user,                 %{"is_active" => !user.is_active} diff --git a/test/pleroma/web/common_api/utils_test.exs b/test/pleroma/web/common_api/utils_test.exs index f2043e152..b0e567ff0 100644 --- a/test/pleroma/web/common_api/utils_test.exs +++ b/test/pleroma/web/common_api/utils_test.exs @@ -168,6 +168,123 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do      end    end +  describe "format_input/3 with markdown" do +    test "Paragraph" do +      code = ~s[Hello\n\nWorld!] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == "<p>Hello</p><p>World!</p>" +    end + +    test "links" do +      code = "https://en.wikipedia.org/wiki/Animal_Crossing_(video_game)" +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><a href="#{code}">#{code}</a></p>] + +      code = "https://github.com/pragdave/earmark/" +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><a href="#{code}">#{code}</a></p>] +    end + +    test "link with local mention" do +      insert(:user, %{nickname: "lain"}) + +      code = "https://example.com/@lain" +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><a href="#{code}">#{code}</a></p>] +    end + +    test "local mentions" do +      mario = insert(:user, %{nickname: "mario"}) +      luigi = insert(:user, %{nickname: "luigi"}) + +      code = "@mario @luigi yo what's up?" +      {result, _, []} = Utils.format_input(code, "text/markdown") + +      assert result == +               ~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{ +                 mario.ap_id +               }" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{ +                 luigi.id +               }" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo what’s up?</p>] +    end + +    test "remote mentions" do +      mario = insert(:user, %{nickname: "mario@mushroom.world", local: false}) +      luigi = insert(:user, %{nickname: "luigi@mushroom.world", local: false}) + +      code = "@mario@mushroom.world @luigi@mushroom.world yo what's up?" +      {result, _, []} = Utils.format_input(code, "text/markdown") + +      assert result == +               ~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{ +                 mario.ap_id +               }" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{ +                 luigi.id +               }" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo what’s up?</p>] +    end + +    test "raw HTML" do +      code = ~s[<a href="http://example.org/">OwO</a><!-- what's this?-->] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<a href="http://example.org/">OwO</a>] +    end + +    test "rulers" do +      code = ~s[before\n\n-----\n\nafter] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == "<p>before</p><hr/><p>after</p>" +    end + +    test "blockquote" do +      code = ~s[> whoms't are you quoting?] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == "<blockquote><p>whoms’t are you quoting?</p></blockquote>" +    end + +    test "code" do +      code = ~s[`mix`] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><code class="inline">mix</code></p>] + +      code = ~s[``mix``] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><code class="inline">mix</code></p>] + +      code = ~s[```\nputs "Hello World"\n```] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<pre><code>puts "Hello World"</code></pre>] + +      code = ~s[    <div>\n    </div>] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<pre><code><div>\n</div></code></pre>] +    end + +    test "lists" do +      code = ~s[- one\n- two\n- three\n- four] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == "<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>" + +      code = ~s[1. one\n2. two\n3. three\n4. four\n] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == "<ol><li>one</li><li>two</li><li>three</li><li>four</li></ol>" +    end + +    test "delegated renderers" do +      code = ~s[*aaaa~*] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><em>aaaa~</em></p>] + +      code = ~s[**aaaa~**] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><strong>aaaa~</strong></p>] + +      # strikethrough +      code = ~s[~~aaaa~~~] +      {result, [], []} = Utils.format_input(code, "text/markdown") +      assert result == ~s[<p><del>aaaa</del>~</p>] +    end +  end +    describe "context_to_conversation_id" do      test "creates a mapping object" do        conversation_id = Utils.context_to_conversation_id("random context") diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index adfe58def..be94c93c2 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -25,6 +25,11 @@ defmodule Pleroma.Web.CommonAPITest do    require Pleroma.Constants +  setup_all do +    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end +    setup do: clear_config([:instance, :safe_dm_mentions])    setup do: clear_config([:instance, :limit])    setup do: clear_config([:instance, :max_pinned_statuses]) @@ -493,7 +498,7 @@ defmodule Pleroma.Web.CommonAPITest do      object = Object.normalize(activity, fetch: false) -    assert object.data["tag"] == ["2hu"] +    assert Object.tags(object) == ["2hu"]    end    test "it adds emoji in the object" do @@ -517,6 +522,27 @@ defmodule Pleroma.Web.CommonAPITest do        assert url == "#{Pleroma.Web.base_url()}/emoji/blank.png"      end +    test "it copies emoji from the subject of the parent post" do +      %Object{} = +        object = +        Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", +          fetch: true +        ) + +      activity = Activity.get_create_by_object_ap_id(object.data["id"]) +      user = insert(:user) + +      {:ok, reply_activity} = +        CommonAPI.post(user, %{ +          in_reply_to_id: activity.id, +          status: ":joker_disapprove:", +          spoiler_text: ":joker_smile:" +        }) + +      assert Object.normalize(reply_activity).data["emoji"][":joker_smile:"] +      refute Object.normalize(reply_activity).data["emoji"][":joker_disapprove:"] +    end +      test "deactivated users can't post" do        user = insert(:user, is_active: false)        assert {:error, _} = CommonAPI.post(user, %{status: "ye"}) @@ -571,7 +597,7 @@ defmodule Pleroma.Web.CommonAPITest do        object = Object.normalize(activity, fetch: false) -      assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" +      assert object.data["content"] == "<p><b>2hu</b></p>"        assert object.data["source"] == post      end @@ -801,13 +827,17 @@ defmodule Pleroma.Web.CommonAPITest do        [user: user, activity: activity]      end +    test "activity not found error", %{user: user} do +      assert {:error, :not_found} = CommonAPI.pin("id", user) +    end +      test "pin status", %{user: user, activity: activity} do        assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) -      id = activity.id +      %{data: %{"id" => object_id}} = Object.normalize(activity)        user = refresh_record(user) -      assert %User{pinned_activities: [^id]} = user +      assert user.pinned_objects |> Map.keys() == [object_id]      end      test "pin poll", %{user: user} do @@ -819,10 +849,11 @@ defmodule Pleroma.Web.CommonAPITest do        assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) -      id = activity.id +      %{data: %{"id" => object_id}} = Object.normalize(activity) +        user = refresh_record(user) -      assert %User{pinned_activities: [^id]} = user +      assert user.pinned_objects |> Map.keys() == [object_id]      end      test "unlisted statuses can be pinned", %{user: user} do @@ -833,7 +864,7 @@ defmodule Pleroma.Web.CommonAPITest do      test "only self-authored can be pinned", %{activity: activity} do        user = insert(:user) -      assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user) +      assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user)      end      test "max pinned statuses", %{user: user, activity: activity_one} do @@ -843,8 +874,12 @@ defmodule Pleroma.Web.CommonAPITest do        user = refresh_record(user) -      assert {:error, "You have already pinned the maximum number of statuses"} = -               CommonAPI.pin(activity_two.id, user) +      assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user) +    end + +    test "only public can be pinned", %{user: user} do +      {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"}) +      {:error, :visibility_error} = CommonAPI.pin(activity.id, user)      end      test "unpin status", %{user: user, activity: activity} do @@ -858,7 +893,7 @@ defmodule Pleroma.Web.CommonAPITest do        user = refresh_record(user) -      assert %User{pinned_activities: []} = user +      assert user.pinned_objects == %{}      end      test "should unpin when deleting a status", %{user: user, activity: activity} do @@ -870,7 +905,40 @@ defmodule Pleroma.Web.CommonAPITest do        user = refresh_record(user) -      assert %User{pinned_activities: []} = user +      assert user.pinned_objects == %{} +    end + +    test "ephemeral activity won't be deleted if was pinned", %{user: user} do +      {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601}) + +      assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + +      {:ok, _activity} = CommonAPI.pin(activity.id, user) +      refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + +      user = refresh_record(user) +      {:ok, _} = CommonAPI.unpin(activity.id, user) + +      # recreates expiration job on unpin +      assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) +    end + +    test "ephemeral activity deletion job won't be deleted on pinning error", %{ +      user: user, +      activity: activity +    } do +      clear_config([:instance, :max_pinned_statuses], 1) + +      {:ok, _activity} = CommonAPI.pin(activity.id, user) + +      {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601}) + +      assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id) + +      user = refresh_record(user) +      {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user) + +      assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)      end    end diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index e76c2760d..99ad87d05 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -358,7 +358,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do        assert activity.data["cc"] == []      end -    @tag :skip      test "discloses application metadata when enabled" do        user = insert(:user, disclose_client: true)        %{user: _user, token: token, conn: conn} = oauth_access(["write:statuses"], user: user) @@ -378,6 +377,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do          })        assert %{ +               "content" => "cofe is my copilot" +             } = json_response_and_validate_schema(result, 200) + +      activity = result.assigns.activity.id + +      result = +        conn +        |> get("api/v1/statuses/#{activity}") + +      assert %{                 "content" => "cofe is my copilot",                 "application" => %{                   "name" => ^app_name, @@ -397,6 +406,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do            "status" => "club mate is my wingman"          }) +      assert %{"content" => "club mate is my wingman"} = +               json_response_and_validate_schema(result, 200) + +      activity = result.assigns.activity.id + +      result = +        conn +        |> get("api/v1/statuses/#{activity}") +        assert %{                 "content" => "club mate is my wingman",                 "application" => nil @@ -1191,20 +1209,27 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do      setup do: clear_config([:instance, :max_pinned_statuses], 1)      test "pin status", %{conn: conn, user: user, activity: activity} do -      id_str = to_string(activity.id) +      id = activity.id -      assert %{"id" => ^id_str, "pinned" => true} = +      assert %{"id" => ^id, "pinned" => true} =                 conn                 |> put_req_header("content-type", "application/json")                 |> post("/api/v1/statuses/#{activity.id}/pin")                 |> json_response_and_validate_schema(200) -      assert [%{"id" => ^id_str, "pinned" => true}] = +      assert [%{"id" => ^id, "pinned" => true}] =                 conn                 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")                 |> json_response_and_validate_schema(200)      end +    test "non authenticated user", %{activity: activity} do +      assert build_conn() +             |> put_req_header("content-type", "application/json") +             |> post("/api/v1/statuses/#{activity.id}/pin") +             |> json_response(403) == %{"error" => "Invalid credentials."} +    end +      test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do        {:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) @@ -1213,7 +1238,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do          |> put_req_header("content-type", "application/json")          |> post("/api/v1/statuses/#{dm.id}/pin") -      assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"} +      assert json_response_and_validate_schema(conn, 422) == %{ +               "error" => "Non-public status cannot be pinned" +             } +    end + +    test "pin by another user", %{activity: activity} do +      %{conn: conn} = oauth_access(["write:accounts"]) + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/v1/statuses/#{activity.id}/pin") +             |> json_response(422) == %{"error" => "Someone else's status cannot be pinned"}      end      test "unpin status", %{conn: conn, user: user, activity: activity} do @@ -1234,13 +1270,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do                 |> json_response_and_validate_schema(200)      end -    test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do -      conn = -        conn -        |> put_req_header("content-type", "application/json") -        |> post("/api/v1/statuses/1/unpin") - -      assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"} +    test "/unpin: returns 404 error when activity doesn't exist", %{conn: conn} do +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/v1/statuses/1/unpin") +             |> json_response_and_validate_schema(404) == %{"error" => "Record not found"}      end      test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index 2de3afc4f..fbea25079 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -262,8 +262,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do        mentions: [],        tags: [          %{ -          name: "#{object_data["tag"]}", -          url: "http://localhost:4001/tag/#{object_data["tag"]}" +          name: "#{hd(object_data["tag"])}", +          url: "http://localhost:4001/tag/#{hd(object_data["tag"])}"          }        ],        application: nil, @@ -286,7 +286,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do          direct_conversation_id: nil,          thread_muted: false,          emoji_reactions: [], -        parent_visible: false +        parent_visible: false, +        pinned_at: nil        }      } diff --git a/test/pleroma/web/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs index 7411d0a7a..b5ee6328d 100644 --- a/test/pleroma/web/media_proxy_test.exs +++ b/test/pleroma/web/media_proxy_test.exs @@ -11,8 +11,7 @@ defmodule Pleroma.Web.MediaProxyTest do    alias Pleroma.Web.MediaProxy    defp decode_result(encoded) do -    [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") -    {:ok, decoded} = MediaProxy.decode_url(sig, base64) +    {:ok, decoded} = MediaProxy.decode_url(encoded)      decoded    end diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs index 312500feb..0fdd5b8e9 100644 --- a/test/pleroma/web/o_auth/o_auth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -805,10 +805,12 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            "client_secret" => app.client_secret          }) -      assert %{"access_token" => token} = json_response(conn, 200) +      assert %{"id" => id, "access_token" => access_token} = json_response(conn, 200) -      token = Repo.get_by(Token, token: token) +      token = Repo.get_by(Token, token: access_token)        assert token +      assert token.id == id +      assert token.token == access_token        assert token.scopes == app.scopes      end diff --git a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs index f389c272b..fa3b29006 100644 --- a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs +++ b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs @@ -27,6 +27,16 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do              body: File.read!("test/fixtures/tesla_mock/status.emelie.json")            } +        %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> +          %Tesla.Env{ +            status: 200, +            headers: [{"content-type", "application/activity+json"}], +            body: +              File.read!("test/fixtures/users_mock/masto_featured.json") +              |> String.replace("{{domain}}", "mastodon.social") +              |> String.replace("{{nickname}}", "emelie") +          } +          %{method: :get, url: "https://mastodon.social/users/emelie"} ->            %Tesla.Env{              status: 200, @@ -52,6 +62,16 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do              headers: [{"content-type", "application/activity+json"}],              body: File.read!("test/fixtures/tesla_mock/emelie.json")            } + +        %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> +          %Tesla.Env{ +            status: 200, +            headers: [{"content-type", "application/activity+json"}], +            body: +              File.read!("test/fixtures/users_mock/masto_featured.json") +              |> String.replace("{{domain}}", "mastodon.social") +              |> String.replace("{{nickname}}", "emelie") +          }        end)        response = @@ -70,6 +90,16 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do              headers: [{"content-type", "application/activity+json"}],              body: File.read!("test/fixtures/tesla_mock/emelie.json")            } + +        %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> +          %Tesla.Env{ +            status: 200, +            headers: [{"content-type", "application/activity+json"}], +            body: +              File.read!("test/fixtures/users_mock/masto_featured.json") +              |> String.replace("{{domain}}", "mastodon.social") +              |> String.replace("{{nickname}}", "emelie") +          }        end)        user = insert(:user) diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index 84477d5a1..2d7b4a40b 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -45,6 +45,26 @@ defmodule Pleroma.Web.WebFingerTest do        assert {:error, _} = WebFinger.finger("pleroma.social")      end +    test "returns error when there is no content-type header" do +      Tesla.Mock.mock(fn +        %{url: "http://social.heldscal.la/.well-known/host-meta"} -> +          {:ok, +           %Tesla.Env{ +             status: 200, +             body: File.read!("test/fixtures/tesla_mock/social.heldscal.la_host_meta") +           }} + +        %{ +          url: +            "https://social.heldscal.la/.well-known/webfinger?resource=acct:invalid_content@social.heldscal.la" +        } -> +          {:ok, %Tesla.Env{status: 200, body: ""}} +      end) + +      user = "invalid_content@social.heldscal.la" +      assert {:error, {:content_type, nil}} = WebFinger.finger(user) +    end +      test "returns error when fails parse xml or json" do        user = "invalid_content@social.heldscal.la"        assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user) @@ -113,5 +133,52 @@ defmodule Pleroma.Web.WebFingerTest do        ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"        {:ok, _data} = WebFinger.finger(ap_id)      end + +    test "respects json content-type" do +      Tesla.Mock.mock(fn +        %{ +          url: +            "https://mastodon.social/.well-known/webfinger?resource=acct:emelie@mastodon.social" +        } -> +          {:ok, +           %Tesla.Env{ +             status: 200, +             body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json"), +             headers: [{"content-type", "application/jrd+json"}] +           }} + +        %{url: "http://mastodon.social/.well-known/host-meta"} -> +          {:ok, +           %Tesla.Env{ +             status: 200, +             body: File.read!("test/fixtures/tesla_mock/mastodon.social_host_meta") +           }} +      end) + +      {:ok, _data} = WebFinger.finger("emelie@mastodon.social") +    end + +    test "respects xml content-type" do +      Tesla.Mock.mock(fn +        %{ +          url: "https://pawoo.net/.well-known/webfinger?resource=acct:pekorino@pawoo.net" +        } -> +          {:ok, +           %Tesla.Env{ +             status: 200, +             body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml"), +             headers: [{"content-type", "application/xrd+xml"}] +           }} + +        %{url: "http://pawoo.net/.well-known/host-meta"} -> +          {:ok, +           %Tesla.Env{ +             status: 200, +             body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") +           }} +      end) + +      {:ok, _data} = WebFinger.finger("pekorino@pawoo.net") +    end    end  end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 953aa010a..deee98599 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -67,13 +67,11 @@ defmodule Pleroma.Web.ConnCase do        end        defp json_response_and_validate_schema( -             %{ -               private: %{ -                 open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec} -               } -             } = conn, +             %{private: %{operation_id: op_id}} = conn,               status             ) do +        {spec, lookup} = OpenApiSpex.Plug.PutApiSpec.get_spec_and_operation_lookup(conn) +          content_type =            conn            |> Plug.Conn.get_resp_header("content-type") diff --git a/test/support/factory.ex b/test/support/factory.ex index af4fff45b..5c4e65c81 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -4,6 +4,9 @@  defmodule Pleroma.Factory do    use ExMachina.Ecto, repo: Pleroma.Repo + +  require Pleroma.Constants +    alias Pleroma.Object    alias Pleroma.User @@ -41,23 +44,27 @@ defmodule Pleroma.Factory do      urls =        if attrs[:local] == false do -        base_domain = Enum.random(["domain1.com", "domain2.com", "domain3.com"]) +        base_domain = attrs[:domain] || Enum.random(["domain1.com", "domain2.com", "domain3.com"])          ap_id = "https://#{base_domain}/users/#{user.nickname}"          %{            ap_id: ap_id,            follower_address: ap_id <> "/followers", -          following_address: ap_id <> "/following" +          following_address: ap_id <> "/following", +          featured_address: ap_id <> "/collections/featured"          }        else          %{            ap_id: User.ap_id(user),            follower_address: User.ap_followers(user), -          following_address: User.ap_following(user) +          following_address: User.ap_following(user), +          featured_address: User.ap_featured_collection(user)          }        end +    attrs = Map.delete(attrs, :domain) +      user      |> Map.put(:raw_bio, user.bio)      |> Map.merge(urls) @@ -221,6 +228,45 @@ defmodule Pleroma.Factory do      }    end +  def add_activity_factory(attrs \\ %{}) do +    featured_collection_activity(attrs, "Add") +  end + +  def remove_activity_factor(attrs \\ %{}) do +    featured_collection_activity(attrs, "Remove") +  end + +  defp featured_collection_activity(attrs, type) do +    user = attrs[:user] || insert(:user) +    note = attrs[:note] || insert(:note, user: user) + +    data_attrs = +      attrs +      |> Map.get(:data_attrs, %{}) +      |> Map.put(:type, type) + +    attrs = Map.drop(attrs, [:user, :note, :data_attrs]) + +    data = +      %{ +        "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), +        "target" => user.featured_address, +        "object" => note.data["object"], +        "actor" => note.data["actor"], +        "type" => "Add", +        "to" => [Pleroma.Constants.as_public()], +        "cc" => [user.follower_address] +      } +      |> Map.merge(data_attrs) + +    %Pleroma.Activity{ +      data: data, +      actor: data["actor"], +      recipients: data["to"] +    } +    |> Map.merge(attrs) +  end +    def note_activity_factory(attrs \\ %{}) do      user = attrs[:user] || insert(:user)      note = attrs[:note] || insert(:note, user: user) diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 1328d6225..8807c2d14 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -89,6 +89,18 @@ defmodule HttpRequestMock do       }}    end +  def get("https://mastodon.sdf.org/users/rinpatch/collections/featured", _, _, _) do +    {:ok, +     %Tesla.Env{ +       status: 200, +       body: +         File.read!("test/fixtures/users_mock/masto_featured.json") +         |> String.replace("{{domain}}", "mastodon.sdf.org") +         |> String.replace("{{nickname}}", "rinpatch"), +       headers: [{"content-type", "application/activity+json"}] +     }} +  end +    def get("https://patch.cx/objects/tesla_mock/poll_attachment", _, _, _) do      {:ok,       %Tesla.Env{ @@ -122,7 +134,7 @@ defmodule HttpRequestMock do       %Tesla.Env{         status: 200,         body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"), -       headers: activitypub_object_headers() +       headers: [{"content-type", "application/jrd+json"}]       }}    end @@ -187,7 +199,8 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml") +       body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml"), +       headers: [{"content-type", "application/xrd+xml"}]       }}    end @@ -526,22 +539,6 @@ defmodule HttpRequestMock do       }}    end -  def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do -    {:ok, -     %Tesla.Env{ -       status: 200, -       body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") -     }} -  end - -  def get("https://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do -    {:ok, -     %Tesla.Env{ -       status: 200, -       body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") -     }} -  end -    def get("http://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do      {:ok,       %Tesla.Env{ @@ -786,7 +783,8 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml") +       body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml"), +       headers: [{"content-type", "application/xrd+xml"}]       }}    end @@ -796,7 +794,7 @@ defmodule HttpRequestMock do          _,          [{"accept", "application/xrd+xml,application/jrd+json"}]        ) do -    {:ok, %Tesla.Env{status: 200, body: ""}} +    {:ok, %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/jrd+json"}]}}    end    def get("http://framatube.org/.well-known/host-meta", _, _, _) do @@ -816,7 +814,7 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       headers: [{"content-type", "application/json"}], +       headers: [{"content-type", "application/jrd+json"}],         body: File.read!("test/fixtures/tesla_mock/framasoft@framatube.org.json")       }}    end @@ -876,7 +874,7 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       headers: [{"content-type", "application/json"}], +       headers: [{"content-type", "application/jrd+json"}],         body: File.read!("test/fixtures/tesla_mock/kaniini@gerzilla.de.json")       }}    end @@ -919,6 +917,18 @@ defmodule HttpRequestMock do       }}    end +  def get("https://mastodon.social/users/lambadalambda/collections/featured", _, _, _) do +    {:ok, +     %Tesla.Env{ +       status: 200, +       body: +         File.read!("test/fixtures/users_mock/masto_featured.json") +         |> String.replace("{{domain}}", "mastodon.social") +         |> String.replace("{{nickname}}", "lambadalambda"), +       headers: activitypub_object_headers() +     }} +  end +    def get("https://apfed.club/channel/indio", _, _, _) do      {:ok,       %Tesla.Env{ @@ -1074,7 +1084,8 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       body: File.read!("test/fixtures/lain.xml") +       body: File.read!("test/fixtures/lain.xml"), +       headers: [{"content-type", "application/xrd+xml"}]       }}    end @@ -1087,7 +1098,16 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       body: File.read!("test/fixtures/lain.xml") +       body: File.read!("test/fixtures/lain.xml"), +       headers: [{"content-type", "application/xrd+xml"}] +     }} +  end + +  def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do +    {:ok, +     %Tesla.Env{ +       status: 200, +       body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml")       }}    end @@ -1153,7 +1173,8 @@ defmodule HttpRequestMock do      {:ok,       %Tesla.Env{         status: 200, -       body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml") +       body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml"), +       headers: [{"content-type", "application/xrd+xml"}]       }}    end @@ -1281,6 +1302,15 @@ defmodule HttpRequestMock do       }}    end +  def get("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", _, _, _) do +    {:ok, +     %Tesla.Env{ +       status: 200, +       body: File.read!("test/fixtures/tesla_mock/emoji-in-summary.json"), +       headers: activitypub_object_headers() +     }} +  end +    def get(url, query, body, headers) do      {:error,       "Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{ | 
