From 805cb94d33e73f80b7bb8d63ee61f43fed78d326 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 22 Jul 2018 14:35:59 +0300 Subject: [PATCH] exception middleware tests --- .../src/reitit/ring/middleware/exception.clj | 44 ++++---- .../reitit/ring/middleware/exception_test.clj | 100 ++++++++++++++++++ .../reitit/ring/middleware/muuntaja_test.clj | 3 +- 3 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 test/clj/reitit/ring/middleware/exception_test.clj diff --git a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj index c42b7977..895f21ea 100644 --- a/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj +++ b/modules/reitit-middleware/src/reitit/ring/middleware/exception.clj @@ -13,6 +13,9 @@ ex-class (class error) error-handler (or (get handlers type) (get handlers ex-class) + (some + (partial get handlers) + (descendants type)) (some (partial get handlers) (super-classes ex-class)) @@ -37,13 +40,13 @@ (defn request-parsing-handler [_ {:keys [format]} _] {:status 400 :headers {"Content-Type" "text/plain"} - :body (str "Malformed " format " request.")}) + :body (str "Malformed " (pr-str format) " request.")}) ;; ;; public api ;; -(def default-handlers +(def default-options {::default default-handler ::ring/response http-response-handler :muuntaja/decode request-parsing-handler @@ -54,25 +57,28 @@ "Catches all exceptions and looks up a exception handler: 1) `:type` of ex-data 2) Class of Exception - 3) Super Classes of Exception - 4) The ::default handler" + 3) descadents `:type` of ex-data + 4) Super Classes of Exception + 5) The ::default handler" ([] - (create-exceptions-middleware default-handlers)) - ([{:keys [handlers] :or {handlers default-handlers}}] - (let [default-handler (get handlers ::default default-handler) + (create-exceptions-middleware default-options)) + ([options] + (let [default-handler (get options ::default default-handler) on-exception (fn [e request respond raise] (try - (respond (call-error-handler default-handler handlers e request)) + (respond (call-error-handler default-handler options e request)) (catch Exception e (raise e))))] - (fn - ([request] - (try - (handler request) - (catch Throwable e - (on-exception e request identity #(throw %))))) - ([request respond raise] - (try - (handler request respond (fn [e] (on-exception e request respond raise))) - (catch Throwable e - (on-exception e request respond raise)))))))) + {:name ::exception + :wrap (fn [handler] + (fn + ([request] + (try + (handler request) + (catch Throwable e + (on-exception e request identity #(throw %))))) + ([request respond raise] + (try + (handler request respond (fn [e] (on-exception e request respond raise))) + (catch Throwable e + (on-exception e request respond raise))))))}))) diff --git a/test/clj/reitit/ring/middleware/exception_test.clj b/test/clj/reitit/ring/middleware/exception_test.clj new file mode 100644 index 00000000..fe7d6733 --- /dev/null +++ b/test/clj/reitit/ring/middleware/exception_test.clj @@ -0,0 +1,100 @@ +(ns reitit.ring.middleware.exception-test + (:require [clojure.test :refer [deftest testing is]] + [reitit.ring :as ring] + [reitit.ring.middleware.exception :as exception] + [reitit.coercion.spec] + [reitit.ring.coercion] + [muuntaja.core :as m]) + (:import (java.sql SQLException SQLWarning))) + +(derive ::kikka ::kukka) + +(deftest exception-test + (letfn [(create [f] + (ring/ring-handler + (ring/router + [["/defaults" + {:handler f}] + ["/coercion" + {:middleware [reitit.ring.coercion/coerce-request-middleware + reitit.ring.coercion/coerce-response-middleware] + :coercion reitit.coercion.spec/coercion + :parameters {:query {:x int?, :y int?}} + :responses {200 {:body {:total pos-int?}}} + :handler f}]] + {:data {:middleware [(exception/create-exceptions-middleware + (merge + exception/default-options + {::kikka (constantly {:status 200, :body "kikka"}) + SQLException (constantly {:status 200, :body "sql"})}))]}})))] + + (testing "normal calls work ok" + (let [response {:status 200, :body "ok"} + app (create (fn [_] response))] + (is (= response (app {:request-method :get, :uri "/defaults"}))))) + + (testing "unknown exception" + (let [app (create (fn [_] (throw (NullPointerException.))))] + (is (= {:status 500 + :body {:type "exception" + :class "java.lang.NullPointerException"}} + (app {:request-method :get, :uri "/defaults"})))) + (let [app (create (fn [_] (throw (ex-info "fail" {:type ::invalid}))))] + (is (= {:status 500 + :body {:type "exception" + :class "clojure.lang.ExceptionInfo"}} + (app {:request-method :get, :uri "/defaults"}))))) + + (testing "::ring/response" + (let [response {:status 200, :body "ok"} + app (create (fn [_] (throw (ex-info "fail" {:type ::ring/response, :response response}))))] + (is (= response (app {:request-method :get, :uri "/defaults"}))))) + + (testing ":muuntaja/decode" + (let [app (create (fn [_] (m/decode m/instance "application/json" "{:so \"invalid\"}")))] + (is (= {:body "Malformed \"application/json\" request." + :headers {"Content-Type" "text/plain"} + :status 400} + (app {:request-method :get, :uri "/defaults"})))) + + (testing "::coercion/request-coercion" + (let [app (create (fn [{{{:keys [x y]} :query} :parameters}] + {:status 200, :body {:total (+ x y)}}))] + + (let [{:keys [status body]} (app {:request-method :get + :uri "/coercion" + :query-params {"x" "1", "y" "2"}})] + (is (= 200 status)) + (is (= {:total 3} body))) + + (let [{:keys [status body]} (app {:request-method :get + :uri "/coercion" + :query-params {"x" "abba", "y" "2"}})] + (is (= 400 status)) + (is (= :reitit.coercion/request-coercion (:type body)))) + + (let [{:keys [status body]} (app {:request-method :get + :uri "/coercion" + :query-params {"x" "-10", "y" "2"}})] + (is (= 500 status)) + (is (= :reitit.coercion/response-coercion (:type body))))))) + + (testing "exact :type" + (let [app (create (fn [_] (throw (ex-info "fail" {:type ::kikka}))))] + (is (= {:status 200, :body "kikka"} + (app {:request-method :get, :uri "/defaults"}))))) + + (testing "parent :type" + (let [app (create (fn [_] (throw (ex-info "fail" {:type ::kukka}))))] + (is (= {:status 200, :body "kikka"} + (app {:request-method :get, :uri "/defaults"}))))) + + (testing "exact Exception" + (let [app (create (fn [_] (throw (SQLException.))))] + (is (= {:status 200, :body "sql"} + (app {:request-method :get, :uri "/defaults"}))))) + + (testing "Exception SuperClass" + (let [app (create (fn [_] (throw (SQLWarning.))))] + (is (= {:status 200, :body "sql"} + (app {:request-method :get, :uri "/defaults"}))))))) diff --git a/test/clj/reitit/ring/middleware/muuntaja_test.clj b/test/clj/reitit/ring/middleware/muuntaja_test.clj index 6312726b..b9b5b6b1 100644 --- a/test/clj/reitit/ring/middleware/muuntaja_test.clj +++ b/test/clj/reitit/ring/middleware/muuntaja_test.clj @@ -2,9 +2,8 @@ (:require [clojure.test :refer [deftest testing is]] [reitit.ring :as ring] [reitit.ring.middleware.muuntaja :as muuntaja] - [muuntaja.core :as m] [reitit.swagger :as swagger] - [reitit.core :as r])) + [muuntaja.core :as m])) (deftest muuntaja-test (let [data {:kikka "kukka"}