|
| 1 | +(ns drawbridge.core-test |
| 2 | + (:require [clojure.string :as str] |
| 3 | + [clojure.test :refer [are deftest is testing]] |
| 4 | + [cheshire.core :as json] |
| 5 | + [drawbridge.core :as drawbridge] |
| 6 | + [nrepl.transport :as transport] |
| 7 | + [ring.middleware.keyword-params :refer [wrap-keyword-params]] |
| 8 | + [ring.middleware.nested-params :refer [wrap-nested-params]] |
| 9 | + [ring.middleware.params :refer [wrap-params]])) |
| 10 | + |
| 11 | +(defn- make-handler |
| 12 | + [& opts] |
| 13 | + (-> (apply drawbridge/ring-handler opts) |
| 14 | + wrap-keyword-params |
| 15 | + wrap-nested-params |
| 16 | + wrap-params)) |
| 17 | + |
| 18 | +(defn- parse-body |
| 19 | + [body] |
| 20 | + (json/parse-string (apply str body) true)) |
| 21 | + |
| 22 | +(defn- encode-params [params] |
| 23 | + (str/join "&" (map (fn [[k v]] |
| 24 | + (str (name k) "=" (java.net.URLEncoder/encode (str v) "UTF-8"))) |
| 25 | + params))) |
| 26 | + |
| 27 | +(defn- request |
| 28 | + [method & [params headers]] |
| 29 | + (let [qs (when (seq params) (encode-params params))] |
| 30 | + (cond-> {:request-method method |
| 31 | + :uri "/repl" |
| 32 | + :headers (or headers {})} |
| 33 | + (and (= method :get) qs) |
| 34 | + (assoc :query-string qs) |
| 35 | + |
| 36 | + (= method :post) |
| 37 | + (-> (assoc :body (java.io.ByteArrayInputStream. |
| 38 | + (.getBytes (or qs "") "UTF-8"))) |
| 39 | + (assoc-in [:headers "content-type"] "application/x-www-form-urlencoded"))))) |
| 40 | + |
| 41 | +(defn- get-set-cookie [response] |
| 42 | + (let [v (get-in response [:headers "Set-Cookie"])] |
| 43 | + (cond |
| 44 | + (string? v) v |
| 45 | + (sequential? v) (str/join "; " v)))) |
| 46 | + |
| 47 | +(defn- extract-session-cookie [response cookie-name] |
| 48 | + (some->> (get-set-cookie response) |
| 49 | + (re-find (re-pattern (str cookie-name "=([^;]+)"))) |
| 50 | + second)) |
| 51 | + |
| 52 | +(deftest method-rejection |
| 53 | + (let [handler (make-handler)] |
| 54 | + (testing "unsupported methods return 405 with illegal method error" |
| 55 | + (are [method] (let [resp (handler (request method)) |
| 56 | + body (json/parse-string (:body resp) true)] |
| 57 | + (and (= 405 (:status resp)) |
| 58 | + (= "Method Not Allowed" (:error body)) |
| 59 | + (re-find #"Only GET and POST" (:reason body)))) |
| 60 | + :put :delete :patch)) |
| 61 | + |
| 62 | + (testing "GET with an op parameter returns 405" |
| 63 | + (let [resp (handler (request :get {:op "eval" :code "(+ 1 2)"})) |
| 64 | + body (json/parse-string (:body resp) true)] |
| 65 | + (is (= 405 (:status resp))) |
| 66 | + (is (= "Method Not Allowed" (:error body))) |
| 67 | + (is (re-find #"POST request method" (:reason body))))))) |
| 68 | + |
| 69 | +(deftest ring-handler-responses |
| 70 | + (testing "GET returns empty response array" |
| 71 | + (let [handler (make-handler) |
| 72 | + resp (handler (request :get))] |
| 73 | + (is (= 200 (:status resp))) |
| 74 | + (is (= [] (parse-body (:body resp)))))) |
| 75 | + |
| 76 | + (testing "POST eval returns result" |
| 77 | + (let [handler (make-handler :default-read-timeout 5000) |
| 78 | + resp (handler (request :post {:op "eval" :code "(+ 1 2)"})) |
| 79 | + messages (parse-body (:body resp))] |
| 80 | + (is (= 200 (:status resp))) |
| 81 | + (is (some #(= "3" (:value %)) messages))))) |
| 82 | + |
| 83 | +(deftest session-handling |
| 84 | + (testing "session cookie is set and can be reused" |
| 85 | + (let [handler (make-handler :default-read-timeout 5000) |
| 86 | + cookie-name "drawbridge-session" |
| 87 | + resp1 (handler (request :post {:op "eval" :code "(+ 10 20)"})) |
| 88 | + cookie (extract-session-cookie resp1 cookie-name)] |
| 89 | + (is (some? cookie) "Session cookie should be set") |
| 90 | + (let [resp2 (handler (-> (request :get) |
| 91 | + (assoc-in [:headers "cookie"] |
| 92 | + (str cookie-name "=" cookie))))] |
| 93 | + (is (= 200 (:status resp2)))))) |
| 94 | + |
| 95 | + (testing "custom cookie name is used in Set-Cookie header" |
| 96 | + (let [handler (make-handler :cookie-name "my-session") |
| 97 | + resp (handler (request :get)) |
| 98 | + set-cookie (get-set-cookie resp)] |
| 99 | + (is (some? set-cookie)) |
| 100 | + (is (re-find #"my-session=" set-cookie))))) |
| 101 | + |
| 102 | +(deftest custom-nrepl-handler |
| 103 | + (testing "responses come from the supplied handler" |
| 104 | + (let [marker "custom-handler-marker" |
| 105 | + custom-handler (fn [msg] |
| 106 | + (transport/send (:transport msg) |
| 107 | + {:status :done :value marker})) |
| 108 | + handler (make-handler :nrepl-handler custom-handler |
| 109 | + :default-read-timeout 5000) |
| 110 | + resp (handler (request :post {:op "eval" :code "ignored"})) |
| 111 | + messages (parse-body (:body resp))] |
| 112 | + (is (= 200 (:status resp))) |
| 113 | + (is (some #(= marker (:value %)) messages))))) |
0 commit comments