From f401eb95f86b1b57e4df5005c5364caf45eead18 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 15 May 2016 14:35:02 -0400 Subject: [PATCH] add comments to the bind channel requests as they're somewhat complex --- .../bind_channel_http_request_tests.clj | 141 ++++++++++++++++-- 1 file changed, 129 insertions(+), 12 deletions(-) diff --git a/clj-browserchannel/test/net/thegeez/browserchannel/server/bind_channel_http_request_tests.clj b/clj-browserchannel/test/net/thegeez/browserchannel/server/bind_channel_http_request_tests.clj index 71912c4..548f7e3 100644 --- a/clj-browserchannel/test/net/thegeez/browserchannel/server/bind_channel_http_request_tests.clj +++ b/clj-browserchannel/test/net/thegeez/browserchannel/server/bind_channel_http_request_tests.clj @@ -112,8 +112,10 @@ (deftest create-session-and-open-backchannel-test (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (-> (mock/request :get "/channel/bind") (mock/query-string @@ -126,15 +128,17 @@ "TYPE" "xmlhttp" "zx" (random-string) "t" 1}))) + ; 3. get backchannel response so far (basically nothing other then headers at this point) async-resp @async-output] (wait-for-agent-send-offs) (let [status (get-status session-id)] (is (= 200 (:status back-resp))) (is (= 200 (:status async-resp))) + ; 4. verify session is still connected, backchannel request still alive (is (not (:closed? async-resp))) (is (contains-all-of? (:headers async-resp) (dissoc (:headers options) "Content-Type"))) - (is (:connected? status)) + (is (connected? session-id)) (is (:has-back-channel? status))))) (defn ->new-backchannel-request @@ -154,12 +158,16 @@ (deftest send-data-to-client-test (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. send data to client along backchannel (send-data session-id {:foo "bar"}) (wait-for-agent-send-offs) + ; 4. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) @@ -167,18 +175,23 @@ (is (not (:closed? async-resp))) (is (contains-all-of? (:headers async-resp) (dissoc (:headers options) "Content-Type"))) + ; 5. verify data was sent (is (= (-> arrays ffirst second (get "__edn") (edn/read-string)) {:foo "bar"}))))) (deftest send-multiple-data-to-client-test (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. send data to client along backchannel (send-data session-id {:foo "bar"}) (send-data session-id "hello, world") (wait-for-agent-send-offs) + ; 4. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) @@ -186,6 +199,7 @@ (is (not (:closed? async-resp))) (is (contains-all-of? (:headers async-resp) (dissoc (:headers options) "Content-Type"))) + ; 5. verify data was sent (is (= (get-edn-from-arrays arrays 0) {:foo "bar"})) (is (= (get-edn-from-arrays arrays 1) @@ -193,13 +207,18 @@ (deftest send-data-to-client-before-backchannel-created (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp)] (wait-for-agent-send-offs) + ; 2. send data to client. no backchannel yet, so it should be queued in a buffer (send-data session-id {:foo "bar"}) (wait-for-agent-send-offs) + ; 3. backchannel request (long request, async response). + ; queued buffer of messages is flushed to backchannel when it first opens (let [back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 4. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) @@ -207,48 +226,61 @@ (is (not (:closed? async-resp))) (is (contains-all-of? (:headers async-resp) (dissoc (:headers options) "Content-Type"))) + ; 5. verify data was sent (is (= (get-edn-from-arrays arrays 0) {:foo "bar"})))))) (deftest send-data-to-client-after-backchannel-reconnect (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (= 200 (:status back-resp))) - ; close backchannel + ; 3. close backchannel. we do it via clear-back-channel directly as it better + ; simulates either a network disconnect or connection timeout (which can and will happen) (send-off (get @sessions session-id) clear-back-channel) (wait-for-agent-send-offs) - ; nothing should have been written + ; 4. get async response so far from the backchannel. nothing should have been written at this point. + ; also, verify backchannel request has indeed been closed (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status async-resp))) (is (:closed? async-resp)) (is (empty? arrays))) + ; reset async response output. hacky necessity due to the way we capture this output (reset! async-output {}) - ; open second backchannel + ; 5. second backchannel request (let [back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 6. send data to client along backchannel (send-data session-id {:foo "bar"}) (wait-for-agent-send-offs) + ; 7. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) (is (= 200 (:status async-resp))) (is (not (:closed? async-resp))) + ; 8. verify data was sent (is (= (get-edn-from-arrays arrays 0) {:foo "bar"})))))) (deftest heartbeat-interval-when-active-backchannel-test (let [options (-> default-options (assoc :keep-alive-interval 2)) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (:heartbeat-timeout @(get @sessions session-id))) + ; 3. wait for heartbeat/keepalive interval to elapse (wait-for-scheduled-interval (:keep-alive-interval options)) + ; 4. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) @@ -256,17 +288,21 @@ (is (not (:closed? async-resp))) (is (contains-all-of? (:headers async-resp) (dissoc (:headers options) "Content-Type"))) + ; 5. verify data was sent ("noop" is the standard keepalive message that is sent) (is (= (get-raw-from-arrays arrays 0) ["noop"]))))) (deftest no-heartbeat-interval-without-active-backchannel-test (let [options (-> default-options (assoc :keep-alive-interval 2)) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp)] (wait-for-agent-send-offs) (is (connected? session-id)) (is (not (:heartbeat-timeout @(get @sessions session-id)))) + ; 2. wait for heartbeat/keepalive interval to elapse. since there should be no + ; scheduled job for it, nothing should have changed after this wait (wait-for-scheduled-interval (:keep-alive-interval options)) (is (connected? session-id)) (is (not (:heartbeat-timeout @(get @sessions session-id)))))) @@ -274,70 +310,91 @@ (deftest heartbeat-interval-is-reactivated-when-backchannel-reconnects-test (let [options (-> default-options (assoc :keep-alive-interval 2)) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (:heartbeat-timeout @(get @sessions session-id))) - ; close backchannel + ; 3. close backchannel. we do it via clear-back-channel directly as it better + ; simulates either a network disconnect or connection timeout (which can and will happen) (send-off (get @sessions session-id) clear-back-channel) (wait-for-agent-send-offs) - ; nothing should have been written + ; 4. get async response so far from the backchannel. nothing should have been written at this point. + ; also, verify backchannel request has indeed been closed (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status async-resp))) (is (:closed? async-resp)) (is (empty? arrays))) + ; reset async response output. hacky necessity due to the way we capture this output (reset! async-output {}) - ; open second backchannel + ; 5. second backchannel request (let [back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (:heartbeat-timeout @(get @sessions session-id))) + ; 6. wait for heartbeat/keepalive interval to elapse (wait-for-scheduled-interval (:keep-alive-interval options)) + ; 7. get async response so far from the backchannel (let [async-resp @async-output arrays (get-response-arrays (:body async-resp))] (is (= 200 (:status back-resp))) (is (= 200 (:status async-resp))) (is (not (:closed? async-resp))) + ; 8. verify data was sent ("noop" is the standard keepalive message that is sent) (is (= (get-raw-from-arrays arrays 0) ["noop"])))))) (deftest session-timeout-without-active-backchannel-test (let [options (-> default-options (assoc :session-timeout-interval 3)) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp)] (wait-for-agent-send-offs) (is (connected? session-id)) (is (:session-timeout @(get @sessions session-id))) + ; 2. wait for session timeout interval to elapse (wait-for-scheduled-interval (:session-timeout-interval options)) + ; 3. verify the session is no longer connected (timed out and closed) (is (not (connected? session-id))))) (deftest no-session-timeout-when-backchannel-active-test (let [options (-> default-options (assoc :session-timeout-interval 3)) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (connected? session-id)) (is (not (:session-timeout @(get @sessions session-id)))) + ; 2. wait for session timeout interval to elapse. since there should be no + ; scheduled job for it, nothing should have happened by the time this wait finishes (wait-for-scheduled-interval (:session-timeout-interval options)) + ; 3. should still have an active session (is (connected? session-id)) (is (not (:session-timeout @(get @sessions session-id)))))) (deftest session-timeout-is-reactivated-after-backchannel-close-test (let [options default-options + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) (is (connected? session-id)) + ; 3. no session timeout interval active because backchannel is active (is (not (:session-timeout @(get @sessions session-id)))) - ; close backchannel + ; 4. close backchannel. we do it via clear-back-channel directly as it better + ; simulates either a network disconnect or connection timeout (which can and will happen) (send-off (get @sessions session-id) clear-back-channel) (wait-for-agent-send-offs) (is (connected? session-id)) + ; 5. session timeout interval now active because backchannel was disconnected (is (:session-timeout @(get @sessions session-id))))) (deftest open-and-close-event-handlers-test @@ -352,18 +409,27 @@ options (-> default-options (assoc :events {:on-open on-open :on-close on-close})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. verify that on-open event was fired and was passed correct arguments + ; (see on-open function defined above) (is (= (get-in @event-output [:on-open :session-id]) session-id)) (is (map? (get-in @event-output [:on-open :request]))) + ; 4. on-close not fired yet (is (nil? (:on-close @event-output))) - ; close session + ; 5. disconnect the session. this does the same basic thing as disconnect!, but we + ; call send-off and close directly like this so we can test passing a (fake) request + ; map just to verify that it makes it to the on-close function (send-off (get @sessions session-id) close {:fake-request-map true} "close") (wait-for-agent-send-offs) (is (not (connected? session-id))) + ; 6. verify that on-close event was fired and passed correct arguments + ; (see on-close function defined above) (is (= (:on-close @event-output) {:session-id session-id :request {:fake-request-map true} @@ -377,10 +443,13 @@ :data data})) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. forwardchannel request so simulate sending data from client to server (let [forward-resp (app (-> (mock/request :post "/channel/bind") (mock/query-string @@ -395,17 +464,22 @@ {"count" "1" "ofs" "0" "req0___edn" (pr-str {:foo "bar"})}))) + ; 4. parse response body [len status] (string/split (:body forward-resp) #"\n" 2) [backchannel-present last-bkch-array-id outstanding-bytes] (json/parse-string status)] (wait-for-agent-send-offs) + ; 5. verify that the response was correct (is (= 200 (:status forward-resp))) (is (> (Long/parseLong len) 0)) (is (= 1 backchannel-present)) (is (= 0 last-bkch-array-id)) (is (= 0 outstanding-bytes)) + ; 6. verify that the on-receive event was fired and passed correct arguments + ; (see on-close function defined above) (is (= 1 (count @received))) (is (= session-id (:session-id (first @received)))) (is (map? (:request (first @received)))) + ; the received data (is (= (:data (first @received)) {:foo "bar"})) (is (connected? session-id)) @@ -419,10 +493,14 @@ :data data})) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. forwardchannel request so simulate sending data from client to server + ; (2 maps included) (let [forward-resp (app (-> (mock/request :post "/channel/bind") (mock/query-string @@ -438,19 +516,24 @@ "ofs" "0" "req0___edn" (pr-str {:foo "bar"}) "req1___edn" (pr-str {:second "map"})}))) + ; 4. parse response body [len status] (string/split (:body forward-resp) #"\n" 2) [backchannel-present last-bkch-array-id outstanding-bytes] (json/parse-string status)] (wait-for-agent-send-offs) + ; 5. verify that the response was correct (is (= 200 (:status forward-resp))) (is (> (Long/parseLong len) 0)) (is (= 1 backchannel-present)) (is (= 0 last-bkch-array-id)) (is (= 0 outstanding-bytes)) + ; 6. verify that the on-receive event was fired and passed correct arguments + ; (see on-close function defined above) (is (= 2 (count @received))) (is (= session-id (:session-id (first @received)))) (is (= session-id (:session-id (second @received)))) (is (map? (:request (first @received)))) (is (map? (:request (second @received)))) + ; the received data (is (= (:data (first @received)) {:foo "bar"})) (is (= (:data (second @received)) @@ -496,18 +579,24 @@ (swap! received conj data)) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) + ; 2. backchannel request (long request, async response) back-resp (app (->new-backchannel-request session-id))] (wait-for-agent-send-offs) + ; 3. forwardchannel request to send data from client to server (let [forward-resp (app (->new-forwardchannel-request session-id 0 "hello 1")) result1 (parse-forward-response forward-resp)] (wait-for-agent-send-offs) + ; 4. a second forwardchannel request to send more data from client to server (let [forward2-resp (app (->new-forwardchannel-request session-id 0 "hello 2")) result2 (parse-forward-response forward2-resp)] (wait-for-agent-send-offs) (is (= 0 (:last-bkch-array-id result1))) (is (= 0 (:last-bkch-array-id result2))) + ; 5. verify that the on-receive event was fired for both pieces of data sent + ; (see on-close function defined above) (is (= 2 (count @received))) (is (= (first @received) "hello 1")) @@ -522,15 +611,19 @@ (swap! received conj data)) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp)] (wait-for-agent-send-offs) + ; 2. forwardchannel request to send data from client to server (let [forward-resp (app (->new-forwardchannel-request session-id 0 :foobar)) result (parse-forward-response forward-resp)] (wait-for-agent-send-offs) (is (= 0 (:backchannel-present result))) (is (= 0 (:last-bkch-array-id result))) (is (= 0 (:outstanding-bytes result))) + ; 5. verify that the on-receive event was fired + ; (see on-close function defined above) (is (= 1 (count @received))) (is (= (first @received) :foobar)) @@ -544,6 +637,9 @@ :data data})) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session. encode a map to be sent as part + ; of this request (encoded in same format as any other forwardchannel request + ; would do to send client->server data) create-resp (app (-> (mock/request :post "/channel/bind") (mock/query-string @@ -560,6 +656,8 @@ session-id (get-session-id create-resp)] (wait-for-agent-send-offs) (is (connected? session-id)) + ; 2. verify that the on-receive event was fired + ; (see on-close function defined above) (is (= 1 (count @received))) (is (= session-id (:session-id (first @received)))) (is (map? (:request (first @received)))) @@ -572,19 +670,26 @@ (swap! received conj data)) options (-> default-options (assoc :events {:on-receive on-receive})) + ; 1. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp) str-to-send "this will get queued but not sent because no backchannel is open" queued-str (pr-str (encode-map str-to-send))] (wait-for-agent-send-offs) + ; 2. send data to client. no backchannel is active, so data will be queued in the buffer (send-data session-id str-to-send) (wait-for-agent-send-offs) + ; 3. forwardchannel request to send data from client to server (let [forward-resp (app (->new-forwardchannel-request session-id 0 42)) result (parse-forward-response forward-resp)] (wait-for-agent-send-offs) + ; 4. verify that the response to the forwardchannel request contained correct backchannel + ; data buffer info (e.g. reflecting the fact that we have queued data to be sent) (is (= 0 (:backchannel-present result))) (is (= 0 (:last-bkch-array-id result))) (is (= (count queued-str) (:outstanding-bytes result))) + ; 5. verify that the on-receive event was fired + ; (see on-close function defined above) (is (= 1 (count @received))) (is (= (first @received) 42)) @@ -592,6 +697,8 @@ (deftest flush-buffer-write-failure-resends-buffer (let [failed-once? (atom false) + ; 1. hacky way to trigger a backchannel write failure + ; this is made possible by test-only options via wrap-test-async-adapter fail-fn (fn [data] (if (and (not @failed-once?) (string? data) @@ -599,20 +706,26 @@ (reset! failed-once? true))) options (-> default-options (assoc :fail-fn fail-fn)) + ; 2. forwardchannel request to create session create-resp (app (->new-session-request) options) session-id (get-session-id create-resp)] (wait-for-agent-send-offs) + ; 3. queue up a bunch of data to be sent. no backchannel has been created yet, + ; so this will all sit in the buffer and get flushed once the backchannel + ; is opened (send-data session-id :first-queued) (send-data session-id :second-queued) (send-data session-id "fail") (send-data session-id :fourth-queued) - ; send-data flushes the buffer after each call, so we just delay - ; opening a backchannel so that we can fill up the buffer with - ; more then one message at a time easier + ; 4. backchannel request (long request, async response) (let [back-resp (app (->new-backchannel-request session-id) options)] (wait-for-agent-send-offs) + ; 5. an exception should have been thrown during the flush-buffer call, but + ; we should have sent out some of the data before then (is (= 200 (:status back-resp))) (let [arrays-up-till-fail (get-response-arrays (:body @async-output))] + ; 6. verify that the backchannel was closed (because of the exception), + ; and that the first 2 pieces of data were sent (is (= 200 (:status @async-output))) (is (:closed? @async-output)) (is (connected? session-id)) @@ -620,8 +733,12 @@ (is (= :second-queued (get-edn-from-arrays arrays-up-till-fail 1))) ; hacky requirement for this unit test due to the way we capture async response output (reset! async-output {}) + ; 7. re-open backchannel. all 4 items should still be in the buffer and will + ; get flushed again at this point (let [back-resp (app (->new-backchannel-request session-id) options)] (wait-for-agent-send-offs) + ; 8. at this point, all 4 items should have been sent and the backchannel request + ; should still be active (is (= 200 (:status back-resp))) (let [arrays (get-response-arrays (:body @async-output))] (is (= 200 (:status @async-output)))