Merge branch 'develop' into 'remove-twitter-api'
[akkoma] / test / web / pleroma_api / controllers / emoji_api_controller_test.exs
index 4b9f5cf9a1fb187842b9ba7f97e254baff6e5b28..d343256fe2652473e5e4dd8f9d9668808460b806 100644 (file)
@@ -8,216 +8,298 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
   import Tesla.Mock
   import Pleroma.Factory
 
-  @emoji_dir_path Path.join(
-                    Pleroma.Config.get!([:instance, :static_dir]),
-                    "emoji"
-                  )
+  @emoji_path Path.join(
+                Pleroma.Config.get!([:instance, :static_dir]),
+                "emoji"
+              )
+  setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
+
+  setup do
+    admin = insert(:user, is_admin: true)
+    token = insert(:oauth_admin_token, user: admin)
 
-  clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
-    Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
+    admin_conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> assign(:token, token)
+
+    Pleroma.Emoji.reload()
+    {:ok, %{admin_conn: admin_conn}}
   end
 
-  test "shared & non-shared pack information in list_packs is ok" do
-    conn = build_conn()
-    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
+  test "GET /api/pleroma/emoji/packs", %{conn: conn} do
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
 
-    assert Map.has_key?(resp, "test_pack")
+    shared = resp["test_pack"]
+    assert shared["files"] == %{"blank" => "blank.png"}
+    assert Map.has_key?(shared["pack"], "download-sha256")
+    assert shared["pack"]["can-download"]
+    assert shared["pack"]["share-files"]
 
-    pack = resp["test_pack"]
+    non_shared = resp["test_pack_nonshared"]
+    assert non_shared["pack"]["share-files"] == false
+    assert non_shared["pack"]["can-download"] == false
+  end
 
-    assert Map.has_key?(pack["pack"], "download-sha256")
-    assert pack["pack"]["can-download"]
+  describe "GET /api/pleroma/emoji/packs/remote" do
+    test "shareable instance", %{admin_conn: admin_conn, conn: conn} do
+      resp =
+        conn
+        |> get("/api/pleroma/emoji/packs")
+        |> json_response(200)
 
-    assert pack["files"] == %{"blank" => "blank.png"}
+      mock(fn
+        %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
 
-    # Non-shared pack
+        %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: ["shareable_emoji_packs"]}})
 
-    assert Map.has_key?(resp, "test_pack_nonshared")
+        %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} ->
+          json(resp)
+      end)
 
-    pack = resp["test_pack_nonshared"]
+      assert admin_conn
+             |> get("/api/pleroma/emoji/packs/remote", %{
+               url: "https://example.com"
+             })
+             |> json_response(200) == resp
+    end
 
-    refute pack["pack"]["shared"]
-    refute pack["pack"]["can-download"]
-  end
+    test "non shareable instance", %{admin_conn: admin_conn} do
+      mock(fn
+        %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
 
-  test "listing remote packs" do
-    admin = insert(:user, is_admin: true)
-    %{conn: conn} = oauth_access(["admin:write"], user: admin)
+        %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: []}})
+      end)
 
-    resp =
-      build_conn()
-      |> get(emoji_api_path(conn, :list_packs))
-      |> json_response(200)
+      assert admin_conn
+             |> get("/api/pleroma/emoji/packs/remote", %{url: "https://example.com"})
+             |> json_response(500) == %{
+               "error" => "The requested instance does not support sharing emoji packs"
+             }
+    end
+  end
 
-    mock(fn
-      %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
-        json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+  describe "GET /api/pleroma/emoji/packs/:name/archive" do
+    test "download shared pack", %{conn: conn} do
+      resp =
+        conn
+        |> get("/api/pleroma/emoji/packs/test_pack/archive")
+        |> response(200)
 
-      %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
-        json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+      {:ok, arch} = :zip.unzip(resp, [:memory])
 
-      %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} ->
-        json(resp)
-    end)
+      assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
+      assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
+    end
+
+    test "non existing pack", %{conn: conn} do
+      assert conn
+             |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive")
+             |> json_response(:not_found) == %{
+               "error" => "Pack test_pack_for_import does not exist"
+             }
+    end
 
-    assert conn
-           |> post(emoji_api_path(conn, :list_from), %{instance_address: "https://example.com"})
-           |> json_response(200) == resp
+    test "non downloadable pack", %{conn: conn} do
+      assert conn
+             |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive")
+             |> json_response(:forbidden) == %{
+               "error" =>
+                 "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
+             }
+    end
   end
 
