From fe5bd004272e5bdf6a0a909f92d7fd47e00d60e9 Mon Sep 17 00:00:00 2001 From: Nicholas Kariniemi Date: Tue, 16 Sep 2014 23:27:10 +0300 Subject: [PATCH] Refactor - wip --- src/clj/grub/state.clj | 28 ++++++++++++++++ src/test/grub/test/unit/state.clj | 56 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/test/grub/test/unit/state.clj diff --git a/src/clj/grub/state.clj b/src/clj/grub/state.clj index 3b19142..df433dc 100644 --- a/src/clj/grub/state.clj +++ b/src/clj/grub/state.clj @@ -5,6 +5,33 @@ [clojure.core.async :as a :refer [! chan go]] [hasch.core :as hasch])) +(def initial-state {:hash (hasch/uuid cs/empty-state) + :state cs/empty-state}) +(def states (atom [initial-state])) + +(defn get-history-state [states hash] + (:state (first (filter #(= (:hash %) hash) states)))) + +(defn add-history-state [current new-state] + (let [{:keys [state hash]} (first current) + new-hash (hasch/uuid new-state)] + (if (= hash new-hash) + current + (conj current {:hash hash :state state})))) + +(defn receive-diff [states diff shadow-hash] + (let [state (:state (first states)) + shadow (get-history-state states shadow-hash)] + (if shadow + {:new-state (diff/patch-state state diff) + :new-shadow (diff/patch-state shadow diff) + :full-sync? false} + {:new-state state + :new-shadow state + :full-sync? true}))) + + + (def state (atom cs/empty-state)) (def to-db (atom nil)) (def to-all (chan)) @@ -26,6 +53,7 @@ {:grubs (util/map-by-key :id grubs) :recipes (util/map-by-key :id recipes)}) + (defn sync-new-client! [to from] (let [client-id (java.util.UUID/randomUUID) client-state (atom cs/empty-state) diff --git a/src/test/grub/test/unit/state.clj b/src/test/grub/test/unit/state.clj new file mode 100644 index 0000000..c30a04d --- /dev/null +++ b/src/test/grub/test/unit/state.clj @@ -0,0 +1,56 @@ +(ns grub.test.unit.state + (:require [grub.state :as s] + [grub.common-state :as cs] + [clojure.test :refer :all] + [hasch.core :as hasch])) + +(deftest apply-diff-normally + ;; Apply changes and return ACK for in sync client/server + (let [state {:grubs {"1" {:text "2 apples" :completed false}} + :recipes {}} + hash (hasch/uuid state) + states [{:hash hash :state state}] + diff {:grubs {:updated {"1" {:completed true}} :deleted #{}}} + shadow-hash hash + {:keys [new-state new-shadow full-sync?]} (s/receive-diff states diff shadow-hash)] + (do + (is (= {:grubs {"1" {:text "2 apples" :completed true}} + :recipes {}} + new-state)) + (is (= {:grubs {"1" {:text "2 apples" :completed true}} + :recipes {}} + new-shadow)) + (is (not full-sync?))))) + +(deftest server-state-changed + ;; Send differences back if server state changed + (let [state {:grubs {"1" {:text "3 apples" :completed false}} :recipes {}} + prev {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} + states [{:hash (hasch/uuid state) :state state} + {:hash (hasch/uuid prev) :state prev}] + diff {:grubs {:updated {"1" {:completed true}} :deleted #{}}} + shadow-hash (hasch/uuid prev) + {:keys [new-state new-shadow full-sync?]} (s/receive-diff states diff shadow-hash)] + (do + (is (= {:grubs {"1" {:text "3 apples" :completed true}} + :recipes {}} + new-state)) + (is (= {:grubs {"1" {:text "2 apples" :completed true}} + :recipes {}} + new-shadow)) + (is (not full-sync?))))) + +(deftest full-sync-if-client-too-far-out-of-sync + ;; Shadow hash not in history means client has fallen too far + ;; out of sync. Send a full sync + (let [state {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} + prev {:grubs {"1" {:text "2 apples" :completed false}} :recipes {}} + states [{:hash (hasch/uuid state) :state state} + {:hash (hasch/uuid prev) :state prev}] + shadow-hash (hasch/uuid {:grubs {} :recipes {}}) + diff {:grubs {:updated {"1" {:completed true}} :deleted #{}}} + {:keys [new-state new-shadow full-sync?]} (s/receive-diff states diff shadow-hash)] + (do + (is (= state new-state)) + (is (= state new-shadow)) + (is full-sync?))))