Integration test for client server sync

This commit is contained in:
Nicholas Kariniemi 2014-09-28 09:35:20 +03:00
parent e3e763469a
commit d174b2236e
2 changed files with 107 additions and 42 deletions

View file

@ -7,12 +7,12 @@
#+cljs (:require-macros [cljs.core.async.macros :refer [go]]))
;; Server state
(def states (atom []))
;; (def states (atom []))
(defn make-server-agent
([in out states] (make-server-agent in out states sync/empty-state))
([in out states initial-client-state]
(go (loop [client-state initial-client-state]
([in out states initial-client-shadow]
(go (loop [client-shadow initial-client-shadow]
(when-let [msg (<! in)]
(condp = (:type msg)
:diff
@ -35,51 +35,54 @@
(recur state))
:new-state
(let [{:keys [diff hash]} (sync/diff-states (:new-states msg) client-state)]
(let [{:keys [diff hash]} (sync/diff-states (:new-states msg) client-shadow)]
(>! out (message/diff-msg diff hash))
(recur client-state))
(recur client-state)))))))
(recur client-shadow))
(recur client-shadow)))))))
;; (defn make-client-agent
;; ([in out states] (make-client-agent in out states sync/empty-state))
;; ([in out states initial-server-state]
;; (a/go-loop [server-state initial-server-state]
;; (when-let [msg (<! in)]
;; (condp = (:type msg)
;; :diff
;; (let [states* @states
;; shadow (sync/get-history-state states* (:hash msg))]
;; (if shadow
;; (let [new-states (sync/apply-diff states* (:diff msg))
;; new-shadow (diff/patch-state shadow (:diff msg))
;; {:keys [diff hash]} (sync/diff-states new-states new-shadow)]
;; (reset! states new-states)
;; (recur new-shadow))
;; (let [state (sync/get-current-state @states)]
;; (>! out (message/full-sync state))
;; (recur state))))
(defn make-client-agent
([in out states] (make-client-agent in out states sync/empty-state))
([in out states initial-server-shadow]
(a/go-loop [server-shadow initial-server-shadow]
(when-let [msg (<! in)]
(condp = (:type msg)
:diff
(let [states* @states
shadow (sync/get-history-state states* (:hash msg))]
(if shadow
(let [new-states (sync/apply-diff states* (:diff msg))
new-shadow (diff/patch-state shadow (:diff msg))
{:keys [diff hash]} (sync/diff-states new-states new-shadow)]
(reset! states new-states)
(recur new-shadow))
(let [state (sync/get-current-state @states)]
(>! out (message/full-sync state))
(recur state))))
;; :full-sync
;; (let [state (:state msg)]
;; (reset! states [state])
;; (recur state))
:full-sync
(let [state (:state msg)]
(reset! states [state])
(recur state))
;; :new-state
;; (let [{:keys [diff hash]} (sync/diff-states (:new-states msg) server-state)]
;; (>! out (message/diff-msg diff hash)))
;; (recur server-state))))))
:new-state
(let [{:keys [diff hash]} (sync/diff-states (:new-states msg) server-shadow)]
(>! out (message/diff-msg diff hash))
(recur server-shadow))
(recur server-shadow))))))
;; TODO: Remove watch, close up channels properly
(defn sync-new-client! [>client <client]
(let [client-id (java.util.UUID/randomUUID)
state-changes (chan)
state-change-events (a/map< (fn [s] {:type :new-state :new-states s}) state-changes)
client-events (chan)]
(add-watch states client-id (fn [_ _ _ new-states] (a/put! state-changes new-states)))
(a/pipe (a/merge [<client state-change-events]) client-events)
(make-server-agent client-events >client states)))
nil)
;; (let [client-id (java.util.UUID/randomUUID)
;; state-changes (chan)
;; state-change-events (a/map< (fn [s] {:type :new-state :new-states s}) state-changes)
;; client-events (chan)]
;; (add-watch states client-id (fn [_ _ _ new-states] (a/put! state-changes new-states)))
;; (a/pipe (a/merge [<client state-change-events]) client-events)
;; (make-server-agent client-events >client states)))
(defn init [to-db grubs recipes]
(reset! states (sync/initial-state grubs recipes))
(add-watch states :to-db (fn [_ _ old-states new-states]
(a/put! to-db (sync/get-current-state new-states)))))
nil)
;; (reset! states (sync/initial-state grubs recipes))
;; (add-watch states :to-db (fn [_ _ old-states new-states]
;; (a/put! to-db (sync/get-current-state new-states)))))

View file

@ -142,3 +142,65 @@
"3" {:text "milk" :completed false}}}
:recipes {:deleted #{}, :updated nil}}
:hash (hasch/uuid client-state)})))
(fact "Client-only changes synced with server"
(let [client-shadow {:grubs {"1" {:text "2 apples" :completed true}} :recipes {}}
client-states (states-atom
{:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
{:grubs {"1" {:text "2 apples" :completed true}} :recipes {}})
server-shadow {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
server-states (states-atom server-shadow)
client-in (chan)
client-out (chan)
server-in (chan)
server-out (chan)
client-state-changes (chan 1)
msg {:type :new-state
:new-states (hashed-states
{:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
{:grubs {"1" {:text "2 apples" :completed true}} :recipes {}})}]
(a/pipe client-out server-in)
(a/pipe server-out client-in)
(state/make-client-agent client-in client-out client-states server-shadow)
(state/make-server-agent server-in server-out server-states client-shadow)
(add-watch client-states :test (fn [_ _ _ new-states] (a/put! client-state-changes new-states)))
(>!! client-in msg)
(<!! client-state-changes)
(:state (last @client-states)) => {:grubs {"1" {:completed true, :text "2 apples"}}
:recipes {}}
(:state (last @server-states)) => {:grubs {"1" {:completed true, :text "2 apples"}}
:recipes {}}))
(fact "Client and server changes synced"
(let [client-shadow {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
client-states (states-atom
{:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
{:grubs {"1" {:text "2 apples" :completed true}} :recipes {}})
server-shadow {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
server-states (states-atom
server-shadow
{:grubs {"1" {:text "4 apples" :completed false}} :recipes {}})
client-in (chan)
client-out (chan)
server-in (chan)
server-out (chan)
msg {:type :new-state
:new-states (hashed-states
{:grubs {"1" {:text "2 apples" :completed false}} :recipes {}}
{:grubs {"1" {:text "2 apples" :completed true}} :recipes {}})}
client-state-changes (chan 1)]
(a/pipe client-out server-in)
(a/pipe server-out client-in)
(state/make-client-agent client-in client-out client-states server-shadow)
(state/make-server-agent server-in server-out server-states client-shadow)
(add-watch client-states :test (fn [_ _ _ new-states] (a/put! client-state-changes new-states)))
(>!! client-in msg)
(<!! client-state-changes)
@client-states => (hashed-states
{:grubs {"1" {:completed false, :text "2 apples"}}, :recipes {}}
{:grubs {"1" {:completed true, :text "2 apples"}}, :recipes {}}
{:grubs {"1" {:completed true, :text "4 apples"}}, :recipes {}})
@server-states => (hashed-states
{:grubs {"1" {:completed false, :text "2 apples"}}, :recipes {}}
{:grubs {"1" {:completed false, :text "4 apples"}}, :recipes {}}
{:grubs {"1" {:completed true, :text "4 apples"}}, :recipes {}})))