-  test "downloading a shared pack from download_shared" do
-    conn = build_conn()
+  describe "POST /api/pleroma/emoji/packs/download" do
+    test "shared pack from remote and non shared from fallback-src", %{
+      admin_conn: admin_conn,
+      conn: conn
+    } do
+      mock(fn
+        %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
 
-    resp =
-      conn
-      |> get(emoji_api_path(conn, :download_shared, "test_pack"))
-      |> response(200)
+        %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: ["shareable_emoji_packs"]}})
 
-    {:ok, arch} = :zip.unzip(resp, [:memory])
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/test_pack"
+        } ->
+          conn
+          |> get("/api/pleroma/emoji/packs/test_pack")
+          |> json_response(200)
+          |> json()
 
-    assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
-    assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
-  end
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/test_pack/archive"
+        } ->
+          conn
+          |> get("/api/pleroma/emoji/packs/test_pack/archive")
+          |> response(200)
+          |> text()
 
-  test "downloading shared & unshared packs from another instance via download_from, deleting them" do
-    on_exit(fn ->
-      File.rm_rf!("#{@emoji_dir_path}/test_pack2")
-      File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2")
-    end)
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/test_pack_nonshared"
+        } ->
+          conn
+          |> get("/api/pleroma/emoji/packs/test_pack_nonshared")
+          |> json_response(200)
+          |> json()
 
-    mock(fn
-      %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
-        json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
+        %{
+          method: :get,
+          url: "https://nonshared-pack"
+        } ->
+          text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
+      end)
 
-      %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
-        json(%{metadata: %{features: []}})
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/download", %{
+               url: "https://example.com",
+               name: "test_pack",
+               as: "test_pack2"
+             })
+             |> json_response(200) == "ok"
 
-      %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
-        json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+      assert File.exists?("#{@emoji_path}/test_pack2/pack.json")
+      assert File.exists?("#{@emoji_path}/test_pack2/blank.png")
 
-      %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
-        json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/test_pack2")
+             |> json_response(200) == "ok"
 
-      %{
-        method: :get,
-        url: "https://example.com/api/pleroma/emoji/packs/list"
-      } ->
-        conn = build_conn()
+      refute File.exists?("#{@emoji_path}/test_pack2")
 
-        conn
-        |> get(emoji_api_path(conn, :list_packs))
-        |> json_response(200)
-        |> json()
+      assert admin_conn
+             |> post(
+               "/api/pleroma/emoji/packs/download",
+               %{
+                 url: "https://example.com",
+                 name: "test_pack_nonshared",
+                 as: "test_pack_nonshared2"
+               }
+             )
+             |> json_response(200) == "ok"
 
-      %{
-        method: :get,
-        url: "https://example.com/api/pleroma/emoji/packs/download_shared/test_pack"
-      } ->
-        conn = build_conn()
+      assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json")
+      assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png")
 
-        conn
-        |> get(emoji_api_path(conn, :download_shared, "test_pack"))
-        |> response(200)
-        |> text()
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2")
+             |> json_response(200) == "ok"
 
-      %{
-        method: :get,
-        url: "https://nonshared-pack"
-      } ->
-        text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
-    end)
+      refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
+    end
 
-    admin = insert(:user, is_admin: true)
+    test "nonshareable instance", %{admin_conn: admin_conn} do
+      mock(fn
+        %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
 
-    conn =
-      build_conn()
-      |> assign(:user, admin)
-      |> assign(:token, insert(:oauth_admin_token, user: admin, scopes: ["admin:write"]))
-
-    assert (conn
-            |> put_req_header("content-type", "application/json")
-            |> post(
-              emoji_api_path(
-                conn,
-                :download_from
-              ),
-              %{
-                instance_address: "https://old-instance",
-                pack_name: "test_pack",
-                as: "test_pack2"
-              }
-              |> Jason.encode!()
-            )
-            |> json_response(500))["error"] =~ "does not support"
-
-    assert conn
-           |> put_req_header("content-type", "application/json")
-           |> post(
-             emoji_api_path(
-               conn,
-               :download_from
-             ),
-             %{
-               instance_address: "https://example.com",
-               pack_name: "test_pack",
-               as: "test_pack2"
+        %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: []}})
+      end)
+
+      assert admin_conn
+             |> post(
+               "/api/pleroma/emoji/packs/download",
+               %{
+                 url: "https://old-instance",
+                 name: "test_pack",
+                 as: "test_pack2"
+               }
+             )
+             |> json_response(500) == %{
+               "error" => "The requested instance does not support sharing emoji packs"
              }
