[#2497] Configurability of :min_content_length (preview proxy). Refactoring, document...
[akkoma] / test / web / media_proxy / media_proxy_controller_test.exs
index d4db44c6312c7248c988fd80fbb5ea69ec255ec8..33e6873f79d85620ea0a6b76c172c71f7aff0895 100644 (file)
@@ -8,34 +8,34 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
   import Mock
 
   alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.MediaProxy.MediaProxyController
   alias Plug.Conn
 
   setup do
     on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
   end
 
-  test "it returns 404 when MediaProxy disabled", %{conn: conn} do
-    clear_config([:media_proxy, :enabled], false)
-
-    assert %Conn{
-             status: 404,
-             resp_body: "Not Found"
-           } = get(conn, "/proxy/hhgfh/eeeee")
-
-    assert %Conn{
-             status: 404,
-             resp_body: "Not Found"
-           } = get(conn, "/proxy/hhgfh/eeee/fff")
-  end
-
-  describe "" do
+  describe "Media Proxy" do
     setup do
       clear_config([:media_proxy, :enabled], true)
       clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+
       [url: MediaProxy.encode_url("https://google.fn/test.png")]
     end
 
+    test "it returns 404 when disabled", %{conn: conn} do
+      clear_config([:media_proxy, :enabled], false)
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/hhgfh/eeeee")
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/hhgfh/eeee/fff")
+    end
+
     test "it returns 403 for invalid signature", %{conn: conn, url: url} do
       Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
       %{path: path} = URI.parse(url)
@@ -56,7 +56,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
              } = get(conn, "/proxy/hhgfh/eeee/fff")
     end
 
-    test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do
+    test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
       invalid_url = String.replace(url, "test.png", "test-file.png")
       response = get(conn, invalid_url)
       assert response.status == 302
@@ -80,42 +80,248 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
     end
   end
 
-  describe "filename_matches/3" do
-    test "preserves the encoded or decoded path" do
-      assert MediaProxyController.filename_matches(
-               %{"filename" => "/Hello world.jpg"},
-               "/Hello world.jpg",
-               "http://pleroma.social/Hello world.jpg"
-             ) == :ok
-
-      assert MediaProxyController.filename_matches(
-               %{"filename" => "/Hello%20world.jpg"},
-               "/Hello%20world.jpg",
-               "http://pleroma.social/Hello%20world.jpg"
-             ) == :ok
-
-      assert MediaProxyController.filename_matches(
-               %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"},
-               "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
-               "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
-             ) == :ok
-
-      assert MediaProxyController.filename_matches(
-               %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"},
-               "/my%2Flong%2Furl%2F2019%2F07%2FS.jp",
-               "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
-             ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
-    end
-
-    test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do
-      # conn.request_path will return encoded url
-      request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg"
-
-      assert MediaProxyController.filename_matches(
-               true,
-               request_path,
-               "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg"
-             ) == :ok
+  describe "Media Preview Proxy" do
+    setup do
+      clear_config([:media_proxy, :enabled], true)
+      clear_config([:media_preview_proxy, :enabled], true)
+      clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+
+      original_url = "https://google.fn/test.png"
+
+      [
+        url: MediaProxy.encode_preview_url(original_url),
+        media_proxy_url: MediaProxy.encode_url(original_url)
+      ]
+    end
+
+    test "returns 404 when media proxy is disabled", %{conn: conn} do
+      clear_config([:media_proxy, :enabled], false)
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/preview/hhgfh/eeeee")
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/preview/hhgfh/fff")
+    end
+
+    test "returns 404 when disabled", %{conn: conn} do
+      clear_config([:media_preview_proxy, :enabled], false)
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/preview/hhgfh/eeeee")
+
+      assert %Conn{
+               status: 404,
+               resp_body: "Not Found"
+             } = get(conn, "/proxy/preview/hhgfh/fff")
+    end
+
+    test "it returns 403 for invalid signature", %{conn: conn, url: url} do
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
+      %{path: path} = URI.parse(url)
+
+      assert %Conn{
+               status: 403,
+               resp_body: "Forbidden"
+             } = get(conn, path)
+
+      assert %Conn{
+               status: 403,
+               resp_body: "Forbidden"
+             } = get(conn, "/proxy/preview/hhgfh/eeee")
+
+      assert %Conn{
+               status: 403,
+               resp_body: "Forbidden"
+             } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
+    end
+
+    test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
+      invalid_url = String.replace(url, "test.png", "test-file.png")
+      response = get(conn, invalid_url)
+      assert response.status == 302
+      assert redirected_to(response) == url
+    end
+
+    test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 500, body: ""}
+      end)
+
+      response = get(conn, url)
+      assert response.status == 424
+      assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
+    end
+
+    test "redirects to media proxy URI on unsupported content type", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
+      end)
+
+      response = get(conn, url)
+      assert response.status == 302
+      assert redirected_to(response) == media_proxy_url
+    end
+
+    test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      # Setting a high :min_content_length to ensure this scenario is not affected by its logic
+      clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
+
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{
+            status: 200,
+            body: "",
+            headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
+          }
+
+        %{method: :get, url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
+      end)
+
+      response = get(conn, url <> "?static=true")
+
+      assert response.status == 200
+      assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
+      assert response.resp_body != ""
+    end
+
+    test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
+         %{
+           conn: conn,
+           url: url,
+           media_proxy_url: media_proxy_url
+         } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
+      end)
+
+      response = get(conn, url)
+
+      assert response.status == 302
+      assert redirected_to(response) == media_proxy_url
+    end
+
+    test "with `static` param and non-GIF image preview requested, " <>
+           "redirects to media preview proxy URI without `static` param",
+         %{
+           conn: conn,
+           url: url,
+           media_proxy_url: media_proxy_url
+         } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+      end)
+
+      response = get(conn, url <> "?static=true")
+
+      assert response.status == 302
+      assert redirected_to(response) == url
+    end
+
+    test "with :min_content_length setting not matched by Content-Length header, " <>
+           "redirects to media proxy URI",
+         %{
+           conn: conn,
+           url: url,
+           media_proxy_url: media_proxy_url
+         } do
+      clear_config([:media_preview_proxy, :min_content_length], 100_000)
+
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{
+            status: 200,
+            body: "",
+            headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
+          }
+      end)
+
+      response = get(conn, url)
+
+      assert response.status == 302
+      assert redirected_to(response) == media_proxy_url
+    end
+
+    test "thumbnails PNG images into PNG", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
+
+        %{method: :get, url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
+      end)
+
+      response = get(conn, url)
+
+      assert response.status == 200
+      assert Conn.get_resp_header(response, "content-type") == ["image/png"]
+      assert response.resp_body != ""
+    end
+
+    test "thumbnails JPEG images into JPEG", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+
+        %{method: :get, url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
+      end)
+
+      response = get(conn, url)
+
+      assert response.status == 200
+      assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
+      assert response.resp_body != ""
+    end
+
+    test "redirects to media proxy URI in case of thumbnailing error", %{
+      conn: conn,
+      url: url,
+      media_proxy_url: media_proxy_url
+    } do
+      Tesla.Mock.mock(fn
+        %{method: "head", url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+
+        %{method: :get, url: ^media_proxy_url} ->
+          %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
+      end)
+
+      response = get(conn, url)
+
+      assert response.status == 302
+      assert redirected_to(response) == media_proxy_url
     end
   end
 end