Merge pull request #96 from metosin/0.1.2

0.1.2
This commit is contained in:
Tommi Reiman 2018-06-06 10:34:51 +03:00 committed by GitHub
commit 93470fd176
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 408 additions and 135 deletions

View file

@ -1,5 +1,26 @@
## 0.1.2-SNAPSHOT
### `reitit-core`
* Better handling of `nil` in route syntax:
* explicit `nil` after path string is always handled as `nil` route
* `nil` as path string causes the whole route to be `nil`
* `nil` as child route is stripped away
```clj
(testing "nil routes are stripped"
(is (= [] (r/routes (r/router nil))))
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
(is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
```
### `reitit-ring`
* Use HTTP redirect (302) with index-files in `reitit.ring/create-resource-handler`.
* `reitit.ring/create-default-handler` now conforms to [RING Spec](https://github.com/ring-clojure/ring/blob/master/SPEC), Fixes [#83](https://github.com/metosin/reitit/issues/83)
https://github.com/metosin/reitit/issues/83
### `reitit-schema`
* updated dependencies:
@ -14,6 +35,8 @@
### `reitit-swagger-ui`
* Use HTTP redirect (302) with index-files in `reitit.swagger-ui/create-swagger-ui-handler`.
* updated dependencies:
```clj

View file

@ -183,7 +183,7 @@ Whole example project is in [`/examples/ring-swagger`](https://github.com/metosi
"application/transit+json"}}}})
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "", :url "/api/swagger.json"})
{:path "/", :url "/api/swagger.json"})
(ring/create-default-handler))))
(defn start []

View file

@ -3,4 +3,4 @@
:dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"]
[metosin/muuntaja "0.4.1"]
[metosin/reitit "0.1.1"]])
[metosin/reitit "0.1.2-SNAPSHOT"]])

View file

@ -3,4 +3,4 @@
:dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"]
[metosin/muuntaja "0.4.1"]
[metosin/reitit "0.1.1"]])
[metosin/reitit "0.1.2-SNAPSHOT"]])

View file

@ -3,5 +3,5 @@
:dependencies [[org.clojure/clojure "1.9.0"]
[ring "1.6.3"]
[metosin/muuntaja "0.5.0"]
[metosin/reitit "0.1.1"]]
[metosin/reitit "0.1.2-SNAPSHOT"]]
:repl-options {:init-ns example.server})

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-core "0.1.1"
(defproject metosin/reitit-core "0.1.2-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -39,14 +39,14 @@
(walk-one [pacc macc routes]
(if (vector? (first routes))
(walk-many pacc macc routes)
(let [[path & [maybe-arg :as args]] routes
[data childs] (if (vector? maybe-arg)
[{} args]
[maybe-arg (rest args)])
macc (into macc (expand data opts))]
(if (seq childs)
(walk-many (str pacc path) macc childs)
[[(str pacc path) macc]]))))]
(when (string? (first routes))
(let [[path & [maybe-arg :as args]] routes
[data childs] (if (or (vector? maybe-arg) (nil? maybe-arg))
[{} args]
[maybe-arg (rest args)])
macc (into macc (expand data opts))
child-routes (walk-many (str pacc path) macc (keep identity childs))]
(if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
(walk-one path (mapv identity data) raw-routes)))
(defn map-data [f routes]
@ -87,10 +87,10 @@
(conflicts-str conflicts)
{:conflicts conflicts})))
(defn name-lookup [[_ {:keys [name]}] opts]
(defn name-lookup [[_ {:keys [name]}] _]
(if name #{name}))
(defn find-names [routes opts]
(defn find-names [routes _]
(into [] (keep #(-> % second :name)) routes))
(defn- compile-route [[p m :as route] {:keys [compile] :as opts}]

View file

@ -9,18 +9,19 @@
(s/def ::path (s/with-gen string? #(gen/fmap (fn [s] (str "/" s)) (s/gen string?))))
(s/def ::arg (s/and any? (complement vector?)))
(s/def ::arg (s/and some? (complement vector?)))
(s/def ::data (s/map-of keyword? any?))
(s/def ::result any?)
(s/def ::raw-route
(s/cat :path ::path
:arg (s/? ::arg)
:childs (s/* (s/and (s/nilable ::raw-route)))))
(s/nilable
(s/cat :path ::path
:arg (s/? ::arg)
:childs (s/* (s/and (s/nilable ::raw-routes))))))
(s/def ::raw-routes
(s/or :route ::raw-route
:routes (s/coll-of ::raw-route :into [])))
:routes (s/coll-of ::raw-routes :into [])))
(s/def ::route
(s/cat :path ::path

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-ring "0.1.1"
(defproject metosin/reitit-ring "0.1.2-SNAPSHOT"
:description "Reitit: Ring routing"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -46,9 +46,9 @@
| `:not-acceptable` | 406, handler returned `nil`"
([]
(create-default-handler
{:not-found (constantly {:status 404, :body ""})
:method-not-allowed (constantly {:status 405, :body ""})
:not-acceptable (constantly {:status 406, :body ""})}))
{:not-found (constantly {:status 404, :body "", :headers {}})
:method-not-allowed (constantly {:status 405, :body "", :headers {}})
:not-acceptable (constantly {:status 406, :body "", :headers {}})}))
([{:keys [not-found method-not-allowed not-acceptable]}]
(fn
([request]
@ -96,24 +96,30 @@
(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 path) (response/resource-response path options))]
(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]
path-or-index-response (fn [path uri]
(or (resource-response path)
(let [separator (if-not (str/ends-with? path "/") "/")]
(loop [[file & files] index-files]
(if file
(or (resource-response (str path separator file))
(recur files)))))))
(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))))
(path-or-index-response path uri))))
(fn [request]
(let [path (-> request :path-params parameter)]
(or (path-or-index-response path) {:status 404}))))]
(let [uri (:uri request)
path (-> request :path-params parameter)]
(or (path-or-index-response path uri)
;; TODO: use generic not-found handler
{:status 404}))))]
(create handler)))))
(defn ring-handler
@ -129,7 +135,7 @@
(fn
([request]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request :any)
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))
@ -141,7 +147,7 @@
(default-handler request)))
([request respond raise]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request :any)
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-schema "0.1.1"
(defproject metosin/reitit-schema "0.1.2-SNAPSHOT"
:description "Reitit: Plumatic Schema coercion"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-spec "0.1.1"
(defproject metosin/reitit-spec "0.1.2-SNAPSHOT"
:description "Reitit: clojure.spec coercion"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-swagger-ui "0.1.1"
(defproject metosin/reitit-swagger-ui "0.1.2-SNAPSHOT"
:description "Reitit: Swagger-ui support"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -26,7 +26,7 @@
;; with path and url set, swagger validator disabled
(swagger-ui/create-swagger-ui-handler
{:path \"\"
{:path \"/\"
:url \"/api/swagger.json\"
:config {:validator-url nil})"
([]
@ -42,11 +42,11 @@
(update $ :root (fnil identity "swagger-ui"))
(update $ :url (fnil identity "/swagger.json"))
(update $ :config #(->> % (map mixed-case-key) (into {})))
(assoc $ :paths {"conf.js" {:headers {"Content-Type" "application/javascript"}
:status 200
:body (conf-js $)}
"config.json" {:headers {"Content-Type" "application/json"}
:status 200
:body (config-json $)}}))]
(assoc $ :paths {"/conf.js" {:headers {"Content-Type" "application/javascript"}
:status 200
:body (conf-js $)}
"/config.json" {:headers {"Content-Type" "application/json"}
:status 200
:body (config-json $)}}))]
(ring/routes
(ring/create-resource-handler options))))))

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-swagger "0.1.1"
(defproject metosin/reitit-swagger "0.1.2-SNAPSHOT"
:description "Reitit: Swagger-support"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit "0.1.1"
(defproject metosin/reitit "0.1.2-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"

View file

@ -10,7 +10,8 @@
[io.pedestal.http.route.definition.table :as table]
[io.pedestal.http.route.map-tree :as map-tree]
[io.pedestal.http.route.router :as pedestal]
[io.pedestal.http.route :as route]))
[io.pedestal.http.route :as route]
[reitit.ring :as ring]))
;;
;; start repl with `lein perf repl`
@ -62,6 +63,26 @@
["/auth/recovery/token/:token" :auth/recovery]
["/workspace/:project/:page" :workspace/page]]))
(def ring-app
(ring/ring-handler
(ring/router
[["/auth/login" {:get identity}]
["/auth/recovery/token/:token" {:get identity}]
["/workspace/:project/:page" {:get identity}]])))
(comment
(ring-app {:request-method :get, :uri "/auth/login"})
;; 213ns
;; 204ns (remove if)
;; 163ns (inline fast-assoc)
;; 156ns (don't inline fast-assoc)
;; 128ns (single method dispatch)
;; 80ns --> (don't inject router & match)
(cc/quick-bench
(ring-app {:request-method :post, :uri "/auth/login"})))
(defn routing-test1 []
(suite "static route")

View file

@ -0,0 +1,109 @@
(ns reitit.json-perf
(:require [criterium.core :as cc]
[reitit.perf-utils :refer :all]
;; reitit
[reitit.ring :as ring]
[muuntaja.middleware :as mm]
;; bidi-yada
[yada.yada :as yada]
[bidi.ring :as bidi-ring]
[byte-streams :as bs]
;; defaults
[ring.middleware.defaults :as defaults]
[compojure.core :as compojure]
[clojure.string :as str]))
;;
;; start repl with `lein perf repl`
;; perf measured with the following setup:
;;
;; Model Name: MacBook Pro
;; Model Identifier: MacBookPro113
;; Processor Name: Intel Core i7
;; Processor Speed: 2,5 GHz
;; Number of Processors: 1
;; Total Number of Cores: 4
;; L2 Cache (per Core): 256 KB
;; L3 Cache: 6 MB
;; Memory: 16 GB
;;
;; TODO: naive implementation
(defn- with-security-headers [response]
(update
response
:headers
(fn [headers]
(-> headers
(assoc "x-frame-options" "SAMEORIGIN")
(assoc "x-xss-protection" "1; mode=block")
(assoc "x-content-type-options" "nosniff")))))
(def security-middleware
{:name ::security
:wrap (fn [handler]
(fn [request]
(with-security-headers (handler request))))})
(def reitit-app
(ring/ring-handler
(ring/router
["/api/ping"
{:get {:handler (fn [_] {:status 200, :body {:ping "pong"}})}}]
{:data {:middleware [mm/wrap-format
security-middleware]}})))
(def bidi-yada-app
(bidi-ring/make-handler
["/api/ping"
(yada/resource
{:produces {:media-type "application/json"}
:methods {:get {:response (fn [_] {:ping "pong"})}}})]))
(def defaults-app
(defaults/wrap-defaults
(mm/wrap-format
(compojure/GET "/api/ping" [] {:status 200, :body {:ping "pong"}}))
defaults/site-defaults))
(def request {:request-method :get, :uri "/api/ping"})
(comment
(defaults-app request)
@(bidi-yada-app request)
(reitit-app request))
(comment
(slurp (:body (defaults-app request)))
(slurp (:body (reitit-app request)))
(bs/to-string (:body @(bidi-yada-app request))))
(defn expect! [body]
(assert (str/starts-with? body "{\"ping\":\"pong\"}")))
(defn perf-test []
;; 176µs
(title "compojure + ring-defaults")
(let [f (fn [] (defaults-app request))]
(expect! (-> (f) :body slurp))
(cc/quick-bench (f)))
;; 60µs
(title "bidi + yada")
(let [f (fn [] (bidi-yada-app request))]
(expect! (-> (f) deref :body bs/to-string))
(cc/quick-bench (f)))
;; 5.0µs
(title "reitit-ring")
(let [f (fn [] (reitit-app request))]
(expect! (-> (f) :body slurp))
(cc/quick-bench (f))))
(comment
(perf-test))

View file

@ -31,7 +31,7 @@
(defrecord RequestOrContext [values queue stack])
(def +items+ 100)
(def +items+ 10)
(defn expected! [x]
(assert (= (range +items+) (:values x))))
@ -47,18 +47,19 @@
map-request {}
record-request (map->RequestOrContext map-request)]
;; 10.8 µs
;; 1000ns
(title "middleware - map")
(expected! (app map-request))
(cc/quick-bench
(app map-request))
;; 4.7 µs
;; 365ns
(title "middleware - record")
(expected! (app record-request))
(cc/quick-bench
(app record-request))
;; 6900ns
(title "middleware - dynamic")
(expected! ((middleware/chain mw identity) record-request))
(cc/quick-bench
@ -110,21 +111,21 @@
{:enter (interceptor value)}) (range +items+)))
ctx (io.pedestal.interceptor.chain/enqueue nil is)]
;; 78 µs
;; 8400ns
(title "pedestal")
(cc/quick-bench
(io.pedestal.interceptor.chain/execute ctx))))
(defn pedestal-tuned-chain-text []
(let [is (map io.pedestal.interceptor/interceptor
(map (fn [value]
{:enter (interceptor value)}) (range +items+)))
ctx (reitit.chain/map->Context (reitit.chain/enqueue nil is))]
#_(defn pedestal-tuned-chain-text []
(let [is (map io.pedestal.interceptor/interceptor
(map (fn [value]
{:enter (interceptor value)}) (range +items+)))
ctx (reitit.chain/map->Context (reitit.chain/enqueue nil is))]
;; 67 µs
(title "pedestal - tuned")
(cc/quick-bench
(reitit.chain/execute ctx))))
;; 67 µs
(title "pedestal - tuned")
(cc/quick-bench
(reitit.chain/execute ctx))))
;;
;; Naive chain
@ -140,17 +141,17 @@
(defn interceptor-test []
(let [interceptors (map (fn [value] [interceptor value]) (range +items+))
app (executor-reduce (interceptor/chain interceptors identity))
app (executor-reduce (interceptor/chain interceptors identity {}))
map-request {}
record-request (map->RequestOrContext map-request)]
;; 13.5 µs (Map)
;; 1900ns
(title "interceptors - map")
(expected! (app map-request))
(cc/quick-bench
(app map-request))
;; 7.2 µs (Record)
;; 1300ns
(title "interceptors - record")
(expected! (app record-request))
(cc/quick-bench
@ -212,24 +213,24 @@
(defn interceptor-chain-test []
(let [interceptors (map (fn [value] [interceptor value]) (range +items+))
app-reduce (executor-reduce (interceptor/chain interceptors identity))
app-queue (executor-queue (interceptor/chain interceptors identity))
app-ctx-queue (executor-ctx-queue (interceptor/chain interceptors identity))
app-reduce (executor-reduce (interceptor/chain interceptors identity {}))
app-queue (executor-queue (interceptor/chain interceptors identity {}))
app-ctx-queue (executor-ctx-queue (interceptor/chain interceptors identity {}))
request {}]
;; 14.2 µs
;; 2000ns
(title "interceptors - reduce")
(expected! (app-reduce request))
(cc/quick-bench
(app-reduce request))
;; 19.4 µs
;; 2500ns
(title "interceptors - queue")
(expected! (app-queue request))
(cc/quick-bench
(app-queue request))
;; 30.9 µs
;; 3200ns
(title "interceptors - ctx-queue")
(expected! (app-ctx-queue request))
(cc/quick-bench

View file

@ -1,4 +1,4 @@
(defproject metosin/reitit-parent "0.1.1"
(defproject metosin/reitit-parent "0.1.2-SNAPSHOT"
:description "Snappy data-driven router for Clojure(Script)"
:url "https://github.com/metosin/reitit"
:license {:name "Eclipse Public License"
@ -9,13 +9,13 @@
:source-uri "https://github.com/metosin/reitit/{version}/{filepath}#L{line}"
:metadata {:doc/format :markdown}}
:managed-dependencies [[metosin/reitit "0.1.1"]
[metosin/reitit-core "0.1.1"]
[metosin/reitit-ring "0.1.1"]
[metosin/reitit-spec "0.1.1"]
[metosin/reitit-schema "0.1.1"]
[metosin/reitit-swagger "0.1.1"]
[metosin/reitit-swagger-ui "0.1.1"]
:managed-dependencies [[metosin/reitit "0.1.2-SNAPSHOT"]
[metosin/reitit-core "0.1.2-SNAPSHOT"]
[metosin/reitit-ring "0.1.2-SNAPSHOT"]
[metosin/reitit-spec "0.1.2-SNAPSHOT"]
[metosin/reitit-schema "0.1.2-SNAPSHOT"]
[metosin/reitit-swagger "0.1.2-SNAPSHOT"]
[metosin/reitit-swagger-ui "0.1.2-SNAPSHOT"]
[meta-merge "1.0.0"]
[ring/ring-core "1.6.3"]
@ -53,7 +53,7 @@
[ring "1.6.3"]
[ikitommi/immutant-web "3.0.0-alpha1"]
[metosin/muuntaja "0.5.0"]
[metosin/muuntaja "0.6.0-SNAPSHOT"]
[metosin/ring-swagger-ui "2.2.10"]
[metosin/jsonista "0.2.1"]
@ -70,6 +70,8 @@
[ikitommi/immutant-web "3.0.0-alpha1"]
[io.pedestal/pedestal.route "0.5.3"]
[org.clojure/core.async "0.4.474"]
[yada "1.2.13"]
[ring/ring-defaults "0.3.1"]
[ataraxy "0.4.0"]
[bidi "2.1.3"]]}
:analyze {:jvm-opts ^:replace ["-server"

View file

@ -108,6 +108,12 @@
r/segment-router :segment-router
r/mixed-router :mixed-router))
(testing "nil routes are stripped"
(is (= [] (r/routes (r/router nil))))
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
(is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
(testing "route coercion & compilation"
(testing "custom compile"

View file

@ -267,56 +267,154 @@
#?(:clj
(deftest resource-handler-test
(doseq [[test app] [["inside a router"
(ring/ring-handler
(ring/router
[["/ping" (constantly {:status 200, :body "pong"})]
["/files/*" (ring/create-resource-handler)]
["/*" (ring/create-resource-handler)]]
{:conflicts (constantly nil)})
(ring/create-default-handler))]
(let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}})
request (fn [uri] {:uri uri, :request-method :get})]
(testing "inside a router"
["outside of a router"
(ring/ring-handler
(ring/router
["/ping" (constantly {:status 200, :body "pong"})])
(ring/routes
(ring/create-resource-handler {:path "/files"})
(ring/create-resource-handler {:path "/"})
(ring/create-default-handler)))]]
prefix ["" "/" "/files" "/files/"]
:let [request (fn [uri] {:uri (str prefix uri), :request-method :get})]]
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body response))))))
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body 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 "index-files"
(let [response (app (request "/docs"))]
(is (= "text/html" (get-in response [:headers "Content-Type"])))
(is (get-in response [:headers "Last-Modified"]))
(is (= "<h1>hello</h1>\n" (slurp (:body response)))))
(let [response (app (request "/docs/"))]
(is (= "text/html" (get-in response [:headers "Content-Type"])))
(is (get-in response [:headers "Last-Modified"]))
(is (= "<h1>hello</h1>\n" (slurp (:body response))))))
(testing "not found"
(let [response (app (request "/not-found"))]
(is (= 404 (:status 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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body 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 "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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))
(testing "outside a router"
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body 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 "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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))
(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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body 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 "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 (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result)))))))))))))

View file

@ -1,14 +1,20 @@
(ns reitit.spec-test
(:require [clojure.test :refer [deftest testing is are]]
(:require [clojure.test :refer [deftest testing is are use-fixtures]]
[#?(:clj clojure.spec.test.alpha :cljs cljs.spec.test.alpha) :as stest]
[clojure.spec.alpha :as s]
[reitit.core :as r]
[reitit.spec :as rs]
[expound.alpha :as e])
[reitit.spec :as rs])
#?(:clj
(:import (clojure.lang ExceptionInfo))))
(stest/instrument)
(defn instrument-all [f]
(try
(stest/instrument)
(f)
(finally
(stest/unstrument))))
(use-fixtures :each instrument-all)
(deftest router-spec-test
@ -40,9 +46,9 @@
;; path
[:invalid {}]
;; vector data
["/api" []
["/ipa"]])))
;; nested path
["/api"
[:ipa]])))
(testing "routes conform to spec (can't spec protocol functions)"
(is (s/valid? ::rs/routes (r/routes (r/router ["/ping"])))))