-             |> Jason.encode!()
-           )
-           |> json_response(200) == "ok"
-
-    assert File.exists?("#{@emoji_dir_path}/test_pack2/pack.json")
-    assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png")
-
-    assert conn
-           |> delete(emoji_api_path(conn, :delete, "test_pack2"))
-           |> json_response(200) == "ok"
-
-    refute File.exists?("#{@emoji_dir_path}/test_pack2")
-
-    # non-shared, downloaded from the fallback URL
-
-    assert conn
-           |> put_req_header("content-type", "application/json")
-           |> post(
-             emoji_api_path(
-               conn,
-               :download_from
-             ),
-             %{
-               instance_address: "https://example.com",
-               pack_name: "test_pack_nonshared",
-               as: "test_pack_nonshared2"
+    end
+
+    test "checksum fail", %{admin_conn: admin_conn} do
+      mock(fn
+        %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+        %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha"
+        } ->
+          %Tesla.Env{
+            status: 200,
+            body: Pleroma.Emoji.Pack.load_pack("pack_bad_sha") |> Jason.encode!()
+          }
+
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha/archive"
+        } ->
+          %Tesla.Env{
+            status: 200,
+            body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip")
+          }
+      end)
+
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/download", %{
+               url: "https://example.com",
+               name: "pack_bad_sha",
+               as: "pack_bad_sha2"
+             })
+             |> json_response(:internal_server_error) == %{
+               "error" => "SHA256 for the pack doesn't match the one sent by the server"
              }
-             |> Jason.encode!()
-           )
-           |> json_response(200) == "ok"
+    end
+
+    test "other error", %{admin_conn: admin_conn} do
+      mock(fn
+        %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+          json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
 
-    assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/pack.json")
-    assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png")
+        %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+          json(%{metadata: %{features: ["shareable_emoji_packs"]}})
 
-    assert conn
-           |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2"))
-           |> json_response(200) == "ok"
+        %{
+          method: :get,
+          url: "https://example.com/api/pleroma/emoji/packs/test_pack"
+        } ->
+          %Tesla.Env{
+            status: 200,
+            body: Pleroma.Emoji.Pack.load_pack("test_pack") |> Jason.encode!()
+          }
+      end)
 
-    refute File.exists?("#{@emoji_dir_path}/test_pack_nonshared2")
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/download", %{
+               url: "https://example.com",
+               name: "test_pack",
+               as: "test_pack2"
+             })
+             |> json_response(:internal_server_error) == %{
+               "error" =>
+                 "The pack was not set as shared and there is no fallback src to download from"
+             }
+    end
   end
 
-  describe "updating pack metadata" do
+  describe "PATCH /api/pleroma/emoji/packs/:name" do
     setup do
-      pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
+      pack_file = "#{@emoji_path}/test_pack/pack.json"
       original_content = File.read!(pack_file)
 
       on_exit(fn ->
         File.write!(pack_file, original_content)
       end)
 
-      admin = insert(:user, is_admin: true)
-      %{conn: conn} = oauth_access(["admin:write"], user: admin)
-
       {:ok,
-       admin: admin,
-       conn: conn,
        pack_file: pack_file,
        new_data: %{
          "license" => "Test license changed",
@@ -228,15 +310,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     end
 
     test "for a pack without a fallback source", ctx do
-      conn = ctx[:conn]
-
-      assert conn
-             |> post(
-               emoji_api_path(conn, :update_metadata, "test_pack"),
-               %{
-                 "new_data" => ctx[:new_data]
-               }
-             )
+      assert ctx[:admin_conn]
+             |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]})
              |> json_response(200) == ctx[:new_data]
 
       assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
@@ -248,7 +323,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
           method: :get,
           url: "https://nonshared-pack"
         } ->
-          text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
+          text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
       end)
 
       new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
@@ -260,15 +335,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
           "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF"
         )
 
