Explorar el Código

fix(db-sync): worker server unit-tests

rcmerci hace 1 semana
padre
commit
9356782168

+ 3 - 2
bb.edn

@@ -174,8 +174,9 @@
    :task (clojure {:dir "clj-e2e"} "-X:dev-run-rtc-extra-part2-test")}
 
   dev:db-sync-test
-  {:doc "Run db-sync unit tests"
-   :task (shell {:dir "deps/db-sync"} "clojure -M:cljs compile db-sync-test && node worker/dist/worker-test.js")}
+  {:doc "Run db-sync server unit tests"
+   :task (do (shell {:dir "deps/db-sync"} "clojure -M:cljs compile db-sync-test")
+             (shell {:dir "deps/db-sync"} "node worker/dist/worker-test.js"))}
 
   dev:db-sync-start
   {:doc "Start db-sync local server + client watch processes in foreground"

+ 37 - 11
deps/db-sync/src/logseq/db_sync/cycle.cljs

@@ -45,18 +45,29 @@
     (keyword? entity) [:db/ident entity]
     :else entity))
 
-(defn- next-parent-eid [db attr eid]
-  (when-let [entity (d/entity db eid)]
-    (let [value (get entity attr)]
-      (:db/id value))))
+(defn- next-parent-eid [db attr eid updates-by-eid]
+  (if (contains? updates-by-eid eid)
+    (get updates-by-eid eid)
+    (when-let [entity (d/entity db eid)]
+      (let [value (get entity attr)]
+        (cond
+          (instance? Entity value) (:db/id value)
+          :else (ref->eid db (normalize-entity-ref value)))))))
 
-(defn- cycle-from-eid? [db attr eid]
-  (loop [seen #{eid}
-         current (next-parent-eid db attr eid)]
+(defn- cycle-from-eid? [db attr start-eid target-eid updates-by-eid]
+  (loop [seen #{target-eid}
+         current start-eid]
     (cond
       (nil? current) false
       (contains? seen current) true
-      :else (recur (conj seen current) (next-parent-eid db attr current)))))
+      :else (recur (conj seen current)
+                   (next-parent-eid db attr current updates-by-eid)))))
+
+(defn- normalize-entity-ref-for-result [db entity-ref]
+  (if (number? entity-ref)
+    (when-let [ent (d/entity db entity-ref)]
+      [:block/uuid (:block/uuid ent)])
+    entity-ref))
 
 (defn detect-cycle
   "Returns a map with cycle details when applying tx-data would introduce a cycle.
@@ -65,16 +76,31 @@
   (reduce
    (fn [_ attr]
      (let [updates (attr-updates-from-tx tx-data attr)
+           updates-by-eid
+           (reduce
+            (fn [acc {:keys [entity value]}]
+              (let [entity-ref (normalize-entity-ref entity)
+                    eid (ref->eid db entity-ref)
+                    value-ref (normalize-entity-ref value)
+                    value-eid (ref->eid db value-ref)]
+                (if eid
+                  (assoc acc eid value-eid)
+                  acc)))
+            {}
+            updates)
            result
            (reduce
             (fn [_ {:keys [entity value]}]
               (if (nil? value)
                 nil
                 (let [entity-ref (normalize-entity-ref entity)
-                      eid (ref->eid db entity-ref)]
-                  (when (and eid (cycle-from-eid? db attr eid))
+                      eid (ref->eid db entity-ref)
+                      value-ref (normalize-entity-ref value)
+                      value-eid (ref->eid db value-ref)]
+                  (when (and eid value-eid
+                             (cycle-from-eid? db attr value-eid eid updates-by-eid))
                     {:attr attr
-                     :entity entity-ref}))))
+                     :entity (normalize-entity-ref-for-result db entity-ref)}))))
             nil
             updates)]
        (when result

+ 68 - 30
deps/db-sync/src/logseq/db_sync/worker_core.cljs

@@ -2,39 +2,77 @@
   (:require [datascript.core :as d]
             [logseq.db.common.order :as db-order]))
 
