datahike-browser-tests 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/.circleci/config.yml +405 -0
  2. package/.circleci/scripts/gen_ci.clj +194 -0
  3. package/.cirrus.yml +60 -0
  4. package/.clj-kondo/babashka/sci/config.edn +1 -0
  5. package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
  6. package/.clj-kondo/config.edn +95 -0
  7. package/.dir-locals.el +2 -0
  8. package/.github/FUNDING.yml +3 -0
  9. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
  10. package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
  11. package/.github/ISSUE_TEMPLATE/config.yml +6 -0
  12. package/.github/pull_request_template.md +24 -0
  13. package/.github/workflows/native-image.yml +84 -0
  14. package/LICENSE +203 -0
  15. package/README.md +273 -0
  16. package/bb/deps.edn +9 -0
  17. package/bb/resources/github-fingerprints +3 -0
  18. package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
  19. package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
  20. package/bb/resources/native-image-tests/run-native-image-tests +74 -0
  21. package/bb/resources/native-image-tests/run-python-tests +22 -0
  22. package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
  23. package/bb/resources/native-image-tests/testconfig.edn +5 -0
  24. package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  25. package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
  26. package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
  27. package/bb/resources/template/pom.xml +22 -0
  28. package/bb/src/tools/build.clj +132 -0
  29. package/bb/src/tools/clj_kondo.clj +32 -0
  30. package/bb/src/tools/deploy.clj +26 -0
  31. package/bb/src/tools/examples.clj +19 -0
  32. package/bb/src/tools/npm.clj +100 -0
  33. package/bb/src/tools/python.clj +14 -0
  34. package/bb/src/tools/release.clj +94 -0
  35. package/bb/src/tools/test.clj +148 -0
  36. package/bb/src/tools/version.clj +47 -0
  37. package/bb.edn +269 -0
  38. package/benchmark/src/benchmark/cli.clj +195 -0
  39. package/benchmark/src/benchmark/compare.clj +157 -0
  40. package/benchmark/src/benchmark/config.clj +316 -0
  41. package/benchmark/src/benchmark/measure.clj +187 -0
  42. package/benchmark/src/benchmark/store.clj +190 -0
  43. package/benchmark/test/benchmark/measure_test.clj +156 -0
  44. package/build.clj +30 -0
  45. package/config.edn +49 -0
  46. package/deps.edn +138 -0
  47. package/dev/sandbox.clj +82 -0
  48. package/dev/sandbox.cljs +127 -0
  49. package/dev/sandbox_benchmarks.clj +27 -0
  50. package/dev/sandbox_client.clj +87 -0
  51. package/dev/sandbox_transact_bench.clj +109 -0
  52. package/dev/user.clj +79 -0
  53. package/doc/README.md +96 -0
  54. package/doc/adl/README.md +6 -0
  55. package/doc/adl/adr-000-adr.org +28 -0
  56. package/doc/adl/adr-001-attribute-references.org +15 -0
  57. package/doc/adl/adr-002-build-tooling.org +54 -0
  58. package/doc/adl/adr-003-db-meta-data.md +52 -0
  59. package/doc/adl/adr-004-github-flow.md +40 -0
  60. package/doc/adl/adr-XYZ-template.md +30 -0
  61. package/doc/adl/index.org +3 -0
  62. package/doc/assets/datahike-logo.svg +3 -0
  63. package/doc/assets/datahiking-invoice.org +85 -0
  64. package/doc/assets/hhtree2.png +0 -0
  65. package/doc/assets/network_topology.svg +624 -0
  66. package/doc/assets/perf.png +0 -0
  67. package/doc/assets/schema_mindmap.mm +132 -0
  68. package/doc/assets/schema_mindmap.svg +970 -0
  69. package/doc/assets/temporal_index.mm +74 -0
  70. package/doc/backend-development.md +78 -0
  71. package/doc/bb-pod.md +89 -0
  72. package/doc/benchmarking.md +360 -0
  73. package/doc/bindings/edn-conversion.md +383 -0
  74. package/doc/cli.md +162 -0
  75. package/doc/cljdoc.edn +27 -0
  76. package/doc/cljs-support.md +133 -0
  77. package/doc/config.md +406 -0
  78. package/doc/contributing.md +114 -0
  79. package/doc/datalog-vs-sql.md +210 -0
  80. package/doc/datomic_differences.md +109 -0
  81. package/doc/development/pull-api-ns.md +186 -0
  82. package/doc/development/pull-frame-state-diagram.jpg +0 -0
  83. package/doc/distributed.md +566 -0
  84. package/doc/entity_spec.md +92 -0
  85. package/doc/gc.md +273 -0
  86. package/doc/java-api.md +808 -0
  87. package/doc/javascript-api.md +421 -0
  88. package/doc/libdatahike.md +86 -0
  89. package/doc/logging_and_error_handling.md +43 -0
  90. package/doc/norms.md +66 -0
  91. package/doc/schema-migration.md +85 -0
  92. package/doc/schema.md +287 -0
  93. package/doc/storage-backends.md +363 -0
  94. package/doc/store-id-refactoring.md +596 -0
  95. package/doc/time_variance.md +325 -0
  96. package/doc/unstructured.md +167 -0
  97. package/doc/versioning.md +261 -0
  98. package/examples/basic/README.md +19 -0
  99. package/examples/basic/deps.edn +6 -0
  100. package/examples/basic/docker-compose.yml +13 -0
  101. package/examples/basic/src/examples/core.clj +60 -0
  102. package/examples/basic/src/examples/schema.clj +155 -0
  103. package/examples/basic/src/examples/store.clj +60 -0
  104. package/examples/basic/src/examples/time_travel.clj +185 -0
  105. package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
  106. package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  107. package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
  108. package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
  109. package/examples/java/README.md +162 -0
  110. package/examples/java/pom.xml +62 -0
  111. package/examples/java/src/main/java/examples/QuickStart.java +115 -0
  112. package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
  113. package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
  114. package/flake.lock +27 -0
  115. package/flake.nix +27 -0
  116. package/http-server/datahike/http/middleware.clj +75 -0
  117. package/http-server/datahike/http/server.clj +269 -0
  118. package/java/src/datahike/java/Database.java +274 -0
  119. package/java/src/datahike/java/Datahike.java +281 -0
  120. package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
  121. package/java/src/datahike/java/DatahikeTest.java +370 -0
  122. package/java/src/datahike/java/EDN.java +170 -0
  123. package/java/src/datahike/java/IEntity.java +11 -0
  124. package/java/src/datahike/java/Keywords.java +161 -0
  125. package/java/src/datahike/java/SchemaFlexibility.java +52 -0
  126. package/java/src/datahike/java/Util.java +219 -0
  127. package/karma.conf.js +19 -0
  128. package/libdatahike/compile-cpp +7 -0
  129. package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
  130. package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
  131. package/libdatahike/src/test_cpp.cpp +61 -0
  132. package/npm-package/PUBLISHING.md +140 -0
  133. package/npm-package/README.md +226 -0
  134. package/npm-package/package.template.json +34 -0
  135. package/npm-package/test-isomorphic.ts +281 -0
  136. package/npm-package/test.js +557 -0
  137. package/npm-package/typescript-test.ts +70 -0
  138. package/package.json +16 -0
  139. package/pydatahike/README.md +569 -0
  140. package/pydatahike/pyproject.toml +91 -0
  141. package/pydatahike/setup.py +42 -0
  142. package/pydatahike/src/datahike/__init__.py +134 -0
  143. package/pydatahike/src/datahike/_native.py +250 -0
  144. package/pydatahike/src/datahike/_version.py +2 -0
  145. package/pydatahike/src/datahike/database.py +722 -0
  146. package/pydatahike/src/datahike/edn.py +311 -0
  147. package/pydatahike/src/datahike/py.typed +0 -0
  148. package/pydatahike/tests/conftest.py +17 -0
  149. package/pydatahike/tests/test_basic.py +170 -0
  150. package/pydatahike/tests/test_database.py +51 -0
  151. package/pydatahike/tests/test_edn_conversion.py +299 -0
  152. package/pydatahike/tests/test_query.py +99 -0
  153. package/pydatahike/tests/test_schema.py +55 -0
  154. package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
  155. package/resources/example_server.edn +4 -0
  156. package/shadow-cljs.edn +56 -0
  157. package/src/data_readers.clj +7 -0
  158. package/src/datahike/api/impl.cljc +176 -0
  159. package/src/datahike/api/specification.cljc +633 -0
  160. package/src/datahike/api/types.cljc +261 -0
  161. package/src/datahike/api.cljc +41 -0
  162. package/src/datahike/array.cljc +99 -0
  163. package/src/datahike/cli.clj +166 -0
  164. package/src/datahike/cljs.cljs +6 -0
  165. package/src/datahike/codegen/cli.clj +406 -0
  166. package/src/datahike/codegen/clj_kondo.clj +291 -0
  167. package/src/datahike/codegen/java.clj +403 -0
  168. package/src/datahike/codegen/naming.cljc +33 -0
  169. package/src/datahike/codegen/native.clj +559 -0
  170. package/src/datahike/codegen/pod.clj +488 -0
  171. package/src/datahike/codegen/python.clj +838 -0
  172. package/src/datahike/codegen/report.clj +55 -0
  173. package/src/datahike/codegen/typescript.clj +262 -0
  174. package/src/datahike/codegen/validation.clj +145 -0
  175. package/src/datahike/config.cljc +294 -0
  176. package/src/datahike/connections.cljc +16 -0
  177. package/src/datahike/connector.cljc +265 -0
  178. package/src/datahike/constants.cljc +142 -0
  179. package/src/datahike/core.cljc +297 -0
  180. package/src/datahike/datom.cljc +459 -0
  181. package/src/datahike/db/interface.cljc +119 -0
  182. package/src/datahike/db/search.cljc +305 -0
  183. package/src/datahike/db/transaction.cljc +937 -0
  184. package/src/datahike/db/utils.cljc +338 -0
  185. package/src/datahike/db.cljc +956 -0
  186. package/src/datahike/experimental/unstructured.cljc +126 -0
  187. package/src/datahike/experimental/versioning.cljc +172 -0
  188. package/src/datahike/externs.js +31 -0
  189. package/src/datahike/gc.cljc +69 -0
  190. package/src/datahike/http/client.clj +188 -0
  191. package/src/datahike/http/writer.clj +79 -0
  192. package/src/datahike/impl/entity.cljc +218 -0
  193. package/src/datahike/index/interface.cljc +93 -0
  194. package/src/datahike/index/persistent_set.cljc +469 -0
  195. package/src/datahike/index/utils.cljc +44 -0
  196. package/src/datahike/index.cljc +32 -0
  197. package/src/datahike/js/api.cljs +172 -0
  198. package/src/datahike/js/api_macros.clj +22 -0
  199. package/src/datahike/js.cljs +163 -0
  200. package/src/datahike/json.cljc +209 -0
  201. package/src/datahike/lru.cljc +146 -0
  202. package/src/datahike/migrate.clj +39 -0
  203. package/src/datahike/norm/norm.clj +245 -0
  204. package/src/datahike/online_gc.cljc +252 -0
  205. package/src/datahike/pod.clj +155 -0
  206. package/src/datahike/pull_api.cljc +325 -0
  207. package/src/datahike/query.cljc +1945 -0
  208. package/src/datahike/query_stats.cljc +88 -0
  209. package/src/datahike/readers.cljc +62 -0
  210. package/src/datahike/remote.cljc +218 -0
  211. package/src/datahike/schema.cljc +228 -0
  212. package/src/datahike/schema_cache.cljc +42 -0
  213. package/src/datahike/spec.cljc +101 -0
  214. package/src/datahike/store.cljc +80 -0
  215. package/src/datahike/tools.cljc +308 -0
  216. package/src/datahike/transit.cljc +80 -0
  217. package/src/datahike/writer.cljc +239 -0
  218. package/src/datahike/writing.cljc +362 -0
  219. package/src/deps.cljs +1 -0
  220. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
  221. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
  222. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
  223. package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
  224. package/test/datahike/integration_test/config_record_file_test.clj +14 -0
  225. package/test/datahike/integration_test/config_record_test.clj +14 -0
  226. package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
  227. package/test/datahike/integration_test/return_map_test.clj +62 -0
  228. package/test/datahike/integration_test.cljc +67 -0
  229. package/test/datahike/norm/norm_test.clj +124 -0
  230. package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
  231. package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
  232. package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
  233. package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
  234. package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
  235. package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
  236. package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
  237. package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
  238. package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
  239. package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
  240. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
  241. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
  242. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
  243. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
  244. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
  245. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
  246. package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
  247. package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
  248. package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
  249. package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
  250. package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
  251. package/test/datahike/test/api_test.cljc +895 -0
  252. package/test/datahike/test/array_test.cljc +40 -0
  253. package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
  254. package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
  255. package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
  256. package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
  257. package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
  258. package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
  259. package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
  260. package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
  261. package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
  262. package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
  263. package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
  264. package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
  265. package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
  266. package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
  267. package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
  268. package/test/datahike/test/attribute_refs/utils.cljc +128 -0
  269. package/test/datahike/test/cache_test.cljc +38 -0
  270. package/test/datahike/test/components_test.cljc +92 -0
  271. package/test/datahike/test/config_test.cljc +158 -0
  272. package/test/datahike/test/core_test.cljc +105 -0
  273. package/test/datahike/test/datom_test.cljc +44 -0
  274. package/test/datahike/test/db_test.cljc +54 -0
  275. package/test/datahike/test/entity_spec_test.cljc +159 -0
  276. package/test/datahike/test/entity_test.cljc +103 -0
  277. package/test/datahike/test/explode_test.cljc +143 -0
  278. package/test/datahike/test/filter_test.cljc +75 -0
  279. package/test/datahike/test/gc_test.cljc +159 -0
  280. package/test/datahike/test/http/server_test.clj +192 -0
  281. package/test/datahike/test/http/writer_test.clj +86 -0
  282. package/test/datahike/test/ident_test.cljc +32 -0
  283. package/test/datahike/test/index_test.cljc +345 -0
  284. package/test/datahike/test/insert.cljc +125 -0
  285. package/test/datahike/test/java_bindings_test.clj +6 -0
  286. package/test/datahike/test/listen_test.cljc +41 -0
  287. package/test/datahike/test/lookup_refs_test.cljc +266 -0
  288. package/test/datahike/test/lru_test.cljc +27 -0
  289. package/test/datahike/test/migrate_test.clj +297 -0
  290. package/test/datahike/test/model/core.cljc +376 -0
  291. package/test/datahike/test/model/invariant.cljc +142 -0
  292. package/test/datahike/test/model/rng.cljc +82 -0
  293. package/test/datahike/test/model_test.clj +217 -0
  294. package/test/datahike/test/nodejs_test.cljs +262 -0
  295. package/test/datahike/test/online_gc_test.cljc +475 -0
  296. package/test/datahike/test/pod_test.clj +369 -0
  297. package/test/datahike/test/pull_api_test.cljc +474 -0
  298. package/test/datahike/test/purge_test.cljc +144 -0
  299. package/test/datahike/test/query_aggregates_test.cljc +101 -0
  300. package/test/datahike/test/query_find_specs_test.cljc +52 -0
  301. package/test/datahike/test/query_fns_test.cljc +523 -0
  302. package/test/datahike/test/query_interop_test.cljc +47 -0
  303. package/test/datahike/test/query_not_test.cljc +189 -0
  304. package/test/datahike/test/query_or_test.cljc +158 -0
  305. package/test/datahike/test/query_pull_test.cljc +147 -0
  306. package/test/datahike/test/query_rules_test.cljc +248 -0
  307. package/test/datahike/test/query_stats_test.cljc +218 -0
  308. package/test/datahike/test/query_test.cljc +984 -0
  309. package/test/datahike/test/schema_test.cljc +424 -0
  310. package/test/datahike/test/specification_test.cljc +30 -0
  311. package/test/datahike/test/store_test.cljc +78 -0
  312. package/test/datahike/test/stress_test.cljc +57 -0
  313. package/test/datahike/test/time_variance_test.cljc +518 -0
  314. package/test/datahike/test/tools_test.clj +134 -0
  315. package/test/datahike/test/transact_test.cljc +518 -0
  316. package/test/datahike/test/tuples_test.cljc +564 -0
  317. package/test/datahike/test/unstructured_test.cljc +291 -0
  318. package/test/datahike/test/upsert_impl_test.cljc +205 -0
  319. package/test/datahike/test/upsert_test.cljc +363 -0
  320. package/test/datahike/test/utils.cljc +110 -0
  321. package/test/datahike/test/validation_test.cljc +48 -0
  322. package/test/datahike/test/versioning_test.cljc +56 -0
  323. package/test/datahike/test.cljc +66 -0
  324. package/tests.edn +24 -0