-      conn = ctx[:conn]
-
-      assert conn
-             |> post(
-               emoji_api_path(conn, :update_metadata, "test_pack"),
-               %{
-                 "new_data" => new_data
-               }
-             )
+      assert ctx[:admin_conn]
+             |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
              |> json_response(200) == new_data_with_sha
 
       assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
@@ -286,181 +354,377 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
       new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
 
-      conn = ctx[:conn]
-
-      assert (conn
-              |> post(
-                emoji_api_path(conn, :update_metadata, "test_pack"),
-                %{
-                  "new_data" => new_data
-                }
-              )
-              |> json_response(:bad_request))["error"] =~ "does not have all"
+      assert ctx[:admin_conn]
+             |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data})
+             |> json_response(:bad_request) == %{
+               "error" => "The fallback archive does not have all files specified in pack.json"
+             }
     end
   end
 
-  test "updating pack files" do
-    pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
-    original_content = File.read!(pack_file)
+  describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do
+    setup do
+      pack_file = "#{@emoji_path}/test_pack/pack.json"
+      original_content = File.read!(pack_file)
 
-    on_exit(fn ->
-      File.write!(pack_file, original_content)
+      on_exit(fn ->
+        File.write!(pack_file, original_content)
+      end)
 
-      File.rm_rf!("#{@emoji_dir_path}/test_pack/blank_url.png")
-      File.rm_rf!("#{@emoji_dir_path}/test_pack/dir")
-      File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2")
-    end)
+      :ok
+    end
 
-    admin = insert(:user, is_admin: true)
-    %{conn: conn} = oauth_access(["admin:write"], user: admin)
-
-    same_name = %{
-      "action" => "add",
-      "shortcode" => "blank",
-      "filename" => "dir/blank.png",
-      "file" => %Plug.Upload{
-        filename: "blank.png",
-        path: "#{@emoji_dir_path}/test_pack/blank.png"
-      }
-    }
-
-    different_name = %{same_name | "shortcode" => "blank_2"}
-
-    assert (conn
-            |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name)
-            |> json_response(:conflict))["error"] =~ "already exists"
-
-    assert conn
-           |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name)
-           |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"}
-
-    assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png")
-
-    assert conn
-           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
-             "action" => "update",
-             "shortcode" => "blank_2",
-             "new_shortcode" => "blank_3",
-             "new_filename" => "dir_2/blank_3.png"
-           })
-           |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"}
-
-    refute File.exists?("#{@emoji_dir_path}/test_pack/dir/")
-    assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png")
-
-    assert conn
-           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
-             "action" => "remove",
-             "shortcode" => "blank_3"
-           })
-           |> json_response(200) == %{"blank" => "blank.png"}
-
-    refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/")
-
-    mock(fn
-      %{
-        method: :get,
-        url: "https://test-blank/blank_url.png"
-      } ->
-        text(File.read!("#{@emoji_dir_path}/test_pack/blank.png"))
-    end)
+    test "create shortcode exists", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank",
+               filename: "dir/blank.png",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(:conflict) == %{
+               "error" => "An emoji with the \"blank\" shortcode already exists"
+             }
+    end
 
-    # The name should be inferred from the URL ending
-    from_url = %{
-      "action" => "add",
-      "shortcode" => "blank_url",
-      "file" => "https://test-blank/blank_url.png"
-    }
+    test "don't rewrite old emoji", %{admin_conn: admin_conn} do
+      on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end)
 
-    assert conn
-           |> post(emoji_api_path(conn, :update_file, "test_pack"), from_url)
-           |> json_response(200) == %{
-             "blank" => "blank.png",
-             "blank_url" => "blank_url.png"
-           }
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank2",
+               filename: "dir/blank.png",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"}
+
+      assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
+
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank",
+               new_shortcode: "blank2",
+               new_filename: "dir_2/blank_3.png"
+             })
+             |> json_response(:conflict) == %{
+               "error" =>
+                 "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option"
+             }
+    end
+
+    test "rewrite old emoji with force option", %{admin_conn: admin_conn} do
+      on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end)
 
