mirror of
https://github.com/metosin/reitit.git
synced 2025-12-23 02:41:10 +00:00
266 lines
9.7 KiB
Clojure
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")))))))
|