reitit/test/cljc/reitit/middleware_test.cljc
Tommi Reiman b1bef1c1cc Fix #216
2019-02-19 19:40:26 +02:00

266 lines
9.7 KiB
Clojure

(ns reitit.middleware-test
(:require [clojure.test :refer [deftest testing is are]]
[reitit.middleware :as middleware]
[reitit.core :as r])
#?(:clj
(:import (clojure.lang ExceptionInfo))))
(def request [])
(defn handler [request]
(conj request :ok))
(defn create
([middleware]
(create middleware nil))
([middleware opts]
(middleware/chain
middleware
handler
:data
opts)))
(deftest expand-middleware-test
(testing "middleware records"
(testing "middleware"
(let [calls (atom 0)
wrap (fn [handler value]
(swap! calls inc)
(fn [request]
(handler (conj request value))))]
(testing "as function"
(reset! calls 0)
(let [app (create [#(wrap % :value)])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as function vector"
(reset! calls 0)
(let [app (create [[#(wrap % :value)]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as keyword"
(reset! calls 0)
(let [app (create [:wrap] {::middleware/registry {:wrap #(wrap % :value)}})]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as keyword vector"
(reset! calls 0)
(let [app (create [[:wrap :value]] {::middleware/registry {:wrap wrap}})]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "missing keyword"
(is (thrown-with-msg?
ExceptionInfo
#"Middleware :wrap not found in registry"
(create [:wrap]))))
(testing "existing keyword, compiling to nil"
(let [app (create [:wrap] {::middleware/registry {:wrap {:compile (constantly nil)}}})]
(is (= [:ok] (app request)))))
(testing "as function vector with value(s)"
(reset! calls 0)
(let [app (create [[wrap :value]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as map"
(reset! calls 0)
(let [app (create [[{:wrap #(wrap % :value)}]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as map vector"
(reset! calls 0)
(let [app (create [[{:wrap wrap} :value]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as Middleware"
(reset! calls 0)
(let [app (create [[(middleware/map->Middleware {:wrap #(wrap % :value)})]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as Middleware vector"
(reset! calls 0)
(let [app (create [[(middleware/map->Middleware {:wrap wrap}) :value]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls)))))))
(testing "compiled Middleware"
(let [calls (atom 0)
mw {:compile (fn [data _]
(swap! calls inc)
(fn [handler value]
(swap! calls inc)
(fn [request]
(handler (into request [data value])))))}
mw3 {:compile (fn [data _]
(swap! calls inc)
{:compile (fn [data _]
(swap! calls inc)
mw)})}]
(testing "as map"
(reset! calls 0)
(let [app (create [[mw :value]])]
(dotimes [_ 10]
(is (= [:data :value :ok] (app request)))
(is (= 2 @calls)))))
(testing "as Middleware"
(reset! calls 0)
(let [app (create [[(middleware/map->Middleware mw) :value]])]
(dotimes [_ 10]
(is (= [:data :value :ok] (app request)))
(is (= 2 @calls)))))
(testing "deeply compiled Middleware"
(reset! calls 0)
(let [app (create [[(middleware/map->Middleware mw3) :value]])]
(dotimes [_ 10]
(is (= [:data :value :ok] (app request)))
(is (= 4 @calls)))))
(testing "too deeply compiled Middleware fails"
(binding [middleware/*max-compile-depth* 2]
(is (thrown?
ExceptionInfo
#"Too deep Middleware compilation"
(create [[(middleware/map->Middleware mw3) :value]])))))
(testing "nil unmounts the middleware"
(let [app (create [{:compile (constantly nil)}
{:compile (constantly nil)}])]
(dotimes [_ 10]
(is (= [:ok] (app request))))))))))
(defn create-app [router]
(let [h (middleware/middleware-handler router)]
(fn [path]
(if-let [f (h path)]
(f request)))))
(deftest middleware-handler-test
(testing "all paths should have a handler"
(is (thrown-with-msg?
ExceptionInfo
#"path \"/ping\" doesn't have a :handler defined"
(middleware/router ["/ping"]))))
(testing "middleware-handler"
(let [mw (fn [handler value]
(fn [request]
(conj (handler (conj request value)) value)))
api-mw #(mw % :api)
handler #(conj % :ok)
router (middleware/router
[["/ping" handler]
["/api" {:middleware [api-mw]}
["/ping" handler]
["/admin" {:middleware [[mw :admin]]}
["/ping" handler]]]])
app (create-app router)]
(testing "not found"
(is (= nil (app "/favicon.ico"))))
(testing "normal handler"
(is (= [:ok] (app "/ping"))))
(testing "with middleware"
(is (= [:api :ok :api] (app "/api/ping"))))
(testing "with nested middleware"
(is (= [:api :admin :ok :admin :api] (app "/api/admin/ping"))))
(testing ":compile middleware can be unmounted at creation-time"
(let [mw1 {:name ::mw1, :compile (constantly #(mw % ::mw1))}
mw2 {:name ::mw2, :compile (constantly nil)}
mw3 {:name ::mw3, :wrap #(mw % ::mw3)}
router (middleware/router
["/api" {:name ::api
:middleware [mw1 mw2 mw3 mw2]
:handler handler}])
app (create-app router)]
(is (= [::mw1 ::mw3 :ok ::mw3 ::mw1] (app "/api")))
(testing "routes contain list of actually applied mw"
(is (= [::mw1 ::mw3] (->> (r/compiled-routes router)
first
last
:middleware
(map :name)))))
(testing "match contains list of actually applied mw"
(is (= [::mw1 ::mw3] (->> "/api"
(r/match-by-path router)
:result
:middleware
(map :name))))))))))
(deftest chain-test
(testing "chain can produce middleware chain of any IntoMiddleware"
(let [mw (fn [handler value]
#(conj (handler (conj % value)) value))
handler #(conj % :ok)
mw1 {:compile (constantly #(mw % ::mw1))}
mw2 {:compile (constantly nil)}
mw3 {:wrap #(mw % ::mw3)}
mw4 #(mw % ::mw4)
mw5 {:compile (fn [{:keys [mount?]} _]
(when mount?
#(mw % ::mw5)))}
chain1 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? true})
chain2 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? false})]
(is (= [::mw1 ::mw3 ::mw4 ::mw5 :ok ::mw5 ::mw4 ::mw3 ::mw1] (chain1 [])))
(is (= [::mw1 ::mw3 ::mw4 :ok ::mw4 ::mw3 ::mw1] (chain2 []))))))
(deftest middleware-transform-test
(let [wrap (fn [handler value]
#(handler (conj % value)))
debug-mw {:name ::debug, :wrap #(wrap % ::debug)}
create (fn [options]
(create-app
(middleware/router
["/ping" {:middleware [{:name ::olipa, :wrap #(wrap % ::olipa)}
{:name ::kerran, :wrap #(wrap % ::kerran)}
{:name ::avaruus, :wrap #(wrap % ::avaruus)}]
:handler #(conj % :ok)}]
options)))]
(testing "by default, all middleware are applied in order"
(let [app (create nil)]
(is (= [::olipa ::kerran ::avaruus :ok] (app "/ping")))))
(testing "middleware can be re-ordered"
(let [app (create {::middleware/transform (partial sort-by :name)})]
(is (= [::avaruus ::kerran ::olipa :ok] (app "/ping")))))
(testing "adding debug middleware between middleware"
(let [app (create {::middleware/transform #(interleave % (repeat debug-mw))})]
(is (= [::olipa ::debug ::kerran ::debug ::avaruus ::debug :ok] (app "/ping")))))
(testing "vector of transformations"
(let [app (create {::middleware/transform [#(interleave % (repeat debug-mw))
(partial sort-by :name)]})]
(is (= [::avaruus ::debug ::debug ::debug ::kerran ::olipa :ok] (app "/ping")))))))