-    assert File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank2",
+               filename: "dir/blank.png",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"}
+
+      assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
+
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank2",
+               new_shortcode: "blank3",
+               new_filename: "dir_2/blank_3.png",
+               force: true
+             })
+             |> json_response(200) == %{
+               "blank" => "blank.png",
+               "blank3" => "dir_2/blank_3.png"
+             }
+
+      assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png")
+    end
+
+    test "with empty filename", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank2",
+               filename: "",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(:bad_request) == %{
+               "error" => "pack name, shortcode or filename cannot be empty"
+             }
+    end
+
+    test "add file with not loaded pack", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/not_loaded/files", %{
+               shortcode: "blank2",
+               filename: "dir/blank.png",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(:bad_request) == %{
+               "error" => "pack \"not_loaded\" is not found"
+             }
+    end
+
+    test "remove file with not loaded pack", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: "blank3"})
+             |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"}
+    end
+
+    test "remove file with empty shortcode", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: ""})
+             |> json_response(:bad_request) == %{
+               "error" => "pack name or shortcode cannot be empty"
+             }
+    end
 
-    assert conn
-           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
-             "action" => "remove",
-             "shortcode" => "blank_url"
-           })
-           |> json_response(200) == %{"blank" => "blank.png"}
+    test "update file with not loaded pack", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{
+               shortcode: "blank4",
+               new_shortcode: "blank3",
+               new_filename: "dir_2/blank_3.png"
+             })
+             |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"}
+    end
+
+    test "new with shortcode as file with update", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank4",
+               filename: "dir/blank.png",
+               file: %Plug.Upload{
+                 filename: "blank.png",
+                 path: "#{@emoji_path}/test_pack/blank.png"
+               }
+             })
+             |> json_response(200) == %{"blank" => "blank.png", "blank4" => "dir/blank.png"}
 
-    refute File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
+      assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
+
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank4",
+               new_shortcode: "blank3",
+               new_filename: "dir_2/blank_3.png"
+             })
+             |> json_response(200) == %{"blank3" => "dir_2/blank_3.png", "blank" => "blank.png"}
+
+      refute File.exists?("#{@emoji_path}/test_pack/dir/")
+      assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png")
+
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank3"})
+             |> json_response(200) == %{"blank" => "blank.png"}
+
+      refute File.exists?("#{@emoji_path}/test_pack/dir_2/")
+
+      on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end)
+    end
+
+    test "new with shortcode from url", %{admin_conn: admin_conn} do
+      mock(fn
+        %{
+          method: :get,
+          url: "https://test-blank/blank_url.png"
+        } ->
+          text(File.read!("#{@emoji_path}/test_pack/blank.png"))
+      end)
+
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank_url",
+               file: "https://test-blank/blank_url.png"
+             })
+             |> json_response(200) == %{
+               "blank_url" => "blank_url.png",
+               "blank" => "blank.png"
+             }
+
+      assert File.exists?("#{@emoji_path}/test_pack/blank_url.png")
+
+      on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end)
+    end
+
+    test "new without shortcode", %{admin_conn: admin_conn} do
+      on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end)
+
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_pack/files", %{
+               file: %Plug.Upload{
+                 filename: "shortcode.png",
+                 path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png"
+               }
+             })
+             |> json_response(200) == %{"shortcode" => "shortcode.png", "blank" => "blank.png"}
+    end
+
+    test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank2"})
+             |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"}
+    end
+
+    test "update non existing emoji", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank2",
+               new_shortcode: "blank3",
+               new_filename: "dir_2/blank_3.png"
+             })
+             |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"}
+    end
+
+    test "update with empty shortcode", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> patch("/api/pleroma/emoji/packs/test_pack/files", %{
+               shortcode: "blank",
+               new_filename: "dir_2/blank_3.png"
+             })
+             |> json_response(:bad_request) == %{
+               "error" => "new_shortcode or new_filename cannot be empty"
+             }
+    end
   end
 
-  test "creating and deleting a pack" do
-    on_exit(fn ->
-      File.rm_rf!("#{@emoji_dir_path}/test_created")
-    end)
+  describe "POST/DELETE /api/pleroma/emoji/packs/:name" do
+    test "creating and deleting a pack", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_created")
+             |> json_response(200) == "ok"
 
-    admin = insert(:user, is_admin: true)
-    %{conn: conn} = oauth_access(["admin:write"], user: admin)
-
-    assert conn
-           |> put_req_header("content-type", "application/json")
-           |> put(
-             emoji_api_path(
-               conn,
-               :create,
-               "test_created"
-             )
-           )
-           |> json_response(200) == "ok"
+      assert File.exists?("#{@emoji_path}/test_created/pack.json")
 
