core_test.cljs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. (ns frontend.modules.outliner.core-test
  2. (:require [cljs.test :refer [deftest is use-fixtures testing] :as test]
  3. [clojure.test.check.generators :as gen]
  4. [frontend.test.fixtures :as fixtures]
  5. [frontend.modules.outliner.core :as outliner-core]
  6. [frontend.modules.outliner.tree :as tree]
  7. [frontend.modules.outliner.transaction :as outliner-tx]
  8. [frontend.db :as db]
  9. [frontend.db.model :as db-model]
  10. [clojure.walk :as walk]
  11. [logseq.graph-parser.block :as gp-block]
  12. [datascript.core :as d]
  13. [frontend.test.helper :as helper]
  14. [clojure.set :as set]))
  15. (def test-db helper/test-db)
  16. (use-fixtures :each
  17. fixtures/load-test-env
  18. fixtures/react-components
  19. fixtures/reset-db)
  20. (defn get-block
  21. ([id]
  22. (get-block id false))
  23. ([id node?]
  24. (cond-> (db/pull test-db '[*] [:block/uuid id])
  25. node?
  26. outliner-core/block)))
  27. (defn build-node-tree
  28. [col]
  29. (let [blocks (->> col
  30. (walk/postwalk
  31. (fn [f]
  32. (if (and (vector? f)
  33. (= 2 (count f))
  34. (integer? (first f))
  35. (vector? (second f)))
  36. {:block/uuid (first f)
  37. :block/children (let [v (second f)]
  38. (cond
  39. (sequential? v)
  40. (mapv
  41. (fn [v]
  42. (if (integer? v)
  43. [v]
  44. v))
  45. v)
  46. :else
  47. [[v]]))}
  48. f)))
  49. (walk/postwalk
  50. (fn [f]
  51. (if (and (vector? f)
  52. (= 1 (count f))
  53. (integer? (first f)))
  54. {:block/uuid (first f)}
  55. f))))
  56. blocks (outliner-core/tree-vec-flatten blocks :block/children)]
  57. (map (fn [block] (assoc block
  58. :block/page 1
  59. :block/content 1)) blocks)))
  60. (defn- build-blocks
  61. [tree]
  62. (gp-block/with-parent-and-left 1 (build-node-tree tree)))
  63. (defn transact-tree!
  64. [tree]
  65. (db/transact! test-db (concat [{:db/id 1
  66. :block/uuid 1
  67. :block/name "Test page"}]
  68. (build-blocks tree))))
  69. (def tree
  70. [[22 [[2 [[3 [[4]
  71. [5]]]
  72. [6 [[7 [[8]]]]]
  73. [9 [[10]
  74. [11]]]]]
  75. [12 [[13]
  76. [14]
  77. [15]]]
  78. [16 [[17]]]]]])
  79. (defn get-blocks-count
  80. []
  81. (count (d/datoms (db/get-db test-db) :avet :block/uuid)))
  82. (defn get-blocks-ids
  83. []
  84. (set (map :v (d/datoms (db/get-db test-db) :avet :block/uuid))))
  85. (defn get-children
  86. [id]
  87. (->> (get-block id true)
  88. (tree/-get-children)
  89. (mapv #(-> % :data :block/uuid))))
  90. (deftest test-delete-block
  91. (testing "
  92. Insert a node between 6 and 9.
  93. [1 [[2 [[3 [[4]
  94. [5]]]
  95. [6 [[7 [[8]]]]] ;; delete 6
  96. [9 [[10]
  97. [11]]]]]
  98. [12 [[13]
  99. [14]
  100. [15]]]
  101. [16 [[17]]]]]
  102. "
  103. (transact-tree! tree)
  104. (let [block (get-block 6)]
  105. (outliner-tx/transact! {:graph test-db}
  106. (outliner-core/delete-blocks! [block] true))
  107. (is (= [3 9] (get-children 2))))))
  108. (deftest test-move-block-as-sibling
  109. (testing "
  110. Move 3 between 14 and 15.
  111. [1 [[2 [[6 [[7 [[8]]]]]
  112. [9 [[10]
  113. [11]]]]]
  114. [12 [[13]
  115. [14]
  116. [3 [[4] ;; moved 3
  117. [5]]]
  118. [15]]]
  119. [16 [[17]]]]]
  120. "
  121. (transact-tree! tree)
  122. (outliner-tx/transact!
  123. {:graph test-db}
  124. (outliner-core/move-blocks! [(get-block 3)] (get-block 14) true))
  125. (is (= [6 9] (get-children 2)))
  126. (is (= [13 14 3 15] (get-children 12))))
  127. (deftest test-move-block-as-first-child
  128. (testing "
  129. Move 3 as first child of 12.
  130. [1 [[2 [[6 [[7 [[8]]]]]
  131. [9 [[10]
  132. [11]]]]]
  133. [12 [[3 [[4] ;; moved 3
  134. [5]]]
  135. [13]
  136. [14]
  137. [15]]]
  138. [16 [[17]]]]]
  139. "
  140. (transact-tree! tree)
  141. (outliner-tx/transact!
  142. {:graph test-db}
  143. (outliner-core/move-blocks! [(get-block 3)] (get-block 12) false))
  144. (is (= [6 9] (get-children 2)))
  145. (is (= [3 13 14 15] (get-children 12))))))
  146. (deftest test-indent-blocks
  147. (testing "
  148. [1 [[2 [[3
  149. [[4]
  150. [5]
  151. [6 [[7 [[8]]]]] ;; indent 6, 9
  152. [9 [[10]
  153. [11]]]]]]]
  154. [12 [[13]
  155. [14]
  156. [15]]]
  157. [16 [[17]]]]]
  158. "
  159. (transact-tree! tree)
  160. (outliner-tx/transact!
  161. {:graph test-db}
  162. (outliner-core/indent-outdent-blocks! [(get-block 6) (get-block 9)] true))
  163. (is (= [4 5 6 9] (get-children 3)))))
  164. (deftest test-outdent-blocks
  165. (testing "
  166. [1 [[2 [[3]
  167. [4] ;; outdent 4, 5
  168. [5]
  169. [6 [[7 [[8]]]]]
  170. [9 [[10]
  171. [11]]]]]
  172. [12 [[13]
  173. [14]
  174. [15]]]
  175. [16 [[17]]]]]
  176. "
  177. (transact-tree! tree)
  178. (outliner-tx/transact!
  179. {:graph test-db}
  180. (outliner-core/indent-outdent-blocks! [(get-block 4) (get-block 5)] false))
  181. (is (= [3 4 5 6 9] (get-children 2)))))
  182. (deftest test-delete-blocks
  183. (testing "
  184. [1 [[2 [[3 [[4]
  185. [5]]]
  186. ;[6 [[7 [[8]]]]] delete 6, 9
  187. ;[9 [[10]
  188. ; [11]]]
  189. ]]
  190. [12 [[13]
  191. [14]
  192. [15]]]
  193. [16 [[17]]]]]
  194. "
  195. (transact-tree! tree)
  196. (outliner-tx/transact!
  197. {:graph test-db}
  198. (outliner-core/delete-blocks! [(get-block 6) (get-block 9)] {}))
  199. (is (= [3] (get-children 2)))))
  200. (deftest test-move-blocks-up-down
  201. (testing "
  202. [1 [[2 [[3 [[4]
  203. [5]]]
  204. [9 [[10] ;; swap 6 and 9
  205. [11]]]
  206. [6 [[7 [[8]]]]]]]
  207. [12 [[13]
  208. [14]
  209. [15]]]
  210. [16 [[17]]]]]
  211. "
  212. (transact-tree! tree)
  213. (outliner-tx/transact!
  214. {:graph test-db}
  215. (outliner-core/move-blocks-up-down! [(get-block 9)] true))
  216. (is (= [3 9 6] (get-children 2)))))
  217. (deftest test-insert-blocks
  218. (testing "
  219. add [18 [19 20] 21] after 6
  220. [1 [[2 [[3 [[4]
  221. [5]]]
  222. [6 [[7 [[8]]]]]
  223. [9 [[10]
  224. [11]]]]]
  225. [12 [[13]
  226. [14]
  227. [15]]]
  228. [16 [[17]]]]]
  229. "
  230. (transact-tree! tree)
  231. (let [new-blocks (build-blocks [[18 [[19] [20]]]
  232. [21]])
  233. target-block (get-block 6)]
  234. (outliner-tx/transact!
  235. {:graph test-db}
  236. (outliner-core/insert-blocks! new-blocks target-block {:sibling? true
  237. :keep-uuid? true
  238. :replace-empty-target? false}))
  239. (is (= [3 6 18 21 9] (get-children 2)))
  240. (is (= [19 20] (get-children 18))))))
  241. (deftest test-batch-transact
  242. (testing "add 4, 5 after 2 and delete 3"
  243. (let [tree [[1 [[2] [3]]]]]
  244. (transact-tree! tree)
  245. (let [new-blocks (build-blocks [[4 [5]]])
  246. target-block (get-block 2)]
  247. (outliner-tx/transact!
  248. {:graph test-db}
  249. (outliner-core/insert-blocks! new-blocks target-block {:sibling? false
  250. :keep-uuid? true
  251. :replace-empty-target? false})
  252. (outliner-core/delete-blocks! [(get-block 3)] {}))
  253. (is (= [4] (get-children 2)))
  254. (is (= [5] (get-children 4)))
  255. (is (nil? (get-block 3)))))))
  256. (deftest test-bocks-with-level
  257. (testing "blocks with level"
  258. (is (= (outliner-core/blocks-with-level
  259. [{:db/id 6,
  260. :block/left #:db{:id 3},
  261. :block/level 3,
  262. :block/parent #:db{:id 2},
  263. :block/uuid 6}
  264. {:db/id 9,
  265. :block/left #:db{:id 6},
  266. :block/level 3,
  267. :block/parent #:db{:id 2},
  268. :block/uuid 9}])
  269. [{:db/id 6,
  270. :block/left #:db{:id 3},
  271. :block/level 1,
  272. :block/parent #:db{:id 2},
  273. :block/uuid 6}
  274. {:db/id 9,
  275. :block/left #:db{:id 6},
  276. :block/level 1,
  277. :block/parent #:db{:id 2},
  278. :block/uuid 9}]))
  279. (is (= (outliner-core/blocks-with-level
  280. [{:db/id 6,
  281. :block/left #:db{:id 3},
  282. :block/level 3,
  283. :block/parent #:db{:id 2},
  284. :block/uuid 6}
  285. {:db/id 9,
  286. :block/left #:db{:id 6},
  287. :block/level 4,
  288. :block/parent #:db{:id 6},
  289. :block/uuid 9}])
  290. [{:db/id 6,
  291. :block/left #:db{:id 3},
  292. :block/level 1,
  293. :block/parent #:db{:id 2},
  294. :block/uuid 6}
  295. {:db/id 9,
  296. :block/left #:db{:id 6},
  297. :block/level 2,
  298. :block/parent #:db{:id 6},
  299. :block/uuid 9}]))))
  300. (deftest test-get-sorted-block-and-children
  301. (testing "get-sorted-block-and-children"
  302. (transact-tree! tree)
  303. (is (=
  304. '(2 3 4 5 6 7 8 9 10 11)
  305. (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 2))))))
  306. (is (=
  307. '(22 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17)
  308. (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 22))))))
  309. (is (=
  310. '(16 17)
  311. (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 16))))))))
  312. ;;; Fuzzy tests
  313. (def init-id (atom 100))
  314. (def unique-id (gen/fmap (fn [_] (swap! init-id inc)) gen/nat))
  315. (def compound (fn [inner-gen]
  316. (gen/tuple unique-id (gen/vector inner-gen 1 2))))
  317. (def gen-node (gen/recursive-gen compound unique-id))
  318. (def gen-tree (gen/vector gen-node))
  319. (defn- gen-safe-tree
  320. []
  321. (->> (gen/generate gen-tree)
  322. (remove integer?)))
  323. (defn gen-blocks
  324. []
  325. (let [tree (gen-safe-tree)]
  326. (if (seq tree)
  327. (let [result (build-blocks tree)]
  328. (if (seq result)
  329. result
  330. (gen-blocks)))
  331. (gen-blocks))))
  332. (defn insert-blocks!
  333. [blocks target]
  334. (outliner-tx/transact! {:graph test-db}
  335. (outliner-core/insert-blocks! blocks
  336. target
  337. {:sibling? (gen/generate gen/boolean)
  338. :keep-uuid? true
  339. :replace-empty-target? false})))
  340. (defn transact-random-tree!
  341. []
  342. (let [tree (gen-safe-tree)]
  343. (transact-tree! tree)))
  344. (defn get-datoms
  345. []
  346. (d/datoms (db/get-db test-db) :avet :block/uuid))
  347. (defn get-random-block
  348. []
  349. (let [datoms (->> (get-datoms)
  350. (remove (fn [datom] (= 1 (:e datom)))))]
  351. (if (seq datoms)
  352. (let [id (:e (gen/generate (gen/elements datoms)))]
  353. (db/pull test-db '[*] id))
  354. (do
  355. (transact-random-tree!)
  356. (get-random-block)))))
  357. (defn get-random-successive-blocks
  358. []
  359. (let [limit (inc (rand-int 20))]
  360. (when-let [block (get-random-block)]
  361. (loop [result [block]
  362. node block]
  363. (if-let [next (outliner-core/get-right-sibling (:db/id node))]
  364. (let [next (db/pull test-db '[*] (:db/id next))]
  365. (if (>= (count result) limit)
  366. result
  367. (recur (conj result next) next)))
  368. result)))))
  369. (deftest ^:long random-inserts
  370. (testing "Random inserts"
  371. (transact-random-tree!)
  372. (let [c1 (get-blocks-ids)
  373. *random-blocks (atom c1)]
  374. (dotimes [_i 200]
  375. ;; (prn "random insert: " i)
  376. (let [blocks (gen-blocks)]
  377. (swap! *random-blocks (fn [old]
  378. (set/union old (set (map :block/uuid blocks)))))
  379. (insert-blocks! blocks (get-random-block)))
  380. (let [total (get-blocks-count)]
  381. ;; (when (not= total (count @*random-blocks))
  382. ;; (defonce wrong-db (db/get-db test-db))
  383. ;; (defonce random-blocks @*random-blocks))
  384. (is (= total (count @*random-blocks))))))))
  385. (deftest ^:long random-deletes
  386. (testing "Random deletes"
  387. (transact-random-tree!)
  388. (dotimes [_i 100]
  389. ;; (prn "Random deletes: " i)
  390. (insert-blocks! (gen-blocks) (get-random-block))
  391. (let [blocks (get-random-successive-blocks)]
  392. (when (seq blocks)
  393. (outliner-tx/transact! {:graph test-db}
  394. (outliner-core/delete-blocks! blocks {})))))))
  395. (deftest ^:long random-moves
  396. (testing "Random moves"
  397. (transact-random-tree!)
  398. (let [c1 (get-blocks-ids)
  399. *random-blocks (atom c1)]
  400. (dotimes [_i 100]
  401. ;; (prn "Random move: " i)
  402. (let [blocks (gen-blocks)]
  403. (swap! *random-blocks (fn [old]
  404. (set/union old (set (map :block/uuid blocks)))))
  405. (insert-blocks! blocks (get-random-block)))
  406. (let [blocks (get-random-successive-blocks)]
  407. (when (seq blocks)
  408. (let [target (get-random-block)]
  409. (outliner-tx/transact! {:graph test-db}
  410. (outliner-core/move-blocks! blocks target (gen/generate gen/boolean)))
  411. (let [total (get-blocks-count)]
  412. (is (= total (count @*random-blocks)))))))))))
  413. (deftest ^:long random-move-up-down
  414. (testing "Random move up down"
  415. (transact-random-tree!)
  416. (let [c1 (get-blocks-ids)
  417. *random-blocks (atom c1)]
  418. (dotimes [_i 100]
  419. ;; (prn "Random move up/down: " i)
  420. (let [blocks (gen-blocks)]
  421. (swap! *random-blocks (fn [old]
  422. (set/union old (set (map :block/uuid blocks)))))
  423. (insert-blocks! blocks (get-random-block)))
  424. (let [blocks (get-random-successive-blocks)]
  425. (when (seq blocks)
  426. (outliner-tx/transact! {:graph test-db}
  427. (outliner-core/move-blocks-up-down! blocks (gen/generate gen/boolean)))
  428. (let [total (get-blocks-count)]
  429. (is (= total (count @*random-blocks))))))))))
  430. (deftest ^:long random-indent-outdent
  431. (testing "Random indent and outdent"
  432. (transact-random-tree!)
  433. (let [c1 (get-blocks-ids)
  434. *random-blocks (atom c1)]
  435. (dotimes [_i 100]
  436. ;; (prn "Random move indent/outdent: " i)
  437. (let [blocks (gen-blocks)]
  438. (swap! *random-blocks (fn [old]
  439. (set/union old (set (map :block/uuid blocks)))))
  440. (insert-blocks! blocks (get-random-block)))
  441. (let [blocks (get-random-successive-blocks)]
  442. (when (seq blocks)
  443. (outliner-tx/transact! {:graph test-db}
  444. (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean)))
  445. (let [total (get-blocks-count)]
  446. (is (= total (count @*random-blocks))))))))))
  447. (deftest ^:long random-mixed-ops
  448. (testing "Random mixed operations"
  449. (transact-random-tree!)
  450. (let [c1 (get-blocks-ids)
  451. *random-blocks (atom c1)
  452. ops [
  453. ;; insert
  454. (fn []
  455. (let [blocks (gen-blocks)]
  456. (swap! *random-blocks (fn [old]
  457. (set/union old (set (map :block/uuid blocks)))))
  458. (insert-blocks! blocks (get-random-block))))
  459. ;; delete
  460. (fn []
  461. (let [blocks (get-random-successive-blocks)]
  462. (when (seq blocks)
  463. (swap! *random-blocks (fn [old]
  464. (set/difference old (set (map :block/uuid blocks)))))
  465. (outliner-tx/transact! {:graph test-db}
  466. (outliner-core/delete-blocks! blocks {})))))
  467. ;; move
  468. (fn []
  469. (let [blocks (get-random-successive-blocks)]
  470. (when (seq blocks)
  471. (outliner-tx/transact! {:graph test-db}
  472. (outliner-core/move-blocks! blocks (get-random-block) (gen/generate gen/boolean))))))
  473. ;; move up down
  474. (fn []
  475. (let [blocks (get-random-successive-blocks)]
  476. (when (seq blocks)
  477. (outliner-tx/transact! {:graph test-db}
  478. (outliner-core/move-blocks-up-down! blocks (gen/generate gen/boolean))))))
  479. ;; indent outdent
  480. (fn []
  481. (let [blocks (get-random-successive-blocks)]
  482. (when (seq blocks)
  483. (outliner-tx/transact! {:graph test-db}
  484. (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean))))))]]
  485. (dotimes [_i 100]
  486. ((rand-nth ops)))
  487. (let [total (get-blocks-count)
  488. page-id 1]
  489. ;; Invariants:
  490. ;; 1. total blocks <= inserted blocks - deleted block
  491. (is (<= total (count @*random-blocks)))
  492. ;; 2. verify page's length + page itself = total blocks
  493. (is (= (inc (db-model/get-page-blocks-count test-db page-id))
  494. total))
  495. ;; 3. verify the outliner parent/left structure
  496. (is (= (inc (count (db-model/get-paginated-blocks test-db page-id {:limit total
  497. :use-cache? false})))
  498. total))))))
  499. (comment
  500. (dotimes [i 5]
  501. (do
  502. (frontend.test.fixtures/reset-datascript test-db)
  503. (cljs.test/run-tests))
  504. )
  505. )