mirror of
https://github.com/penpot/penpot.git
synced 2026-08-11 15:29:06 +00:00
The info-service-uri-not-configured test used config-get-mock with an empty map, which falls back to cf/config for missing keys. In a REPL with real config, media-processing-service-uri is set, causing the code to attempt an HTTP call instead of raising the expected error. Use (constantly nil) to ensure cf/get always returns nil, matching the test intent of simulating an unconfigured service URI. AI-assisted-by: mimo-v2.5
594 lines
31 KiB
Clojure
594 lines
31 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
|
|
|
(ns backend-tests.media-remote-test
|
|
(:require
|
|
[app.common.exceptions :as ex]
|
|
[app.config :as cf]
|
|
[app.media.remote :as media.remote]
|
|
[app.setup :as-alias setup]
|
|
[app.util.json :as json]
|
|
[backend-tests.helpers :as th]
|
|
[clojure.test :as t]
|
|
[cuerdas.core :as str]
|
|
[datoteka.fs :as fs]
|
|
[datoteka.io :as io]
|
|
[mockery.core :refer [with-mocks]])
|
|
(:import
|
|
java.io.ByteArrayInputStream))
|
|
|
|
(defn- mk-system
|
|
"Minimal system map for media.remote/process tests."
|
|
[]
|
|
{::setup/shared-keys {:media-processor "test-shared-key"}})
|
|
|
|
(defn- json-stream
|
|
"Create an InputStream from a Clojure data structure (JSON-encoded)."
|
|
[data]
|
|
(ByteArrayInputStream.
|
|
(json/encode data)))
|
|
|
|
(def config-mock
|
|
"Standard config mock for media-processor service."
|
|
{:media-processing-service-uri "http://localhost:6065"
|
|
:media-processing-service-timeout 5000})
|
|
|
|
(defn- write-font-tmp
|
|
"Write font bytes to a tempfile and return the Path. Caller is responsible for cleanup."
|
|
[bytes suffix]
|
|
(let [tmp (fs/create-tempfile :prefix "penpot-test-font-" :suffix suffix)]
|
|
(io/write* tmp bytes)
|
|
tmp))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; :info
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest info-happy-path
|
|
(t/testing "info returns dimensions and merges into input"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (json-stream {:width 800 :height 600 :mtype "image/jpeg" :size 12345 :orientation 1})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
result (media.remote/process (mk-system)
|
|
{:cmd :info
|
|
:input {:path path :mtype "image/jpeg"}})]
|
|
(t/is (= 800 (:width result)))
|
|
(t/is (= 600 (:height result)))
|
|
(t/is (= (fs/size path) (:size result)))
|
|
(t/is (some? (:ts result)))
|
|
(t/is (= path (:path result)))
|
|
(t/is (= "image/jpeg" (:mtype result)))
|
|
(t/is (= 1 (:call-count @mock))))))))
|
|
|
|
(t/deftest info-verifies-request-params
|
|
(t/testing "info sends correct endpoint, method, and x-shared-key header"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (json-stream {:width 100 :height 100 :mtype "image/jpeg" :size 1 :orientation 1})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")]
|
|
(media.remote/process (mk-system)
|
|
{:cmd :info :input {:path path :mtype "image/jpeg"}})
|
|
(let [[system req-map] (:call-args @mock)]
|
|
;; System passed through
|
|
(t/is (some? (::setup/shared-keys system)))
|
|
;; Request structure
|
|
(t/is (= :post (:method req-map)))
|
|
(t/is (str/includes? (str (:uri req-map)) "api/image/info"))
|
|
(t/is (= "test-shared-key" (get-in req-map [:headers "x-shared-key"])))
|
|
(t/is (str/starts-with?
|
|
(get-in req-map [:headers "Content-Type"])
|
|
"multipart/form-data"))))))))
|
|
|
|
(t/deftest info-no-content-length-header
|
|
(t/testing "info does not send Content-Length header (JDK uses chunked encoding)"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (json-stream {:width 100 :height 100 :mtype "image/jpeg" :size 1 :orientation 1})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")]
|
|
(media.remote/process (mk-system)
|
|
{:cmd :info :input {:path path :mtype "image/jpeg"}})
|
|
(let [[_ req-map] (:call-args @mock)]
|
|
(t/is (nil? (get-in req-map [:headers "Content-Length"])))))))))
|
|
|
|
(t/deftest info-service-uri-not-configured
|
|
(t/testing "info throws when service URI is not configured"
|
|
(with-redefs [cf/get (constantly nil)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :info :input {:path path :mtype "image/jpeg"}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :internal (:type (ex-data err))))
|
|
(t/is (= :media-processor-not-configured (:code (ex-data err))))))))
|
|
|
|
(t/deftest info-service-unavailable
|
|
(t/testing "info throws when service-request raises unavailable"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "Cannot connect to media-processor service"
|
|
{:type :internal
|
|
:code :media-processor-unavailable})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :info :input {:path path :mtype "image/jpeg"}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :internal (:type (ex-data err))))
|
|
(t/is (= :media-processor-unavailable (:code (ex-data err)))))))))
|
|
|
|
(t/deftest info-service-timeout
|
|
(t/testing "info throws when service-request raises timeout"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "media-processor service request timed out"
|
|
{:type :internal
|
|
:code :media-processor-timeout})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :info :input {:path path :mtype "image/jpeg"}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :internal (:type (ex-data err))))
|
|
(t/is (= :media-processor-timeout (:code (ex-data err)))))))))
|
|
|
|
(t/deftest info-mtype-mismatch
|
|
(t/testing "info raises :media-type-mismatch when detected mtype differs from declared"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (json-stream {:width 100 :height 100 :size 100
|
|
:mtype "image/png"})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :info
|
|
:input {:path path :mtype "image/jpeg"}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :validation (:type (ex-data err))))
|
|
(t/is (= :media-type-mismatch (:code (ex-data err)))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; :generic-thumbnail
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest generic-thumbnail-happy-path
|
|
(t/testing "generic-thumbnail returns tempfile with correct format"
|
|
(let [thumb-bytes (.getBytes "fake-jpeg-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. thumb-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
result (media.remote/process (mk-system)
|
|
{:cmd :generic-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 80
|
|
:width 200
|
|
:height 200})]
|
|
(t/is (= :jpeg (:format result)))
|
|
(t/is (= "image/jpeg" (:mtype result)))
|
|
(t/is (pos? (:size result)))
|
|
(t/is (fs/exists? (:data result)))))))))
|
|
|
|
(t/deftest generic-thumbnail-verifies-query-params
|
|
(t/testing "generic-thumbnail sends correct query params with mode=fit"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. (.getBytes "data" "UTF-8"))}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")]
|
|
(media.remote/process (mk-system)
|
|
{:cmd :generic-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 85
|
|
:width 300
|
|
:height 400})
|
|
(let [[_ req-map] (:call-args @mock)]
|
|
(t/is (str/includes? (str (:uri req-map)) "width=300"))
|
|
(t/is (str/includes? (str (:uri req-map)) "height=400"))
|
|
(t/is (str/includes? (str (:uri req-map)) "quality=85"))
|
|
(t/is (str/includes? (str (:uri req-map)) "format=jpeg"))
|
|
(t/is (str/includes? (str (:uri req-map)) "mode=fit"))))))))
|
|
|
|
(t/deftest generic-thumbnail-service-unavailable
|
|
(t/testing "generic-thumbnail throws on service error"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "Cannot connect to media-processor service"
|
|
{:type :internal
|
|
:code :media-processor-unavailable})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :generic-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 85
|
|
:width 200
|
|
:height 200}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :media-processor-unavailable (:code (ex-data err)))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; :profile-thumbnail
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest profile-thumbnail-happy-path
|
|
(t/testing "profile-thumbnail returns tempfile and uses mode=crop"
|
|
(let [thumb-bytes (.getBytes "fake-png-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. thumb-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
result (media.remote/process (mk-system)
|
|
{:cmd :profile-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 85
|
|
:width 128
|
|
:height 128})]
|
|
(t/is (some? (:data result)))
|
|
(t/is (fs/exists? (:data result)))
|
|
;; Verify mode=crop in URI
|
|
(let [[_ req-map] (:call-args @mock)]
|
|
(t/is (str/includes? (str (:uri req-map)) "mode=crop")))))))))
|
|
|
|
(t/deftest profile-thumbnail-service-unavailable
|
|
(t/testing "profile-thumbnail throws on service error"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "Cannot connect to media-processor service"
|
|
{:type :internal
|
|
:code :media-processor-unavailable})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :profile-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 85
|
|
:width 128
|
|
:height 128}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :media-processor-unavailable (:code (ex-data err)))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; :generate-fonts
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest generate-fonts-ttf-happy-path
|
|
(t/testing "generate-fonts with TTF path makes per-variant calls"
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
ttfpath (write-font-tmp ttfbytes ".ttf")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [result (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/ttf" ttfpath}})]
|
|
;; Original path preserved
|
|
(t/is (= ttfpath (get result "font/ttf")))
|
|
;; Variants written to tempfiles
|
|
(t/is (fs/exists? (get result "font/otf")))
|
|
(t/is (fs/exists? (get result "font/woff")))
|
|
;; Two calls: one for otf, one for woff
|
|
(t/is (= 2 (:call-count @mock))))
|
|
(finally
|
|
(fs/delete ttfpath))))))))
|
|
|
|
(t/deftest generate-fonts-ttf-as-path
|
|
(t/testing "generate-fonts with TTF as tempfile Path works"
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
tmp-path (write-font-tmp ttfbytes ".ttf")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [result (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/ttf" tmp-path}})]
|
|
;; Path preserved
|
|
(t/is (= tmp-path (get result "font/ttf")))
|
|
;; Variant written
|
|
(t/is (fs/exists? (get result "font/otf"))))
|
|
(finally
|
|
(fs/delete tmp-path))))))))
|
|
|
|
(t/deftest generate-fonts-otf-happy-path
|
|
(t/testing "generate-fonts with OTF path"
|
|
(let [otfbytes (io/read* (io/resource "backend_tests/test_files/font-1.otf"))
|
|
otfpath (write-font-tmp otfbytes ".otf")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [result (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/otf" otfpath}})]
|
|
(t/is (= otfpath (get result "font/otf")))
|
|
(t/is (fs/exists? (get result "font/ttf")))
|
|
(t/is (fs/exists? (get result "font/woff")))
|
|
;; Two calls: one for ttf, one for woff
|
|
(t/is (= 2 (:call-count @mock))))
|
|
(finally
|
|
(fs/delete otfpath))))))))
|
|
|
|
(t/deftest generate-fonts-woff-happy-path
|
|
(t/testing "generate-fonts with WOFF path"
|
|
(let [woffbytes (io/read* (io/resource "backend_tests/test_files/font-1.woff"))
|
|
woffpath (write-font-tmp woffbytes ".woff")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [result (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/woff" woffpath}})]
|
|
(t/is (= woffpath (get result "font/woff")))
|
|
(t/is (fs/exists? (get result "font/ttf")))
|
|
(t/is (fs/exists? (get result "font/otf")))
|
|
;; Two calls: one for ttf, one for otf
|
|
(t/is (= 2 (:call-count @mock))))
|
|
(finally
|
|
(fs/delete woffpath)))))))
|
|
|
|
(t/deftest generate-fonts-woff2-happy-path
|
|
(t/testing "generate-fonts with WOFF2 path"
|
|
(let [woff2bytes (io/read* (io/resource "backend_tests/test_files/font-1.woff2"))
|
|
woff2path (write-font-tmp woff2bytes ".woff2")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [result (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/woff2" woff2path}})]
|
|
(t/is (= woff2path (get result "font/woff2")))
|
|
(t/is (fs/exists? (get result "font/ttf")))
|
|
(t/is (fs/exists? (get result "font/otf")))
|
|
(t/is (fs/exists? (get result "font/woff")))
|
|
;; Three calls: one for ttf, one for otf, one for woff
|
|
(t/is (= 3 (:call-count @mock))))
|
|
(finally
|
|
(fs/delete woff2path)))))))))
|
|
|
|
(t/deftest generate-fonts-verifies-query-params
|
|
(t/testing "generate-fonts sends target-type query param with 180s timeout"
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
ttfpath (write-font-tmp ttfbytes ".ttf")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(media.remote/process (mk-system)
|
|
{:cmd :generate-fonts :input {"font/ttf" ttfpath}})
|
|
(let [[_ req-map] (:call-args @mock)]
|
|
(t/is (str/includes? (str (:uri req-map)) "target-type="))
|
|
(t/is (= 180000 (:timeout req-map))))
|
|
(finally
|
|
(fs/delete ttfpath))))))))
|
|
|
|
(t/deftest generate-fonts-woff-verifies-target-types
|
|
(t/testing "generate-fonts with WOFF sends target-type query param"
|
|
(let [woffbytes (io/read* (io/resource "backend_tests/test_files/font-1.woff"))
|
|
woffpath (write-font-tmp woffbytes ".woff")
|
|
fake-bytes (.getBytes "fake-font-data" "UTF-8")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (ByteArrayInputStream. fake-bytes)}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(media.remote/process (mk-system)
|
|
{:cmd :generate-fonts :input {"font/woff" woffpath}})
|
|
(let [[_ req-map] (:call-args @mock)]
|
|
(t/is (str/includes? (str (:uri req-map)) "target-type=")))
|
|
(finally
|
|
(fs/delete woffpath))))))))
|
|
|
|
(t/deftest generate-fonts-no-recognized-variant
|
|
(t/testing "generate-fonts throws when no recognized font variant"
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/unknown" (.getBytes "data" "UTF-8")}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :validation (:type (ex-data err))))
|
|
(t/is (= :invalid-font (:code (ex-data err))))))))
|
|
|
|
(t/deftest generate-fonts-connection-error
|
|
(t/testing "generate-fonts throws on service error"
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
ttfpath (write-font-tmp ttfbytes ".ttf")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "Cannot connect to media-processor service"
|
|
{:type :internal
|
|
:code :media-processor-unavailable})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts :input {"font/ttf" ttfpath}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :media-processor-unavailable (:code (ex-data err)))))
|
|
(finally
|
|
(fs/delete ttfpath))))))))
|
|
|
|
(t/deftest generate-fonts-timeout-error
|
|
(t/testing "generate-fonts throws on service timeout"
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
ttfpath (write-font-tmp ttfbytes ".ttf")]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:throw (ex-info "media-processor service request timed out"
|
|
{:type :internal
|
|
:code :media-processor-timeout})}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(try
|
|
(let [err (ex/try! (media.remote/process (mk-system)
|
|
{:cmd :generate-fonts :input {"font/ttf" ttfpath}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :media-processor-timeout (:code (ex-data err)))))
|
|
(finally
|
|
(fs/delete ttfpath))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; Status code handling (service-request)
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest service-request-raises-on-400
|
|
(t/testing "service-request raises :validation on status 400"
|
|
(with-mocks [mock {:target 'app.http.client/req
|
|
:return {:status 400
|
|
:body (json-stream {:type "validation"
|
|
:code "invalid-image"
|
|
:hint "bad input"})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [err (ex/try! (media.remote/service-request
|
|
(mk-system)
|
|
{:method :post
|
|
:uri "http://localhost:6065/api/image/info"
|
|
:body nil
|
|
:headers {}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :validation (:type (ex-data err))))
|
|
(t/is (= :invalid-image (:code (ex-data err)))))))))
|
|
|
|
(t/deftest service-request-raises-on-500
|
|
(t/testing "service-request raises :internal on status 500"
|
|
(with-mocks [mock {:target 'app.http.client/req
|
|
:return {:status 500
|
|
:body (json-stream {:type "internal"
|
|
:code "processing-error"
|
|
:hint "Internal server error"})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [err (ex/try! (media.remote/service-request
|
|
(mk-system)
|
|
{:method :post
|
|
:uri "http://localhost:6065/api/image/info"
|
|
:body nil
|
|
:headers {}}))]
|
|
(t/is (ex/error? err))
|
|
(t/is (= :internal (:type (ex-data err))))
|
|
(t/is (= :processing-error (:code (ex-data err)))))))))
|
|
|
|
(t/deftest service-request-passes-on-200
|
|
(t/testing "service-request returns response on status 200"
|
|
(with-mocks [mock {:target 'app.http.client/req
|
|
:return {:status 200
|
|
:body (json-stream {:width 100 :height 100})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [resp (media.remote/service-request
|
|
(mk-system)
|
|
{:method :post
|
|
:uri "http://localhost:6065/api/image/info"
|
|
:body nil
|
|
:headers {}})]
|
|
(t/is (= 200 (:status resp))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; Shared key
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(t/deftest shared-key-sent-correctly
|
|
(t/testing "x-shared-key header matches the system's shared key"
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body (json-stream {:width 1 :height 1 :mtype "image/jpeg" :size 1 :orientation 1})}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [system {::setup/shared-keys {:media-processor "my-secret-key-123"}}]
|
|
(media.remote/process system
|
|
{:cmd :info
|
|
:input {:path (th/tempfile "backend_tests/test_files/sample.jpg")
|
|
:mtype "image/jpeg"}})
|
|
(let [[system-arg _] (:call-args @mock)]
|
|
;; System passed through correctly
|
|
(t/is (= "my-secret-key-123"
|
|
(-> system-arg ::setup/shared-keys :media-processor)))))))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; Stream closure
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defn- tracking-stream
|
|
"Create an InputStream that tracks whether it was closed.
|
|
Returns a map with :stream (the InputStream) and :closed (an atom)."
|
|
[^bytes data]
|
|
(let [closed (atom false)
|
|
delegate (ByteArrayInputStream. data)
|
|
stream (proxy [java.io.InputStream] []
|
|
(read
|
|
([] (.read delegate))
|
|
([^bytes b] (.read delegate b))
|
|
([^bytes b off len] (.read delegate b off len)))
|
|
(close []
|
|
(reset! closed true)
|
|
(.close delegate)))]
|
|
{:stream stream :closed closed}))
|
|
|
|
(t/deftest info-closes-response-stream
|
|
(t/testing "info closes the response stream after parsing JSON"
|
|
(let [json-str "{\"width\":100,\"height\":100,\"mtype\":\"image/jpeg\",\"size\":1,\"orientation\":1}"
|
|
json-data (.getBytes json-str "UTF-8")
|
|
{:keys [stream closed]} (tracking-stream json-data)]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body stream}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")]
|
|
(media.remote/process (mk-system)
|
|
{:cmd :info
|
|
:input {:path path :mtype "image/jpeg"}})
|
|
;; Stream should be closed after processing
|
|
(t/is @closed)))))))
|
|
|
|
(t/deftest font-convert-closes-response-stream
|
|
(t/testing "font-convert closes the response stream after writing"
|
|
(let [{:keys [stream closed]} (tracking-stream (.getBytes "fake-font-data" "UTF-8"))]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body stream}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [ttfbytes (io/read* (io/resource "backend_tests/test_files/font-1.ttf"))
|
|
ttfpath (write-font-tmp ttfbytes ".ttf")]
|
|
(try
|
|
(media.remote/process (mk-system)
|
|
{:cmd :generate-fonts
|
|
:input {"font/ttf" ttfpath}})
|
|
;; Stream should be closed after processing
|
|
(t/is @closed)
|
|
(finally
|
|
(fs/delete ttfpath)))))))))
|
|
|
|
(t/deftest thumbnail-closes-response-stream
|
|
(t/testing "thumbnail closes the response stream after writing"
|
|
(let [{:keys [stream closed]} (tracking-stream (.getBytes "fake-thumbnail-data" "UTF-8"))]
|
|
(with-mocks [mock {:target 'app.media.remote/service-request
|
|
:return {:status 200
|
|
:body stream}}]
|
|
(with-redefs [cf/get (th/config-get-mock config-mock)]
|
|
(let [path (th/tempfile "backend_tests/test_files/sample.jpg")]
|
|
(media.remote/process (mk-system)
|
|
{:cmd :generic-thumbnail
|
|
:input {:path path :mtype "image/jpeg"}
|
|
:format :jpeg
|
|
:quality 85
|
|
:width 200
|
|
:height 200})
|
|
;; Stream should be closed after processing
|
|
(t/is @closed)))))))
|