diff --git a/clj-browserchannel/project.clj b/clj-browserchannel/project.clj index 53a658d..ed7d875 100644 --- a/clj-browserchannel/project.clj +++ b/clj-browserchannel/project.clj @@ -9,6 +9,7 @@ [org.clojure/clojurescript "1.8.51"]]} :dev - {:dependencies [[pjstadig/humane-test-output "0.8.0"]] + {:dependencies [[pjstadig/humane-test-output "0.8.0"] + [ring/ring-mock "0.3.0"]] :injections [(require 'pjstadig.humane-test-output) (pjstadig.humane-test-output/activate!)]}}) diff --git a/clj-browserchannel/test/net/thegeez/browserchannel/common.clj b/clj-browserchannel/test/net/thegeez/browserchannel/common.clj index 33d1399..0a97660 100644 --- a/clj-browserchannel/test/net/thegeez/browserchannel/common.clj +++ b/clj-browserchannel/test/net/thegeez/browserchannel/common.clj @@ -3,3 +3,18 @@ (defn ->queue [& x] (apply conj clojure.lang.PersistentQueue/EMPTY x)) + +(defn random-string + [& [n]] + (let [n (or n 12) + chars (map char (concat (range 48 58) (range 66 91) (range 97 123))) + password (take n (repeatedly #(rand-nth chars)))] + (reduce str password))) + +(defn contains-all-of? + [m other-m] + (->> other-m + (map (fn [[k v]] + (= (get m k) v))) + (remove true?) + (empty?))) diff --git a/clj-browserchannel/test/net/thegeez/browserchannel/server/http_request_tests.clj b/clj-browserchannel/test/net/thegeez/browserchannel/server/http_request_tests.clj new file mode 100644 index 0000000..d1cfda4 --- /dev/null +++ b/clj-browserchannel/test/net/thegeez/browserchannel/server/http_request_tests.clj @@ -0,0 +1,88 @@ +(ns net.thegeez.browserchannel.server.http-request-tests + (:use + clojure.test + net.thegeez.browserchannel.common + net.thegeez.browserchannel.server + net.thegeez.browserchannel.test-async-adapter) + (:require + [cheshire.core :as json] + [ring.util.response :as response] + [ring.mock.request :as mock])) + +(use-fixtures :each async-output-fixture) + +(defn app + [request & [options]] + ((-> (fn [{:keys [uri] :as request}] + (if (or (= "" uri) + (= "/" uri)) + (response/response "Hello, world!") + (response/not-found "not found"))) + (wrap-browserchannel (or options default-options)) + (wrap-test-async-adapter (or options default-options))) + request)) + +;; http://web.archive.org/web/20121226064550/http://code.google.com/p/libevent-browserchannel-server/wiki/BrowserChannelProtocol#Get_Host_Prefixes + +(deftest get-host-prefixes-test-incorrect-version + (let [resp (app (mock/request + :get "/channel/test" + {"VER" 7 + "MODE" "init" + "zx" (random-string) + "t" 1}))] + (is (= 400 (:status resp))))) + +(deftest get-host-prefixes-test-incorrect-request + (let [resp (app (mock/request + :get "/channel/test"))] + (is (= 400 (:status resp))))) + +(deftest get-host-prefixes-test-no-prefixes + (let [resp (app (mock/request + :get "/channel/test" + {"VER" 8 + "MODE" "init" + "zx" (random-string) + "t" 1}))] + (is (= 200 (:status resp))) + (is (contains-all-of? (:headers resp) (:headers default-options))) + (is (= (json/parse-string (:body resp)) + [nil nil])))) + +(deftest get-host-prefixes-test-with-prefixes + (let [options (merge default-options + {:host-prefixes ["a", "b", "c"]}) + resp (app (mock/request + :get "/channel/test" + {"VER" 8 + "MODE" "init" + "zx" (random-string) + "t" 1}) + options) + body (json/parse-string (:body resp))] + (is (= 200 (:status resp))) + (is (contains-all-of? (:headers resp) (:headers default-options))) + (is (not (nil? (some #{(first body)} (:host-prefixes options))))) + (is (nil? (second body))))) + +(deftest buffering-proxy-test + (let [resp (app (mock/request + :get "/channel/test" + {"VER" 8 + "zx" (random-string) + "t" 1})) + async-resp @async-output] + (is (= 200 (:status resp))) + (is (= 200 (:status async-resp))) + (is (contains-all-of? (:headers async-resp) (:headers default-options))) + (is (not (:closed? async-resp))) + (is (= "11111" (:body async-resp))) + ; browserchannel's buffering proxy test is supposed to work by initially sending + ; "11111", then waits for 2 seconds (without closing the response), then sends + ; "2" and finally closes the response. + ; wait for 3 secs just to be safe + (Thread/sleep 3000) + (let [async-resp @async-output] + (is (:closed? async-resp)) + (is (= "111112" (:body async-resp)))))) diff --git a/clj-browserchannel/test/net/thegeez/browserchannel/test_async_adapter.clj b/clj-browserchannel/test/net/thegeez/browserchannel/test_async_adapter.clj new file mode 100644 index 0000000..cea6ead --- /dev/null +++ b/clj-browserchannel/test/net/thegeez/browserchannel/test_async_adapter.clj @@ -0,0 +1,47 @@ +(ns net.thegeez.browserchannel.test-async-adapter + (:require + [net.thegeez.browserchannel.async-adapter :as bc-async-adapter])) + +(def async-output (atom {})) + +(defn write-async-output! + [data] + (if (map? data) + (swap! async-output merge data) + (swap! async-output (fn [{:keys [body] :as output}] + (assoc output :body (str body data)))))) + +(defn closed-async-output? + [] + (:closed? @async-output)) + +(defn async-output-fixture [f] + (reset! async-output {}) + (f)) + +(deftype TestResponse + [write-fn closed?-fn] + bc-async-adapter/IAsyncAdapter + + (head [this status headers] + (let [headers (assoc headers "Transfer-Encoding" "chunked")] + (write-fn {:status status :headers headers}))) + + (write-chunk [this data] + (if-not (closed?-fn) + (write-fn data) + (throw bc-async-adapter/ConnectionClosedException))) + + (close [this] + (write-fn {:closed? true}))) + +(defn wrap-test-async-adapter + [handler & [options]] + (fn [request] + (let [resp (handler request)] + (if (= :http (:async resp)) + (let [reactor (:reactor resp) + emit (TestResponse. write-async-output! closed-async-output?)] + (reactor emit) + {:status 200}) + resp)))) \ No newline at end of file