diff --git a/CHANGELOG.md b/CHANGELOG.md index 6de9d4ba..c8003383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,18 +14,68 @@ We use [Break Versioning][breakver]. The version numbers follow a `. [Middleware]` to transform the expanded Middleware (default: identity). -| `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware -| `:reitit.ring/default-options-handler` | Default handler for `:options` method in endpoints (default: default-options-handler) +| key | description | +| ----------------------------------------|-------------| +| `:reitit.middleware/transform` | Function of `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity). +| `:reitit.middleware/registry` | Map of `keyword => IntoMiddleware` to replace keyword references into Middleware +| `:reitit.ring/default-options-endpoint` | Default endpoint for `:options` method (default: default-options-endpoint) Example router: diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index afd39a80..fa4c26fc 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -1,6 +1,7 @@ (ns reitit.http (:require [meta-merge.core :refer [meta-merge]] [reitit.interceptor :as interceptor] + [reitit.exception :as ex] [reitit.ring :as ring] [reitit.core :as r])) @@ -13,11 +14,11 @@ (update acc method expand opts) acc)) data ring/http-methods)]) -(defn compile-result [[path data] {::keys [default-options-handler] :as opts}] +(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}] (let [[top childs] (ring/group-keys data) childs (cond-> childs - (and (not (:options childs)) (not (:handler top)) default-options-handler) - (assoc :options {:no-doc true, :handler default-options-handler})) + (and (not (:options childs)) (not (:handler top)) default-options-endpoint) + (assoc :options (expand default-options-endpoint opts))) compile (fn [[path data] opts scope] (interceptor/compile-result [path data] opts scope)) ->endpoint (fn [p d m s] @@ -47,11 +48,11 @@ support for http-methods and Interceptors. See documentation on [[reitit.core/router]] for available options. In addition, the following options are available: - | key | description - | ---------------------------------------|------------- - | `:reitit.interceptor/transform` | Function or vector of functions of type `[Interceptor] => [Interceptor]` to transform the expanded Interceptors (default: identity) - | `:reitit.interceptor/registry` | Map of `keyword => IntoInterceptor` to replace keyword references into Interceptors - | `:reitit.http/default-options-handler` | Default handler for `:options` method in endpoints (default: reitit.ring/default-options-handler) + | key | description + | ----------------------------------------|------------- + | `:reitit.interceptor/transform` | Function or vector of functions of type `[Interceptor] => [Interceptor]` to transform the expanded Interceptors (default: identity) + | `:reitit.interceptor/registry` | Map of `keyword => IntoInterceptor` to replace keyword references into Interceptors + | `:reitit.http/default-options-endpoint` | Default endpoint for `:options` method in endpoints (default: reitit.ring/default-options-endpoint) Example: @@ -66,7 +67,10 @@ ([data opts] (let [opts (merge {:coerce coerce-handler :compile compile-result - ::default-options-handler ring/default-options-handler} opts)] + ::default-options-endpoint ring/default-options-endpoint} opts)] + (when (contains? opts ::default-options-handler) + (ex/fail! (str "Option :reitit.http/default-options-handler is deprecated." + " Use :reitit.http/default-options-endpoint instead."))) (r/router data opts)))) (defn routing-interceptor diff --git a/modules/reitit-pedestal/src/reitit/pedestal.clj b/modules/reitit-pedestal/src/reitit/pedestal.clj index fcca5a46..7d53cd06 100644 --- a/modules/reitit-pedestal/src/reitit/pedestal.clj +++ b/modules/reitit-pedestal/src/reitit/pedestal.clj @@ -11,7 +11,7 @@ (defn- arities [f] (->> (class f) .getDeclaredMethods - (filter #(= "invoke" (.getName %))) + (filter (fn [^Method m] (= "invoke" (.getName m)))) (map #(alength (.getParameterTypes ^Method %))) (set))) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 463e911e..5345ad43 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -1,12 +1,12 @@ (ns reitit.ring (:require [meta-merge.core :refer [meta-merge]] [reitit.middleware :as middleware] + [reitit.exception :as ex] [reitit.core :as r] [reitit.impl :as impl] #?@(:clj [[ring.util.mime-type :as mime-type] [ring.util.response :as response]]) - [clojure.string :as str] - [reitit.exception :as ex])) + [clojure.string :as str])) (declare get-match) (declare get-router) @@ -108,7 +108,7 @@ (defn routes "Create a ring handler by combining several handlers into one." [& handlers] - (let [single-arity (apply some-fn handlers)] + (let [single-arity (apply some-fn (keep identity handlers))] (fn ([request] (single-arity request)) @@ -189,6 +189,48 @@ ;; TODO: ring.middleware.not-modified/wrap-not-modified ;; TODO: ring.middleware.head/wrap-head ;; TODO: handle etags + (defn -create-file-or-resource-handler + [response-fn {:keys [parameter root path loader allow-symlinks? index-files paths not-found-handler] + :or {parameter (keyword "") + root "public" + index-files ["index.html"] + paths (constantly nil) + not-found-handler (constantly {:status 404, :body "", :headers {}})}}] + (let [options {:root root + :loader loader + :index-files? false + :allow-symlinks? allow-symlinks?} + path-size (count path) + create (fn [handler] + (fn + ([request] (handler request)) + ([request respond _] (respond (handler request))))) + join-paths (fn [& paths] + (str/replace (str/replace (str/join "/" paths) #"([/]+)" "/") #"/$" "")) + response (fn [path] + (if-let [response (or (paths (join-paths "/" path)) + (response-fn path options))] + (response/content-type response (mime-type/ext-mime-type path)))) + path-or-index-response (fn [path uri] + (or (response path) + (loop [[file & files] index-files] + (if file + (if (response (join-paths path file)) + (response/redirect (join-paths uri file)) + (recur files)))))) + handler (if path + (fn [request] + (let [uri (:uri request)] + (if-let [path (if (>= (count uri) path-size) (subs uri path-size))] + (path-or-index-response path uri)))) + (fn [request] + (let [uri (:uri request) + path (-> request :path-params parameter)] + (or (path-or-index-response path uri) + (not-found-handler request)))))] + (create handler)))) + +#?(:clj (defn create-resource-handler "A ring handler for serving classpath resources, configured via options: @@ -202,42 +244,25 @@ | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" ([] (create-resource-handler nil)) - ([{:keys [parameter root path loader allow-symlinks? index-files paths not-found-handler] - :or {parameter (keyword "") - root "public" - index-files ["index.html"] - paths (constantly nil) - not-found-handler (constantly {:status 404, :body "", :headers {}})}}] - (let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?} - path-size (count path) - create (fn [handler] - (fn - ([request] (handler request)) - ([request respond _] (respond (handler request))))) - join-paths (fn [& paths] - (str/replace (str/replace (str/join "/" paths) #"([/]+)" "/") #"/$" "")) - resource-response (fn [path] - (if-let [response (or (paths (join-paths "/" path)) - (response/resource-response path options))] - (response/content-type response (mime-type/ext-mime-type path)))) - path-or-index-response (fn [path uri] - (or (resource-response path) - (loop [[file & files] index-files] - (if file - (if (resource-response (join-paths path file)) - (response/redirect (join-paths uri file)) - (recur files)))))) - handler (if path - (fn [request] - (let [uri (:uri request)] - (if-let [path (if (>= (count uri) path-size) (subs uri path-size))] - (path-or-index-response path uri)))) - (fn [request] - (let [uri (:uri request) - path (-> request :path-params parameter)] - (or (path-or-index-response path uri) - (not-found-handler request)))))] - (create handler))))) + ([opts] + (-create-file-or-resource-handler response/resource-response opts)))) + +#?(:clj + (defn create-file-handler + "A ring handler for serving file resources, configured via options: + + | key | description | + | -------------------|-------------| + | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` + | :root | optional resource root, defaults to `\"public\"` + | :path | optional path to mount the handler to. Works only if mounted outside of a router. + | :loader | optional class loader to resolve the resources + | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` + | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" + ([] + (create-file-handler nil)) + ([opts] + (-create-file-or-resource-handler response/file-response opts)))) (defn create-enrich-request [inject-match? inject-router?] (cond diff --git a/project.clj b/project.clj index f73b0230..a5983172 100644 --- a/project.clj +++ b/project.clj @@ -27,33 +27,33 @@ [metosin/reitit-frontend "0.4.2"] [metosin/reitit-sieppari "0.4.2"] [metosin/reitit-pedestal "0.4.2"] - [metosin/ring-swagger-ui "3.24.3"] - [metosin/spec-tools "0.10.0"] - [metosin/schema-tools "0.12.1"] - [metosin/muuntaja "0.6.6"] - [metosin/jsonista "0.2.5"] - [metosin/sieppari "0.0.0-alpha8"] + [metosin/ring-swagger-ui "3.25.0"] + [metosin/spec-tools "0.10.2"] + [metosin/schema-tools "0.12.2"] + [metosin/muuntaja "0.6.7"] + [metosin/jsonista "0.2.6"] + [metosin/sieppari "0.0.0-alpha9"] [metosin/malli "0.0.1-20200404.091302-14"] ;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111 - [com.fasterxml.jackson.core/jackson-core "2.10.0"] + [com.fasterxml.jackson.core/jackson-core "2.11.0"] [meta-merge "1.0.0"] - [fipp "0.6.22" :exclusions [org.clojure/core.rrb-vector]] + [fipp "0.6.23" :exclusions [org.clojure/core.rrb-vector]] [expound "0.8.4"] [lambdaisland/deep-diff "0.0-47"] - [com.bhauman/spell-spec "0.1.1"] - [ring/ring-core "1.8.0"] + [com.bhauman/spell-spec "0.1.2"] + [ring/ring-core "1.8.1"] [io.pedestal/pedestal.service "0.5.7"]] - :plugins [[jonase/eastwood "0.3.6"] + :plugins [[jonase/eastwood "0.3.11"] ;[lein-virgil "0.1.7"] [lein-doo "0.1.11"] - [lein-cljsbuild "1.1.7"] + [lein-cljsbuild "1.1.8"] [lein-cloverage "1.1.2"] [lein-codox "0.10.7"] - [metosin/bat-test "0.4.3"]] + [metosin/bat-test "0.4.4"]] :profiles {:dev {:jvm-opts ^:replace ["-server"] @@ -80,37 +80,37 @@ [org.clojure/clojurescript "1.10.597"] ;; modules dependencies - [metosin/schema-tools] - [metosin/spec-tools] - [metosin/muuntaja] + [metosin/schema-tools "0.12.2"] + [metosin/spec-tools "0.10.2"] + [metosin/muuntaja "0.6.7"] [metosin/sieppari] - [metosin/jsonista] + [metosin/jsonista "0.2.6"] [metosin/malli] - [lambdaisland/deep-diff] - [meta-merge] - [com.bhauman/spell-spec] - [expound] - [fipp] + [lambdaisland/deep-diff "0.0-47"] + [meta-merge "1.0.0"] + [com.bhauman/spell-spec "0.1.1"] + [expound "0.8.4"] + [fipp "0.6.23"] [orchestra "2019.02.06-1"] - [ring "1.8.0"] + [ring "1.8.1"] [ikitommi/immutant-web "3.0.0-alpha1"] [metosin/ring-http-response "0.9.1"] - [metosin/ring-swagger-ui "2.2.10"] + [metosin/ring-swagger-ui "3.25.0"] [criterium "0.4.5"] - [org.clojure/test.check "0.10.0"] - [org.clojure/tools.namespace "0.3.1"] + [org.clojure/test.check "1.0.0"] + [org.clojure/tools.namespace "1.0.0"] [com.gfredericks/test.chuck "0.2.10"] [io.pedestal/pedestal.service "0.5.7"] - [org.clojure/core.async "0.6.532"] + [org.clojure/core.async "1.1.587"] [manifold "0.1.8"] - [funcool/promesa "4.0.2"] + [funcool/promesa "5.1.0"] - [com.clojure-goes-fast/clj-async-profiler "0.4.0"] + [com.clojure-goes-fast/clj-async-profiler "0.4.1"] [ring-cors "0.1.13"] [com.bhauman/rebel-readline "0.1.4"]]} @@ -125,16 +125,16 @@ [io.pedestal/pedestal.service "0.5.7"] [io.pedestal/pedestal.jetty "0.5.7"] [calfpath "0.7.2"] - [org.clojure/core.async "0.6.532"] + [org.clojure/core.async "1.1.587"] [manifold "0.1.8"] - [funcool/promesa "4.0.2"] + [funcool/promesa "5.1.0"] [metosin/sieppari] [yada "1.2.16"] [aleph "0.4.6"] [ring/ring-defaults "0.3.2"] [ataraxy "0.4.2"] [bidi "2.1.6"] - [janus "1.3.0"]]} + [janus "1.3.2"]]} :analyze {:jvm-opts ^:replace ["-server" "-Dclojure.compiler.direct-linking=true" "-XX:+PrintCompilation" diff --git a/test/clj/reitit/http_test.clj b/test/clj/reitit/http_test.clj index 4235b9ce..8c2c5ba4 100644 --- a/test/clj/reitit/http_test.clj +++ b/test/clj/reitit/http_test.clj @@ -6,7 +6,8 @@ [reitit.interceptor.sieppari :as sieppari] [reitit.http :as http] [reitit.ring :as ring] - [reitit.core :as r])) + [reitit.core :as r]) + (:import (clojure.lang ExceptionInfo))) (defn interceptor [name] {:enter (fn [ctx] (update-in ctx [:request ::i] (fnil conj []) name))}) @@ -169,6 +170,10 @@ (testing "handler rejects" (is (= -406 (:status (app {:request-method :get, :uri "/pong"})))))))))) +(deftest default-options-handler-test + (testing "Assertion fails when using deprecated options-handler" + (is (thrown? ExceptionInfo (ring/router [] {::ring/default-options-handler (fn [_])}))))) + (deftest default-options-handler-test (let [response {:status 200, :body "ok"}] @@ -199,7 +204,7 @@ [["/get" {:get (constantly response)}] ["/options" {:options (constantly response)}] ["/any" (constantly response)]] - {::http/default-options-handler nil}) + {::http/default-options-endpoint nil}) {:executor sieppari/executor})] (testing "endpoint with a non-options handler" diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 1c40defd..8cc1fb2b 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -24,6 +24,15 @@ ([request respond _] (respond (handler request)))) +(deftest routes-test + (testing "nils are removed" + (is (= 123 + ((ring/routes + (constantly nil) + nil + (constantly 123)) + ::irrelevant))))) + (deftest ring-router-test (testing "all paths should have a handler" @@ -161,8 +170,8 @@ (deftest default-handler-test (let [response {:status 200, :body "ok"} router (ring/router - [["/ping" {:get (constantly response)}] - ["/pong" (constantly nil)]]) + [["/ping" {:get (constantly response)}] + ["/pong" (constantly nil)]]) app (ring/ring-handler router)] (testing "match" @@ -188,9 +197,9 @@ (testing "with custom http responses" (let [app (ring/ring-handler router (ring/create-default-handler - {:not-found (constantly {:status -404}) - :method-not-allowed (constantly {:status -405}) - :not-acceptable (constantly {:status -406})}))] + {:not-found (constantly {:status -404}) + :method-not-allowed (constantly {:status -405}) + :not-acceptable (constantly {:status -406})}))] (testing "route doesn't match" (is (= -404 (:status (app {:request-method :get, :uri "/"}))))) (testing "method doesn't match" @@ -200,7 +209,7 @@ (testing "with some custom http responses" (let [app (ring/ring-handler router (ring/create-default-handler - {:not-found (constantly {:status -404})}))] + {:not-found (constantly {:status -404})}))] (testing "route doesn't match" (is (= 405 (:status (app {:request-method :post, :uri "/ping"})))))))))) @@ -445,158 +454,163 @@ (app request))))))) #?(:clj - (deftest resource-handler-test + (deftest file-resource-handler-test (let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}}) request (fn [uri] {:uri uri, :request-method :get})] - (testing "inside a router" - (testing "from root" - (let [app (ring/ring-handler - (ring/router - ["/*" (ring/create-resource-handler)]) - (ring/create-default-handler))] - (testing test - (testing "different file-types" - (let [response (app (request "/hello.json"))] - (is (= "application/json" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) - (let [response (app (request "/hello.xml"))] - (is (= "text/xml" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body response)))))) + (doseq [[name create] [["resource-handler" ring/create-resource-handler] + ["file-handler" #(ring/create-file-handler (assoc % :root "dev-resources/public"))]]] - (testing "index-files" - (let [response (app (request "/docs"))] - (is (= (redirect "/docs/index.html") response))) - (let [response (app (request "/docs/"))] - (is (= (redirect "/docs/index.html") response)))) + (testing (str "for " name) + (testing "inside a router" - (testing "not found" - (let [response (app (request "/not-found"))] - (is (= 404 (:status response))))) + (testing "from root" + (let [app (ring/ring-handler + (ring/router + ["/*" (create nil)]) + (ring/create-default-handler))] + (testing test + (testing "different file-types" + (let [response (app (request "/hello.json"))] + (is (= "application/json" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) + (let [response (app (request "/hello.xml"))] + (is (= "text/xml" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body response)))))) - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result) - raise ::not-called] - (app (request "/hello.xml") respond raise) - (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) - (is (get-in @result [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body @result))))))))) + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= (redirect "/docs/index.html") response))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response)))) - (testing "from path" - (let [app (ring/ring-handler - (ring/router - ["/files/*" (ring/create-resource-handler)]) - (ring/create-default-handler)) - request #(request (str "/files" %)) - redirect #(redirect (str "/files" %))] - (testing test - (testing "different file-types" - (let [response (app (request "/hello.json"))] - (is (= "application/json" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) - (let [response (app (request "/hello.xml"))] - (is (= "text/xml" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body response)))))) + (testing "not found" + (let [response (app (request "/not-found"))] + (is (= 404 (:status response))))) - (testing "index-files" - (let [response (app (request "/docs"))] - (is (= (redirect "/docs/index.html") response))) - (let [response (app (request "/docs/"))] - (is (= (redirect "/docs/index.html") response)))) + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result) + raise ::not-called] + (app (request "/hello.xml") respond raise) + (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) + (is (get-in @result [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body @result))))))))) - (testing "not found" - (let [response (app (request "/not-found"))] - (is (= 404 (:status response))))) + (testing "from path" + (let [app (ring/ring-handler + (ring/router + ["/files/*" (create nil)]) + (ring/create-default-handler)) + request #(request (str "/files" %)) + redirect #(redirect (str "/files" %))] + (testing test + (testing "different file-types" + (let [response (app (request "/hello.json"))] + (is (= "application/json" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) + (let [response (app (request "/hello.xml"))] + (is (= "text/xml" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body response)))))) - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result) - raise ::not-called] - (app (request "/hello.xml") respond raise) - (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) - (is (get-in @result [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body @result)))))))))) + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= (redirect "/docs/index.html") response))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response)))) - (testing "outside a router" + (testing "not found" + (let [response (app (request "/not-found"))] + (is (= 404 (:status response))))) - (testing "from root" - (let [app (ring/ring-handler - (ring/router []) - (ring/routes - (ring/create-resource-handler {:path "/"}) - (ring/create-default-handler)))] - (testing test - (testing "different file-types" - (let [response (app (request "/hello.json"))] - (is (= "application/json" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) - (let [response (app (request "/hello.xml"))] - (is (= "text/xml" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body response)))))) + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result) + raise ::not-called] + (app (request "/hello.xml") respond raise) + (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) + (is (get-in @result [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body @result)))))))))) - (testing "index-files" - (let [response (app (request "/docs"))] - (is (= (redirect "/docs/index.html") response))) - (let [response (app (request "/docs/"))] - (is (= (redirect "/docs/index.html") response)))) + (testing "outside a router" - (testing "not found" - (let [response (app (request "/not-found"))] - (is (= 404 (:status response))))) + (testing "from root" + (let [app (ring/ring-handler + (ring/router []) + (ring/routes + (create {:path "/"}) + (ring/create-default-handler)))] + (testing test + (testing "different file-types" + (let [response (app (request "/hello.json"))] + (is (= "application/json" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) + (let [response (app (request "/hello.xml"))] + (is (= "text/xml" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body response)))))) - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result) - raise ::not-called] - (app (request "/hello.xml") respond raise) - (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) - (is (get-in @result [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body @result))))))))) + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= (redirect "/docs/index.html") response))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response)))) - (testing "from path" - (let [app (ring/ring-handler - (ring/router []) - (ring/routes - (ring/create-resource-handler {:path "/files"}) - (ring/create-default-handler))) - request #(request (str "/files" %)) - redirect #(redirect (str "/files" %))] - (testing test - (testing "different file-types" - (let [response (app (request "/hello.json"))] - (is (= "application/json" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) - (let [response (app (request "/hello.xml"))] - (is (= "text/xml" (get-in response [:headers "Content-Type"]))) - (is (get-in response [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body response)))))) + (testing "not found" + (let [response (app (request "/not-found"))] + (is (= 404 (:status response))))) - (testing "index-files" - (let [response (app (request "/docs"))] - (is (= (redirect "/docs/index.html") response))) - (let [response (app (request "/docs/"))] - (is (= (redirect "/docs/index.html") response)))) + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result) + raise ::not-called] + (app (request "/hello.xml") respond raise) + (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) + (is (get-in @result [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body @result))))))))) - (testing "not found" - (let [response (app (request "/not-found"))] - (is (= 404 (:status response))))) + (testing "from path" + (let [app (ring/ring-handler + (ring/router []) + (ring/routes + (create {:path "/files"}) + (ring/create-default-handler))) + request #(request (str "/files" %)) + redirect #(redirect (str "/files" %))] + (testing test + (testing "different file-types" + (let [response (app (request "/hello.json"))] + (is (= "application/json" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "{\"hello\": \"file\"}" (slurp (:body response))))) + (let [response (app (request "/hello.xml"))] + (is (= "text/xml" (get-in response [:headers "Content-Type"]))) + (is (get-in response [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body response)))))) - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result) - raise ::not-called] - (app (request "/hello.xml") respond raise) - (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) - (is (get-in @result [:headers "Last-Modified"])) - (is (= "file\n" (slurp (:body @result))))))))))))) + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= (redirect "/docs/index.html") response))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response)))) + + (testing "not found" + (let [response (app (request "/not-found"))] + (is (= 404 (:status response))))) + + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result) + raise ::not-called] + (app (request "/hello.xml") respond raise) + (is (= "text/xml" (get-in @result [:headers "Content-Type"]))) + (is (get-in @result [:headers "Last-Modified"])) + (is (= "file\n" (slurp (:body @result))))))))))))))) (deftest router-available-in-default-branch (testing "1-arity"