-(defn- attr-updates-from-tx [tx-data attr]
-  (filter (fn [d] (= attr (:a d))) tx-data))
+(defn- normalize-eid [db entity]
+  (cond
+    (number? entity) (when (pos? entity) entity)
+    (vector? entity) (d/entid db entity)
+    (uuid? entity) (d/entid db [:block/uuid entity])
+    (keyword? entity) (d/entid db [:db/ident entity])
+    :else nil))
+
+(defn- attr-updates-from-tx [db tx-data attr]
+  (keep (fn [tx]
+          (cond
+            (and (vector? tx)
+                 (= :db/add (first tx))
+                 (= attr (nth tx 2 nil)))
+            (let [eid (normalize-eid db (nth tx 1 nil))]
+              (when eid
+                {:e eid
+                 :a attr
+                 :v (nth tx 3 nil)}))
+
+            (and (map? tx) (contains? tx attr))
+            (let [entity (or (:db/id tx) (:block/uuid tx) (:db/ident tx))
+                  eid (normalize-eid db entity)]
+              (when eid
+                {:e eid
+                 :a attr
+                 :v (get tx attr)}))
+
+            :else nil))
+        tx-data))
 
 (defn fix-duplicate-orders [db tx-data]
-  (let [updates (attr-updates-from-tx tx-data :block/order)
-        fixes (reduce
-               (fn [acc d]
-                 (let [eid (:e d)
-                       value (:v d)
-                       parent (when eid (:block/parent (d/entity db eid)))
-                       parent-eid (:db/id parent)]
-                   (if (and eid parent-eid value)
-                     (let [siblings (d/datoms db :avet :block/parent parent-eid)
-                           same-order-siblings (-> (filter
-                                                    (fn [datom]
-                                                      (let [sib-eid (:e datom)]
-                                                        (when (and (not= sib-eid eid)
-                                                                   (= value (:block/order (d/entity db sib-eid))))
-                                                          sib-eid)))
-                                                    siblings)
-                                                   sort
-                                                   vec)
-                           orders (map (fn [d] (:block/order (d/entity db (:e d)))) siblings)
-                           start (some (fn [order] (when (< order value) order)) orders)
-                           end (some (fn [order] (when (> order value) order)) orders)]
-                       (if same-order-siblings
-                         (let [orders (db-order/gen-n-keys (inc (count same-order-siblings)) start end)]
-                           (into acc
-                                 (map-indexed (fn [idx id] [:db/add id :block/order (nth orders idx)]) (conj same-order-siblings eid))))
-                         acc))
-                     acc)))
+  (let [updates (->> (attr-updates-from-tx db tx-data :block/order)
+                     (filter (fn [{:keys [e v]}] (and e v))))
+        groups (group-by (fn [{:keys [e v]}]
+                           (let [parent (:block/parent (d/entity db e))]
+                             [(:db/id parent) v]))
+                         updates)
+        fixes (reduce-kv
+               (fn [acc [parent-eid value] group]
+                 (if (and parent-eid value)
+                   (let [update-eids (->> group (map :e) sort vec)
+                         siblings (d/datoms db :avet :block/parent parent-eid)
+                         update-eids-set (set update-eids)
+                         existing-same (->> siblings
+                                            (keep (fn [datom]
+                                                    (let [sib-eid (:e datom)]
+                                                      (when (and (not (contains? update-eids-set sib-eid))
+                                                                 (= value (:block/order (d/entity db sib-eid))))
+                                                        sib-eid)))))
+                         need-fix? (or (seq existing-same)
+                                       (> (count update-eids) 1))]
+                     (if need-fix?
+                       (let [orders (->> siblings
+                                         (keep (fn [d]
+                                                 (let [sib-eid (:e d)]
+                                                   (when-not (contains? update-eids-set sib-eid)
+                                                     (:block/order (d/entity db sib-eid)))))))
+                             end (some (fn [order]
+                                         (when (> (compare order value) 0)
+                                           order))
+                                       orders)
+                             new-orders (db-order/gen-n-keys (count update-eids) value end)]
+                         (into acc
+                               (map-indexed (fn [idx id]
+                                              [:db/add id :block/order (nth new-orders idx)])
+                                            update-eids)))
+                       acc))
+                   acc))
                []
-               updates)]
+               groups)]
     (if (seq fixes)
       (into tx-data fixes)
       tx-data)))

+ 1 - 4
docs/agent-guide/db-sync/db-sync-guide.md

@@ -46,10 +46,7 @@ This guide helps AI agents implement and review db-sync features consistently ac
 
 ## Testing & Verification
 - Local dev(client+server): `bb dev:db-sync-start` runs the db-sync watcher, `wrangler dev`, and `yarn watch` with `ENABLE_DB_SYNC_LOCAL=true`
-- Unit tests: `bb dev:lint-and-test`
-- DB-sync tests: `bb dev:db-sync-test`
-- Single test example: `bb dev:test -v logseq.db-sync.storage-test/foo`
-- Worker local build: `clojure -M:cljs release db-sync`
+- DB-sync server side unit-tests: `bb dev:db-sync-test`
 
 ## Review Checklist
 - Protocol versioning and error handling are consistent across client/server.