-    assert File.exists?("#{@emoji_dir_path}/test_created/pack.json")
+      assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
+               "pack" => %{},
+               "files" => %{}
+             }
 
-    assert Jason.decode!(File.read!("#{@emoji_dir_path}/test_created/pack.json")) == %{
-             "pack" => %{},
-             "files" => %{}
-           }
+      assert admin_conn
+             |> delete("/api/pleroma/emoji/packs/test_created")
+             |> json_response(200) == "ok"
+
+      refute File.exists?("#{@emoji_path}/test_created/pack.json")
+    end
+
+    test "if pack exists", %{admin_conn: admin_conn} do
+      path = Path.join(@emoji_path, "test_created")
+      File.mkdir(path)
+      pack_file = Jason.encode!(%{files: %{}, pack: %{}})
+      File.write!(Path.join(path, "pack.json"), pack_file)
+
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/test_created")
+             |> json_response(:conflict) == %{
+               "error" => "A pack named \"test_created\" already exists"
+             }
 
-    assert conn
-           |> delete(emoji_api_path(conn, :delete, "test_created"))
-           |> json_response(200) == "ok"
+      on_exit(fn -> File.rm_rf(path) end)
+    end
+
+    test "with empty name", %{admin_conn: admin_conn} do
+      assert admin_conn
+             |> post("/api/pleroma/emoji/packs/ ")
+             |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
+    end
+  end
+
+  test "deleting nonexisting pack", %{admin_conn: admin_conn} do
+    assert admin_conn
+           |> delete("/api/pleroma/emoji/packs/non_existing")
+           |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"}
+  end
 
-    refute File.exists?("#{@emoji_dir_path}/test_created/pack.json")
+  test "deleting with empty name", %{admin_conn: admin_conn} do
+    assert admin_conn
+           |> delete("/api/pleroma/emoji/packs/ ")
+           |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
   end
 
-  test "filesystem import" do
+  test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
     on_exit(fn ->
-      File.rm!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt")
-      File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
+      File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt")
+      File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
     end)
 
-    conn = build_conn()
-    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
 
     refute Map.has_key?(resp, "test_pack_for_import")
 
-    admin = insert(:user, is_admin: true)
-    %{conn: conn} = oauth_access(["admin:write"], user: admin)
-
-    assert conn
-           |> post(emoji_api_path(conn, :import_from_fs))
+    assert admin_conn
+           |> get("/api/pleroma/emoji/packs/import")
            |> json_response(200) == ["test_pack_for_import"]
 
-    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
     assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
 
-    File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
-    refute File.exists?("#{@emoji_dir_path}/test_pack_for_import/pack.json")
+    File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
+    refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
 
-    emoji_txt_content = "blank, blank.png, Fun\n\nblank2, blank.png"
+    emoji_txt_content = """
+    blank, blank.png, Fun
+    blank2, blank.png
+    foo, /emoji/test_pack_for_import/blank.png
+    bar
+    """
 
-    File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
+    File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
 
-    assert conn
-           |> post(emoji_api_path(conn, :import_from_fs))
+    assert admin_conn
+           |> get("/api/pleroma/emoji/packs/import")
            |> json_response(200) == ["test_pack_for_import"]
 
-    resp = build_conn() |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
+    resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200)
 
     assert resp["test_pack_for_import"]["files"] == %{
              "blank" => "blank.png",
-             "blank2" => "blank.png"
+             "blank2" => "blank.png",
+             "foo" => "blank.png"
            }
   end
+
+  describe "GET /api/pleroma/emoji/packs/:name" do
+    test "shows pack.json", %{conn: conn} do
+      assert %{
+               "files" => %{"blank" => "blank.png"},
+               "pack" => %{
+                 "can-download" => true,
+                 "description" => "Test description",
+                 "download-sha256" => _,
+                 "homepage" => "https://pleroma.social",
+                 "license" => "Test license",
+                 "share-files" => true
+               }
+             } =
+               conn
+               |> get("/api/pleroma/emoji/packs/test_pack")
+               |> json_response(200)
+    end
+
+    test "non existing pack", %{conn: conn} do
+      assert conn
+             |> get("/api/pleroma/emoji/packs/non_existing")
+             |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"}
+    end
+
+    test "error name", %{conn: conn} do
+      assert conn
+             |> get("/api/pleroma/emoji/packs/ ")
+             |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"}
+    end
+  end
 end