Integration test for client server sync
This commit is contained in:
parent
e3e763469a
commit
d174b2236e
2 changed files with 107 additions and 42 deletions
|
@ -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)))))
|
||||
|
|
|
@ -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 {}})))
|
||||
|
|
Loading…
Reference in a new issue