@@ -0,0 +1,291 @@
1
+ (ns datahike.test.unstructured-test
2
+ (:require
3
+ #?(:cljs [cljs.test :as t :refer-macros [is deftest testing]]
4
+ :clj [clojure.test :as t :refer [is deftest testing]])
5
+ [datahike.api :as d]
6
+ [datahike.experimental.unstructured :as du]))
7
+
8
+ (deftest test-value-type-inference
9
+ (testing "Basic type inference"
10
+ (is (= :db.type/long (du/value->type 42)))
11
+ (is (= :db.type/float (du/value->type (float 42.0))))
12
+ (is (= :db.type/string (du/value->type "hello")))
13
+ (is (= :db.type/boolean (du/value->type true)))
14
+ (is (= :db.type/keyword (du/value->type :keyword)))
15
+ (is (= :db.type/ref (du/value->type {:a 1})))))
16
+
17
+ (deftest test-schema-inference
18
+ (testing "Schema inference for basic values"
19
+ (is (= {:db/ident :name
20
+ :db/valueType :db.type/string
21
+ :db/cardinality :db.cardinality/one}
22
+ (du/infer-value-schema :name "Alice"))))
23
+
24
+ (testing "Schema inference for collections"
25
+ (is (= {:db/ident :tags
26
+ :db/valueType :db.type/keyword
27
+ :db/cardinality :db.cardinality/many}
28
+ (du/infer-value-schema :tags [:tag1 :tag2])))
29
+
30
+ (is (= {:db/ident :address
31
+ :db/valueType :db.type/ref
32
+ :db/cardinality :db.cardinality/one}
33
+ (du/infer-value-schema :address {:street "123 Main St"}))))
34
+
35
+ (testing "Empty vector handling"
36
+ (is (nil? (du/infer-value-schema :empty-tags [])))))
37
+
38
+ (deftest test-process-unstructured-data
39
+ (testing "Processing simple data"
40
+ (let [data {:name "Alice" :age 30}
41
+ result (du/process-unstructured-data data)]
42
+ (is (= 2 (count (:schema result))))
43
+ (is (= 1 (count (:tx-data result))))
44
+ (is (= -1 (:db/id (first (:tx-data result)))))))
45
+
46
+ (testing "Processing nested data"
47
+ (let [data {:name "Bob"
48
+ :age 25
49
+ :address {:street "123 Main St"
50
+ :city "Anytown"}}
51
+ result (du/process-unstructured-data data)]
52
+ ;; There should be 5 schema entries: name, age, address, street, city
53
+ (is (= 5 (count (:schema result))))
54
+ (is (= 2 (count (:tx-data result))))
55
+ (is (some #(= -1 (:db/id %)) (:tx-data result)))))
56
+
57
+ (testing "Processing empty vectors"
58
+ (let [data {:name "Charlie"
59
+ :tags []}
60
+ result (du/process-unstructured-data data)]
61
+ ;; The schema should only include the name attribute, not tags
62
+ (is (= 1 (count (:schema result))))
63
+ (is (= :name
64
+ (-> result :schema first :db/ident)))
65
+ ;; The transaction data should still include the empty tags vector
66
+ (let [tx-entity (first (:tx-data result))]
67
+ (is (= "Charlie" (:name tx-entity)))
68
+ (is (= [] (:tags tx-entity)))))))
69
+
70
+ (deftest test-basic-schema-on-read
71
+ (testing "Basic operations with schema-on-read"
72
+ (let [cfg {:store {:backend :memory
73
+ :id #uuid "10000000-0000-0000-0000-000000000001"}
74
+ :schema-flexibility :read}
75
+ _ (d/delete-database cfg)
76
+ _ (d/create-database cfg)
77
+ conn (d/connect cfg)]
78
+
79
+ ;; Just add simple data directly
80
+ (d/transact conn [{:name "Alice" :age 30}])
81
+
82
+ ;; Simple query
83
+ (is (= #{["Alice"]}
84
+ (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))
85
+
86
+ (d/release conn))))
87
+
88
+ (deftest test-unstructured-schema-on-read
89
+ (testing "Using process-unstructured-data with schema-on-read"
90
+ (let [cfg {:store {:backend :memory
91
+ :id #uuid "20000000-0000-0000-0000-000000000002"}
92
+ :schema-flexibility :read}
93
+ _ (d/delete-database cfg)
94
+ _ (d/create-database cfg)
95
+ conn (d/connect cfg)
96
+ data {:name "Bob"
97
+ :age 25
98
+ :address {:street "123 Main St"
99
+ :city "Anytown"}}]
100
+
101
+ ;; Use process-unstructured-data to prepare data
102
+ (let [processed (du/process-unstructured-data data)
103
+ tx-report (d/transact conn (:tx-data processed))]
104
+
105
+ ;; Verify simple attribute
106
+ (is (= #{["Bob"]}
107
+ (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))
108
+
109
+ ;; We can't directly query nested data with the temp IDs in the schema-on-read mode
110
+ ;; This requires a different approach
111
+ ;; Let's verify we can at least retrieve the address entity id
112
+ (let [address-id (d/q '[:find ?a :where [?e :address ?a]] (d/db conn))]
113
+ (is (not (empty? address-id)))))
114
+
115
+ (d/release conn))))
116
+
117
+ (deftest test-transact-unstructured-api
118
+ (testing "Using the transact-unstructured API with schema-on-read"
119
+ (let [cfg {:store {:backend :memory
120
+ :id #uuid "30000000-0000-0000-0000-000000000003"}
121
+ :schema-flexibility :read}
122
+ _ (d/delete-database cfg)
123
+ _ (d/create-database cfg)
124
+ conn (d/connect cfg)
125
+ data {:name "Charlie"
126
+ :age 35
127
+ :skills ["programming" "design" "management"]
128
+ :contact {:email "charlie@example.com"
129
+ :phone "555-1234"}}]
130
+
131
+ ;; Use the high-level API
132
+ (du/transact-unstructured conn data)
133
+
134
+ ;; Verify basic data
135
+ (is (= #{["Charlie"]}
136
+ (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))
137
+ (is (= #{[35]}
138
+ (d/q '[:find ?a :where [?e :age ?a]] (d/db conn))))
139
+
140
+ ;; Verify array data - just check if any skills were added
141
+ (let [skills (d/q '[:find ?s :where [_ :skills ?s]] (d/db conn))]
142
+ (is (not (empty? skills))))
143
+
144
+ ;; Verify that nested object was stored properly by checking contact
145
+ (let [contacts (d/q '[:find ?e :where [?e :email "charlie@example.com"]] (d/db conn))]
146
+ (is (not (empty? contacts))))
147
+
148
+ (d/release conn))))
149
+
150
+ (deftest test-schema-on-write
151
+ (testing "Using transact-unstructured with schema-on-write"
152
+ (let [cfg {:store {:backend :memory
153
+ :id #uuid "40000000-0000-0000-0000-000000000004"}} ;; Default is schema-on-write
154
+ _ (d/delete-database cfg)
155
+ _ (d/create-database cfg)
156
+ conn (d/connect cfg)
157
+ data {:name "Diana"
158
+ :age 28
159
+ :active true}]
160
+
161
+ ;; First, verify that we can't insert data directly (requires schema)
162
+ (is (thrown? Exception
163
+ (d/transact conn [data])))
164
+
165
+ ;; Now use transact-unstructured, which should infer and apply schema first
166
+ (du/transact-unstructured conn data)
167
+
168
+ ;; Verify the schema was created
169
+ (let [schema-query '[:find ?a ?t ?c
170
+ :where
171
+ [?e :db/ident ?a]
172
+ [?e :db/valueType ?t]
173
+ [?e :db/cardinality ?c]]]
174
+ (is (= #{[:name :db.type/string :db.cardinality/one]
175
+ [:age :db.type/long :db.cardinality/one]
176
+ [:active :db.type/boolean :db.cardinality/one]}
177
+ (d/q schema-query (d/db conn)))))
178
+
179
+ ;; Verify the data was inserted
180
+ (is (= #{["Diana"]}
181
+ (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))
182
+ (is (= #{[28]}
183
+ (d/q '[:find ?a :where [?e :age ?a]] (d/db conn))))
184
+ (is (= #{[true]}
185
+ (d/q '[:find ?a :where [?e :active ?a]] (d/db conn))))
186
+
187
+ ;; Test schema evolution with new attributes
188
+ (let [new-data {:name "Erik"
189
+ :age 42
190
+ :email "erik@example.com" ;; New attribute
191
+ :active false}]
192
+
193
+ ;; This should work and auto-add the email schema
194
+ (du/transact-unstructured conn new-data)
195
+
196
+ ;; Verify new schema was added
197
+ (let [schema-query '[:find ?a ?t ?c
198
+ :where
199
+ [?e :db/ident ?a]
200
+ [?e :db/valueType ?t]
201
+ [?e :db/cardinality ?c]]]
202
+ (is (= #{[:name :db.type/string :db.cardinality/one]
203
+ [:age :db.type/long :db.cardinality/one]
204
+ [:active :db.type/boolean :db.cardinality/one]
205
+ [:email :db.type/string :db.cardinality/one]}
206
+ (d/q schema-query (d/db conn)))))
207
+
208
+ ;; Verify both records exist
209
+ (is (= #{["Diana"] ["Erik"]}
210
+ (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))
211
+ (is (= #{["erik@example.com"]}
212
+ (d/q '[:find ?e :where [_ :email ?e]] (d/db conn)))))
213
+
214
+ (d/release conn))))
215
+
216
+ (deftest test-cardinality-many-inference
217
+ (testing "Inferring cardinality/many from vector values"
218
+ (let [cfg {:store {:backend :memory
219
+ :id #uuid "50000000-0000-0000-0000-000000000005"}}
220
+ _ (d/delete-database cfg)
221
+ _ (d/create-database cfg)
222
+ conn (d/connect cfg)
223
+ ;; First set up the schema for name with unique identity
224
+ _ (d/transact conn [{:db/ident :name
225
+ :db/valueType :db.type/string
226
+ :db/cardinality :db.cardinality/one
227
+ :db/unique :db.unique/identity}])
228
+
229
+ data {:name "Fiona"
230
+ :tags ["clojure" "datalog" "databases"]} ;; Vector = cardinality many
231
+ ]
232
+
233
+ ;; This should automatically set up the tags attribute as cardinality/many
234
+ (du/transact-unstructured conn data)
235
+
236
+ ;; Verify the schema for tags with cardinality/many
237
+ (let [schema-query '[:find ?t ?c
238
+ :where
239
+ [?e :db/ident :tags]
240
+ [?e :db/valueType ?t]
241
+ [?e :db/cardinality ?c]]]
242
+ (is (= #{[:db.type/string :db.cardinality/many]} ;; Check cardinality/many
243
+ (d/q schema-query (d/db conn)))))
244
+
245
+ ;; Verify all array values were stored
246
+ (let [tags (d/q '[:find ?t :where [_ :tags ?t]] (d/db conn))]
247
+ (is (= #{"clojure" "datalog" "databases"}
248
+ (set (map first tags)))))
249
+
250
+ ;; Test adding more values to the array for the same entity
251
+ (d/transact conn [{:db/id [:name "Fiona"]
252
+ :tags ["datahike"]}]) ;; Add another tag
253
+
254
+ ;; Verify all tags are preserved (cardinality/many behavior)
255
+ (let [tags (d/q '[:find ?t :where [_ :tags ?t]] (d/db conn))]
256
+ (is (= #{"clojure" "datalog" "databases" "datahike"}
257
+ (set (map first tags)))))
258
+
259
+ (d/release conn))))
260
+
261
+ (deftest test-schema-conflict
262
+ (testing "Detecting schema conflicts"
263
+ (let [cfg {:store {:backend :memory
264
+ :id #uuid "60000000-0000-0000-0000-000000000006"}}
265
+ _ (d/delete-database cfg)
266
+ _ (d/create-database cfg)
267
+ conn (d/connect cfg)]
268
+
269
+ ;; First add schema for 'score' as a long
270
+ (d/transact conn [{:db/ident :score
271
+ :db/valueType :db.type/long
272
+ :db/cardinality :db.cardinality/one}])
273
+
274
+ ;; Add a data point with correct type
275
+ (d/transact conn [{:score 100}])
276
+
277
+ ;; Now try to add data with incompatible type (string instead of long)
278
+ (let [data {:name "Grace"
279
+ :score "High"}] ;; Score is a string but schema says long
280
+
281
+ ;; This should detect the conflict and throw an exception
282
+ (is (thrown-with-msg? Exception #"Schema conflict detected"
283
+ (du/transact-unstructured conn data))))
284
+
285
+ ;; Verify the data was not inserted
286
+ (is (empty? (d/q '[:find ?n :where [_ :name ?n]] (d/db conn))))
287
+
288
+ ;; Verify the original score still works
289
+ (is (= #{[100]} (d/q '[:find ?s :where [_ :score ?s]] (d/db conn))))
290
+
291
+ (d/release conn))))
@@ -0,0 +1,205 @@
1
+ (ns datahike.test.upsert-impl-test
2
+ (:require
3
+ #?(:cljs [cljs.test :as t :refer-macros [is deftest testing]]
4
+ :clj [clojure.test :as t :refer [is deftest testing]])
5
+ [hitchhiker.tree.utils.async :as ha]
6
+ [hitchhiker.tree :as tree]
7
+ [hitchhiker.tree.messaging :as msg]
8
+ [datahike.index.hitchhiker-tree.upsert :as htu]
9
+ [datahike.test.utils :refer [setup-db conn-id]]
10
+ [datahike.constants :as const]
11
+ [datahike.db :as db]
12
+ [datahike.api :as d]))
13
+
14
+ (defn upsert-helper
15
+ [t k]
16
+ (ha/<?? (msg/enqueue t [(htu/new-UpsertOp k 0 [0 1])])))
17
+
18
+ (deftest hh-tree-upsert
19
+ (let [new-tree (tree/b-tree (tree/->Config 3 3 2))
20
+ projected-vec [4 :name "Marcel" 1]
21
+ tree (reduce upsert-helper (ha/<?? new-tree)
22
+ (into (sorted-set)
23
+ #{[1 :at "home" 1]
24
+ [1 :age 44 1]
25
+ [1 :name "Petr" 1]
26
+ [2 :age 25 1]
27
+ [2 :name "Ivan" 1]
28
+ [3 :age 11 1]
29
+ projected-vec}))]
30
+ (testing "Against an entry in the projection area,"
31
+ (testing "we are in a projection"
32
+ (is (= (:key (first (:op-buf tree))) projected-vec)))
33
+ (testing "basic lookup works"
34
+ (is (= [[1 :age 44 1] nil] (first (msg/lookup-fwd-iter tree [1 :age 44 1])))))
35
+ (testing "a totally new entry is persisted"
36
+ (let [new [5 :name "Jo" 3]
37
+ tree-after (upsert-helper tree new)]
38
+ ;; Is new in?
39
+ (is (= [new nil] (first (msg/lookup-fwd-iter tree-after new))))))
40
+ (testing "new = old"
41
+ (let [new [4 :name "Marcel" 2]
42
+ tree-after (upsert-helper tree new)]
43
+ (is (= [new nil] (first (msg/lookup-fwd-iter tree-after new))))))
44
+ (testing "old v <> new v,"
45
+ (testing "not keeping history"
46
+ (let [new [4 :name "New-Name" 2]
47
+ tree-after (upsert-helper tree new)]
48
+ ;; Is new in?
49
+ (is (= [new nil] (first (msg/lookup-fwd-iter tree-after new))))
50
+ ;; Is old deleted?
51
+ (is (= nil (msg/lookup tree-after projected-vec)))))))
52
+
53
+ (testing "Against an entry located at leaf level,"
54
+ (testing "basic lookup works"
55
+ (is (= [[1 :age 44 1] nil] (first (msg/lookup-fwd-iter tree [1 :age 44 1])))))
56
+ (testing "re-inserting works"
57
+ (let [new [1 :age 44 1]
58
+ tree-after (upsert-helper tree new)]
59
+ (is (= [new nil] (first (msg/lookup-fwd-iter tree-after new))))))
60
+ (testing "old v <> new v"
61
+ (testing "not keeping history"
62
+ (let [new [1 :age 40 5]
63
+ tree-after (upsert-helper tree new)]
64
+ ;; Is new in?
65
+ (is (= [new nil] (first (msg/lookup-fwd-iter tree-after new))))
66
+ ;; Is old deleted?
67
+ (is (= nil (msg/lookup tree-after [1 :age 44 1]))))))))
68
+
69
+ (testing "when it overflows"
70
+ ;; I.e. testing the projection of a deferred-op sitting on an index-node.
71
+ (let [new-tree (tree/b-tree (tree/->Config 3 3 2))
72
+ tree (reduce upsert-helper (ha/<?? new-tree)
73
+ (into (sorted-set)
74
+ #{[1 :age 44 1]
75
+ [1 :name "Petr" 1]
76
+ [2 :age 25 1]
77
+ [2 :name "Ivan" 1]
78
+ [3 :age 11 1]
79
+ [4 :age 12 1]
80
+ [4 :age 20 1]
81
+ [4 :name "Paulo" 1]
82
+ [4 :age 40 1]}))] ;; triggers the overflow
83
+
84
+ (is (= nil (msg/lookup tree [4 :age 12 1])))
85
+ (is (= nil (msg/lookup tree [4 :age 20 1])))
86
+ (is (= [[1 :age 44 1] nil] (first (msg/lookup-fwd-iter tree [1 :age 44 1])))))))
87
+
88
+ (defn connect []
89
+ (let [cfg {:schema-flexibility :read
90
+ :store {:backend :memory :id (random-uuid)}
91
+ :initial-tx []}
92
+ _ (d/delete-database cfg)
93
+ _ (d/create-database cfg)]
94
+ (d/connect cfg)))
95
+
96
+ (deftest datahike-upsert
97
+ (testing "IndexNode"
98
+ (let [txs (vec (for [i (range 1000)]
99
+ {:name (str "Peter" i)
100
+ :age i}))
101
+ conn (connect)]
102
+ (is (d/transact conn txs))
103
+ (d/release conn)))
104
+
105
+ (testing "simple upsert and history"
106
+ (let [txs [[:db/add 199 :name "Peter"]
107
+ [:db/add 199 :name "Ptr"]]
108
+ conn (connect)]
109
+ (is (d/transact conn txs))
110
+ (is (not (d/datoms @conn :eavt 199 :name "Peter"))) ;; no history
111
+ (is (d/datoms (d/history @conn) :eavt 199 :name "Peter"))
112
+ (d/release conn)))
113
+
114
+ (testing "transacting the same datoms twice should work with :avet"
115
+ (let [dvec #(vector (:e %) (:a %) (:v %))
116
+ txs [[:db/add 1 :age 44]
117
+ [:db/add 2 :age 25]
118
+ [:db/add 3 :age 11]]
119
+ db (-> (db/empty-db {:age {:db/index true}})
120
+ (d/db-with txs)
121
+ ;; transacting again
122
+ (d/db-with txs))]
123
+ (is (= [[3 :age 11]
124
+ [2 :age 25]
125
+ [1 :age 44]]
126
+ (map dvec (d/datoms db :avet)))))))
127
+
128
+ (deftest temporal-upsert
129
+ (let [schema [{:db/ident :name
130
+ :db/valueType :db.type/string
131
+ :db/unique :db.unique/identity
132
+ :db/index true
133
+ :db/cardinality :db.cardinality/one}
134
+ {:db/ident :age
135
+ :db/valueType :db.type/long
136
+ :db/cardinality :db.cardinality/one}]
137
+ initial-tx (conj schema
138
+ {:name "Alice"
139
+ :age 25}
140
+ {:name "Bob"
141
+ :age 35})
142
+ cfg {:store {:backend :memory
143
+ :id #uuid "70000000-0000-0000-0000-000000000007"}
144
+ :schema-flexibility :read
145
+ :initial-tx initial-tx}
146
+ conn (setup-db cfg)
147
+ query '[:find ?a ?t ?op
148
+ :where
149
+ [?e :name "Alice"]
150
+ [?e :age ?a ?t ?op]]]
151
+ (testing "when multiple transactions"
152
+ (d/transact conn [[:db/add [:name "Alice"] :age 20]
153
+ [:db/add [:name "Bob"] :age 15]])
154
+ (d/transact conn [[:db/add [:name "Alice"] :age 10]])
155
+ (d/transact conn [[:db/add [:name "Alice"] :age 1]])
156
+ (is (= #{[25 (+ const/tx0 1) true]
157
+ [25 (+ const/tx0 2) false]
158
+ [20 (+ const/tx0 2) true]
159
+ [20 (+ const/tx0 3) false]
160
+ [10 (+ const/tx0 3) true]
161
+ [10 (+ const/tx0 4) false]
162
+ [1 (+ const/tx0 4) true]}
163
+ (d/q query (d/history @conn))))
164
+ (d/release conn))
165
+ (testing "when only one transaction"
166
+ (let [conn (setup-db cfg)]
167
+ (d/transact conn [[:db/add [:name "Alice"] :age 20]
168
+ [:db/add [:name "Bob"] :age 15]
169
+ [:db/add [:name "Alice"] :age 10]
170
+ [:db/add [:name "Alice"] :age 1]])
171
+ (is (= #{[25 (+ const/tx0 1) true]
172
+ [25 (+ const/tx0 2) false]
173
+ [20 (+ const/tx0 2) true]
174
+ [20 (+ const/tx0 2) false]
175
+ [10 (+ const/tx0 2) true]
176
+ [10 (+ const/tx0 2) false]
177
+ [1 (+ const/tx0 2) true]}
178
+ (d/q query (d/history @conn))))
179
+ (d/release conn)))))
180
+
181
+ (deftest upsert-read-handlers
182
+ (let [config {:store {:backend :file :path "/tmp/upsert-read-handlers" :id #uuid "ab1d0000-0000-0000-0000-000000000001"}
183
+ :schema-flexibility :write
184
+ :keep-history? false}
185
+ schema [{:db/ident :block/string
186
+ :db/valueType :db.type/string
187
+ :db/cardinality :db.cardinality/one}
188
+ {:db/ident :block/children
189
+ :db/valueType :db.type/ref
190
+ :db/index true
191
+ :db/cardinality :db.cardinality/one}]
192
+ _ (d/delete-database config)
193
+ _ (d/create-database config)
194
+ conn (d/connect config)]
195
+ (d/transact conn schema)
196
+ (d/transact conn (vec (for [i (range 1000)]
197
+ {:db/id (inc i) :block/children (inc i)})))
198
+ (d/release conn)
199
+
200
+ (let [conn (d/connect config)]
201
+ ;; Would fail if upsert read handlers are not present
202
+ (is (d/datoms @conn :eavt))
203
+ (d/release conn))
204
+
205
+ (d/delete-database config)))