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,299 @@
1
+ """Tests for EDN conversion rules.
2
+
3
+ Tests the universal Python → EDN conversion that follows the rules:
4
+ - Keys: always keywordized
5
+ - Values: : prefix = keyword, else literal
6
+ - Escape: \\: for literal colon strings
7
+ """
8
+ import pytest
9
+ from datahike.database import _key_to_keyword, _value_to_edn, _dict_to_edn, _python_to_edn
10
+ from datahike import edn, kw
11
+
12
+
13
+ class TestKeyConversion:
14
+ """Test key → keyword conversion."""
15
+
16
+ def test_simple_key(self):
17
+ assert _key_to_keyword("name") == ":name"
18
+
19
+ def test_namespaced_key(self):
20
+ assert _key_to_keyword("person/name") == ":person/name"
21
+
22
+ def test_key_with_leading_colon(self):
23
+ # Leading : is stripped (convenience)
24
+ assert _key_to_keyword(":db/id") == ":db/id"
25
+
26
+ def test_key_with_question_mark(self):
27
+ assert _key_to_keyword("keep-history?") == ":keep-history?"
28
+
29
+ def test_key_with_dash(self):
30
+ assert _key_to_keyword("schema-flexibility") == ":schema-flexibility"
31
+
32
+
33
+ class TestValueConversion:
34
+ """Test value conversion rules."""
35
+
36
+ def test_string_without_colon(self):
37
+ assert _value_to_edn("Alice") == '"Alice"'
38
+
39
+ def test_string_with_colon_prefix(self):
40
+ # : prefix → keyword
41
+ assert _value_to_edn(":active") == ":active"
42
+
43
+ def test_string_with_escaped_colon(self):
44
+ # \\: → literal string with :
45
+ assert _value_to_edn("\\:literal") == '":literal"'
46
+
47
+ def test_string_with_quotes(self):
48
+ # Quotes should be escaped
49
+ assert _value_to_edn('say "hello"') == '"say \\"hello\\""'
50
+
51
+ def test_integer(self):
52
+ assert _value_to_edn(42) == "42"
53
+
54
+ def test_float(self):
55
+ assert _value_to_edn(3.14) == "3.14"
56
+
57
+ def test_boolean_true(self):
58
+ assert _value_to_edn(True) == "true"
59
+
60
+ def test_boolean_false(self):
61
+ assert _value_to_edn(False) == "false"
62
+
63
+ def test_none(self):
64
+ assert _value_to_edn(None) == "nil"
65
+
66
+ def test_list(self):
67
+ assert _value_to_edn([1, 2, 3]) == "[1 2 3]"
68
+
69
+ def test_nested_list(self):
70
+ assert _value_to_edn([1, [2, 3]]) == "[1 [2 3]]"
71
+
72
+ def test_nested_dict(self):
73
+ result = _value_to_edn({"a": 1})
74
+ assert result == "{:a 1}"
75
+
76
+
77
+ class TestDictConversion:
78
+ """Test dict → EDN map conversion."""
79
+
80
+ def test_simple_dict(self):
81
+ assert _dict_to_edn({"name": "Alice"}) == '{:name "Alice"}'
82
+
83
+ def test_dict_with_keyword_value(self):
84
+ assert _dict_to_edn({"status": ":active"}) == '{:status :active}'
85
+
86
+ def test_dict_with_multiple_keys(self):
87
+ result = _dict_to_edn({"name": "Alice", "age": 30})
88
+ # Dict order might vary, check both possibilities
89
+ assert result in [
90
+ '{:name "Alice" :age 30}',
91
+ '{:age 30 :name "Alice"}'
92
+ ]
93
+
94
+ def test_nested_dict(self):
95
+ result = _dict_to_edn({
96
+ "store": {
97
+ "backend": ":mem",
98
+ "id": "test"
99
+ }
100
+ })
101
+ assert ":store" in result
102
+ assert ":backend :mem" in result
103
+ assert ':id "test"' in result
104
+
105
+
106
+ class TestCompleteConversion:
107
+ """Test complete Python → EDN conversion."""
108
+
109
+ def test_config_conversion(self):
110
+ config = {
111
+ "store": {
112
+ "backend": ":mem",
113
+ "id": "test"
114
+ },
115
+ "schema-flexibility": ":read",
116
+ "keep-history?": True
117
+ }
118
+ edn = _python_to_edn(config)
119
+
120
+ assert ":store" in edn
121
+ assert ":backend :mem" in edn
122
+ assert ':id "test"' in edn
123
+ assert ":schema-flexibility :read" in edn
124
+ assert ":keep-history? true" in edn
125
+
126
+ def test_transaction_data(self):
127
+ data = [
128
+ {"name": "Alice", "status": ":active"},
129
+ {"name": "Bob", "status": ":inactive"}
130
+ ]
131
+ edn = _python_to_edn(data)
132
+
133
+ assert ':name "Alice"' in edn
134
+ assert ":status :active" in edn
135
+ assert ':name "Bob"' in edn
136
+ assert ":status :inactive" in edn
137
+
138
+ def test_schema_transaction(self):
139
+ schema = [{
140
+ "db/ident": ":person/name",
141
+ "db/valueType": ":db.type/string",
142
+ "db/cardinality": ":db.cardinality/one"
143
+ }]
144
+ edn = _python_to_edn(schema)
145
+
146
+ assert ":db/ident :person/name" in edn
147
+ assert ":db/valueType :db.type/string" in edn
148
+ assert ":db/cardinality :db.cardinality/one" in edn
149
+
150
+
151
+ class TestEDNHelpers:
152
+ """Test explicit EDN type helpers."""
153
+
154
+ def test_keyword_simple(self):
155
+ kw = edn.keyword("name")
156
+ assert kw.to_edn() == ":name"
157
+
158
+ def test_keyword_namespaced(self):
159
+ kw = edn.keyword("name", "person")
160
+ assert kw.to_edn() == ":person/name"
161
+
162
+ def test_symbol(self):
163
+ sym = edn.symbol("my-fn")
164
+ assert sym.to_edn() == "my-fn"
165
+
166
+ def test_symbol_namespaced(self):
167
+ sym = edn.symbol("assoc", "clojure.core")
168
+ assert sym.to_edn() == "clojure.core/assoc"
169
+
170
+ def test_uuid(self):
171
+ u = edn.uuid("550e8400-e29b-41d4-a716-446655440000")
172
+ assert u.to_edn() == '#uuid "550e8400-e29b-41d4-a716-446655440000"'
173
+
174
+ def test_inst(self):
175
+ i = edn.inst("2024-01-01T00:00:00Z")
176
+ assert i.to_edn() == '#inst "2024-01-01T00:00:00Z"'
177
+
178
+ def test_forced_string(self):
179
+ s = edn.string(":literal")
180
+ assert s.startswith("<STRING>")
181
+ # When converted to EDN, should be quoted string
182
+ assert _value_to_edn(s) == '":literal"'
183
+
184
+ def test_keyword_in_dict(self):
185
+ data = {
186
+ "db/ident": edn.keyword("person/name"),
187
+ "db/valueType": kw.STRING
188
+ }
189
+ result = _python_to_edn(data)
190
+
191
+ assert ":db/ident :person/name" in result
192
+ assert ":db/valueType :db.type/string" in result
193
+
194
+
195
+ class TestKeywordConstants:
196
+ """Test pre-defined keyword constants."""
197
+
198
+ def test_db_id(self):
199
+ assert kw.DB_ID == ":db/id"
200
+
201
+ def test_db_ident(self):
202
+ assert kw.DB_IDENT == ":db/ident"
203
+
204
+ def test_value_types(self):
205
+ assert kw.STRING == ":db.type/string"
206
+ assert kw.LONG == ":db.type/long"
207
+ assert kw.BOOLEAN == ":db.type/boolean"
208
+ assert kw.REF == ":db.type/ref"
209
+
210
+ def test_cardinality(self):
211
+ assert kw.ONE == ":db.cardinality/one"
212
+ assert kw.MANY == ":db.cardinality/many"
213
+
214
+ def test_unique(self):
215
+ assert kw.UNIQUE_VALUE == ":db.unique/value"
216
+ assert kw.UNIQUE_IDENTITY == ":db.unique/identity"
217
+
218
+ def test_schema_flexibility(self):
219
+ assert kw.SCHEMA_READ == ":read"
220
+ assert kw.SCHEMA_WRITE == ":write"
221
+
222
+
223
+ class TestEdgeCases:
224
+ """Test edge cases and special scenarios."""
225
+
226
+ def test_empty_dict(self):
227
+ assert _dict_to_edn({}) == "{}"
228
+
229
+ def test_empty_list(self):
230
+ assert _value_to_edn([]) == "[]"
231
+
232
+ def test_deeply_nested(self):
233
+ data = {
234
+ "a": {
235
+ "b": {
236
+ "c": {
237
+ "d": "value"
238
+ }
239
+ }
240
+ }
241
+ }
242
+ edn = _python_to_edn(data)
243
+ assert ":a" in edn
244
+ assert ":b" in edn
245
+ assert ":c" in edn
246
+ assert ":d" in edn
247
+ assert '"value"' in edn
248
+
249
+ def test_list_of_dicts(self):
250
+ data = [
251
+ {"name": "Alice"},
252
+ {"name": "Bob"}
253
+ ]
254
+ edn = _python_to_edn(data)
255
+ assert '[' in edn
256
+ assert '{:name "Alice"}' in edn
257
+ assert '{:name "Bob"}' in edn
258
+
259
+ def test_dict_with_list_value(self):
260
+ data = {"tags": [":important", ":verified"]}
261
+ edn = _python_to_edn(data)
262
+ assert ":tags" in edn
263
+ assert ":important" in edn
264
+ assert ":verified" in edn
265
+
266
+ def test_string_with_slash(self):
267
+ # Slash in regular string should be preserved
268
+ result = _value_to_edn("path/to/file")
269
+ assert result == '"path/to/file"'
270
+
271
+ def test_string_with_backslash(self):
272
+ # Backslash should be escaped
273
+ result = _value_to_edn("C:\\path\\to\\file")
274
+ assert result == '"C:\\\\path\\\\to\\\\file"'
275
+
276
+
277
+ class TestCustomBackendScenario:
278
+ """Test conversion for custom backend configurations."""
279
+
280
+ def test_custom_s3_backend(self):
281
+ config = {
282
+ "store": {
283
+ "backend": ":my-s3",
284
+ "bucket": "my-bucket",
285
+ "region": "us-west-2",
286
+ "encryption": {
287
+ "type": ":aes256",
288
+ "key-id": "secret"
289
+ }
290
+ }
291
+ }
292
+ edn = _python_to_edn(config)
293
+
294
+ assert ":backend :my-s3" in edn
295
+ assert ':bucket "my-bucket"' in edn
296
+ assert ':region "us-west-2"' in edn
297
+ assert ":encryption" in edn
298
+ assert ":type :aes256" in edn
299
+ assert ':key-id "secret"' in edn
@@ -0,0 +1,99 @@
1
+ """Tests for query and data retrieval operations."""
2
+ import json
3
+ import pytest
4
+ from datahike import (
5
+ create_database,
6
+ delete_database,
7
+ transact,
8
+ q,
9
+ pull,
10
+ database,
11
+ )
12
+
13
+
14
+ def test_transact_and_query_json(mem_config_flexible):
15
+ """Test transact and query with JSON format."""
16
+ with database(mem_config_flexible) as cfg:
17
+ # Transact data using JSON format
18
+ tx_data = json.dumps([
19
+ {"name": "Alice", "age": 30},
20
+ {"name": "Bob", "age": 25}
21
+ ])
22
+ result = transact(cfg, tx_data, input_format='json')
23
+ assert result is not None
24
+
25
+ # Query for Alice
26
+ query_result = q(
27
+ '[:find ?e ?name :where [?e :name ?name] [?e :name "Alice"]]',
28
+ [('db', cfg)],
29
+ output_format='json'
30
+ )
31
+ assert len(query_result) > 0
32
+
33
+
34
+ def test_pull_entity(mem_config_flexible):
35
+ """Test pull API to retrieve entity."""
36
+ with database(mem_config_flexible) as cfg:
37
+ # Transact data
38
+ tx_data = json.dumps([{"name": "Alice", "age": 30}])
39
+ transact(cfg, tx_data, input_format='json')
40
+
41
+ # Query to get entity ID
42
+ query_result = q(
43
+ '[:find ?e ?name :where [?e :name ?name] [?e :name "Alice"]]',
44
+ [('db', cfg)],
45
+ output_format='json'
46
+ )
47
+
48
+ # Extract entity ID from result
49
+ actual_results = query_result[1] if query_result[0] == '!set' else query_result
50
+ entity_id = actual_results[0][0]
51
+
52
+ # Pull entity
53
+ entity = pull(cfg, '[*]', entity_id, output_format='json')
54
+ assert entity.get('name') == 'Alice' or entity.get(':name') == 'Alice'
55
+
56
+
57
+ def test_query_with_multiple_results(mem_config_flexible):
58
+ """Test query returning multiple results."""
59
+ with database(mem_config_flexible) as cfg:
60
+ # Transact multiple entities
61
+ tx_data = json.dumps([
62
+ {"name": "Alice", "age": 30},
63
+ {"name": "Bob", "age": 25},
64
+ {"name": "Charlie", "age": 35}
65
+ ])
66
+ transact(cfg, tx_data, input_format='json')
67
+
68
+ # Query all names
69
+ query_result = q(
70
+ '[:find ?name :where [?e :name ?name]]',
71
+ [('db', cfg)],
72
+ output_format='json'
73
+ )
74
+
75
+ # Should have 3 results
76
+ actual_results = query_result[1] if query_result[0] == '!set' else query_result
77
+ assert len(actual_results) == 3
78
+
79
+
80
+ def test_query_with_parameters(mem_config_flexible):
81
+ """Test query with input parameters."""
82
+ with database(mem_config_flexible) as cfg:
83
+ # Transact data
84
+ tx_data = json.dumps([
85
+ {"name": "Alice", "age": 30},
86
+ {"name": "Bob", "age": 25}
87
+ ])
88
+ transact(cfg, tx_data, input_format='json')
89
+
90
+ # Query with parameter
91
+ query_result = q(
92
+ '[:find ?e :in $ ?name :where [?e :name ?name]]',
93
+ [('db', cfg), ('param', '"Alice"')],
94
+ output_format='json'
95
+ )
96
+
97
+ # Should find Alice
98
+ actual_results = query_result[1] if query_result[0] == '!set' else query_result
99
+ assert len(actual_results) > 0
@@ -0,0 +1,55 @@
1
+ """Tests for schema operations."""
2
+ import pytest
3
+ from datahike import (
4
+ transact,
5
+ schema,
6
+ q,
7
+ database,
8
+ )
9
+
10
+
11
+ def test_explicit_schema(mem_config):
12
+ """Test transact and query with explicit schema."""
13
+ with database(mem_config) as cfg:
14
+ # Transact schema using EDN format
15
+ schema_tx = '''[
16
+ {:db/ident :name
17
+ :db/valueType :db.type/string
18
+ :db/cardinality :db.cardinality/one}
19
+ {:db/ident :age
20
+ :db/valueType :db.type/long
21
+ :db/cardinality :db.cardinality/one}
22
+ ]'''
23
+ transact(cfg, schema_tx, input_format='edn')
24
+
25
+ # Check schema was added
26
+ db_schema = schema(cfg, output_format='json')
27
+ assert db_schema is not None
28
+
29
+ # Transact data using EDN
30
+ tx_data = '[{:name "Charlie" :age 35} {:name "Diana" :age 28}]'
31
+ transact(cfg, tx_data, input_format='edn')
32
+
33
+ # Query
34
+ query_result = q(
35
+ '[:find ?e ?name ?age :where [?e :name ?name] [?e :age ?age]]',
36
+ [('db', cfg)],
37
+ output_format='json'
38
+ )
39
+ actual_results = query_result[1] if query_result[0] == '!set' else query_result
40
+ assert len(actual_results) == 2
41
+
42
+
43
+ def test_schema_flexibility_read(mem_config_flexible):
44
+ """Test schema-flexibility :read allows dynamic attributes."""
45
+ with database(mem_config_flexible) as cfg:
46
+ # Transact data without predefined schema
47
+ tx_data = '[{:dynamic-attr "test" :another-attr 42}]'
48
+ transact(cfg, tx_data, input_format='edn')
49
+
50
+ # Query should work
51
+ query_result = q(
52
+ '[:find ?e ?val :where [?e :dynamic-attr ?val]]',
53
+ [('db', cfg)]
54
+ )
55
+ assert len(query_result) > 0
@@ -0,0 +1,5 @@
1
+ ;; clj-kondo export configuration for Datahike
2
+ ;; AUTO-GENERATED from api-specification
3
+ ;; To regenerate: bb codegen-clj-kondo
4
+
5
+ {:var-definitions {datahike.api {unlisten {:arglists (quote ([arg0 arg1])), :doc "Removes registered listener from connection."}, delete-database {:arglists (quote ([arg0] [])), :doc "Deletes a database given via configuration map."}, pull {:arglists (quote ([arg0 arg1] [arg0 arg1 arg2])), :doc "Fetches data using recursive declarative pull pattern."}, entity {:arglists (quote ([arg0 arg1])), :doc "Retrieves an entity by its id. Returns lazy map-like structure."}, metrics {:arglists (quote ([arg0])), :doc "Returns database metrics (datom counts, index sizes, etc)."}, reverse-schema {:arglists (quote ([arg0])), :doc "Returns reverse schema definition (attribute id to ident mapping)."}, datoms {:arglists (quote ([arg0 arg1] [arg0 arg1 arg2])), :doc "Index lookup. Returns sequence of datoms matching index components."}, load-entities {:arglists (quote ([arg0 arg1])), :doc "Load entities directly (bulk load)."}, is-filtered {:arglists (quote ([arg0])), :doc "Returns true if database was filtered using filter, false otherwise."}, q {:arglists (quote ([arg0] [arg0 arg1])), :doc "Executes a datalog query."}, schema {:arglists (quote ([arg0])), :doc "Returns current schema definition."}, index-range {:arglists (quote ([arg0 arg1])), :doc "Returns part of :avet index between start and end values."}, pull-many {:arglists (quote ([arg0 arg1] [arg0 arg1 arg2])), :doc "Same as pull, but accepts sequence of ids and returns sequence of maps."}, since {:arglists (quote ([arg0 arg1])), :doc "Returns database state since given time point (Date or transaction ID). Contains only datoms added since that point."}, query-stats {:arglists (quote ([arg0] [arg0 arg1])), :doc "Executes query and returns execution statistics."}, entity-db {:arglists (quote ([arg0])), :doc "Returns database that entity was created from."}, gc-storage {:arglists (quote ([arg0 arg1] [arg0])), :doc "Invokes garbage collection on connection's store. Removes old snapshots before given time point."}, create-database {:arglists (quote ([arg0] [])), :doc "Creates a database via configuration map."}, db {:arglists (quote ([arg0])), :doc "Returns the underlying immutable database value from a connection. Prefer using @conn directly."}, connect {:arglists (quote ([arg0] [arg0 arg1] [])), :doc "Connects to a Datahike database via configuration map."}, release {:arglists (quote ([arg0])), :doc "Releases a database connection."}, history {:arglists (quote ([arg0])), :doc "Returns full historical state of database including all assertions and retractions."}, transact! {:arglists (quote ([arg0 arg1])), :doc "Same as transact, but asynchronously returns a future."}, database-exists? {:arglists (quote ([arg0] [])), :doc "Checks if a database exists via configuration map."}, transact {:arglists (quote ([arg0 arg1])), :doc "Applies transaction to the database and updates connection."}, as-of {:arglists (quote ([arg0 arg1])), :doc "Returns database state at given time point (Date or transaction ID)."}, seek-datoms {:arglists (quote ([arg0 arg1] [arg0 arg1 arg2])), :doc "Like datoms, but returns datoms starting from specified components through end of index."}, filter {:arglists (quote ([arg0 arg1])), :doc "Returns filtered view over database. Only includes datoms where (pred db datom) is true."}, listen {:arglists (quote ([arg0 arg1] [arg0 arg1 arg2])), :doc "Listen for changes on connection. Callback called with transaction report on each transact."}, db-with {:arglists (quote ([arg0 arg1])), :doc "Applies transaction to immutable db value, returns new db. Same as (:db-after (with db tx-data))."}, with {:arglists (quote ([arg0 arg1] [arg0 arg1] [arg0 arg1 arg2])), :doc "Applies transaction to immutable db value. Returns transaction report."}, tempid {:arglists (quote ([arg0] [arg0 arg1])), :doc "Allocates temporary id (negative integer). Prefer using negative integers directly."}}}, :linters {:type-mismatch {:namespaces {datahike.api {unlisten {:arities {2 {:args [:any :any], :ret :any}}}, delete-database {:arities {1 {:args [:any], :ret :any}, 0 {:args [], :ret :any}}}, pull {:arities {2 {:args [:any :any], :ret :any}, 3 {:args [:any :vector :any], :ret :any}}}, entity {:arities {2 {:args [:any :any], :ret :any}}}, metrics {:arities {1 {:args [:any], :ret :any}}}, reverse-schema {:arities {1 {:args [:any], :ret :any}}}, datoms {:arities {2 {:args [:any :any], :ret :any}, 3 {:args [:any :keyword :any], :ret :any}}}, load-entities {:arities {2 {:args [:any :any], :ret :any}}}, is-filtered {:arities {1 {:args [:any], :ret :boolean}}}, q {:arities {1 {:args [:any], :ret :any}, 2 {:args [:any :any], :ret :any}}}, schema {:arities {1 {:args [:any], :ret :any}}}, index-range {:arities {2 {:args [:any :any], :ret :any}}}, pull-many {:arities {2 {:args [:any :any], :ret :seqable}, 3 {:args [:any :vector :any], :ret :seqable}}}, since {:arities {2 {:args [:any :any], :ret :any}}}, query-stats {:arities {1 {:args [:any], :ret :any}, 2 {:args [:any :any], :ret :any}}}, entity-db {:arities {1 {:args [:any], :ret :any}}}, gc-storage {:arities {2 {:args [:any :any], :ret :any}, 1 {:args [:any], :ret :any}}}, create-database {:arities {1 {:args [:any], :ret :any}, 0 {:args [], :ret :any}}}, db {:arities {1 {:args [:any], :ret :any}}}, connect {:arities {1 {:args [:any], :ret :any}, 2 {:args [:any :any], :ret :any}, 0 {:args [], :ret :any}}}, release {:arities {1 {:args [:any], :ret :nil}}}, history {:arities {1 {:args [:any], :ret :any}}}, transact! {:arities {2 {:args [:any :any], :ret :any}}}, database-exists? {:arities {1 {:args [:any], :ret :boolean}, 0 {:args [], :ret :boolean}}}, transact {:arities {2 {:args [:any :any], :ret :any}}}, as-of {:arities {2 {:args [:any :any], :ret :any}}}, seek-datoms {:arities {2 {:args [:any :any], :ret :any}, 3 {:args [:any :keyword :any], :ret :any}}}, filter {:arities {2 {:args [:any :any], :ret :any}}}, listen {:arities {2 {:args [:any :any], :ret :any}, 3 {:args [:any :any :any], :ret :any}}}, db-with {:arities {2 {:args [:any :any], :ret :any}}}, with {:arities {2 {:args [:any :any], :ret :any}, 3 {:args [:any :any :any], :ret :any}}}, tempid {:arities {1 {:args [:any], :ret :any}, 2 {:args [:any :int], :ret :int}}}}}}}}
@@ -0,0 +1,4 @@
1
+ {:port 4444
2
+ :level :debug
3
+ :dev-mode true
4
+ :token "securerandompassword"}
@@ -0,0 +1,56 @@
1
+ {:deps {:aliases [:cljs :test]}
2
+ :src-paths ["src" "test"]
3
+ :dev-http {8022 "target/browser-integration-test"}
4
+ :builds {:repl
5
+ {:target :node-script
6
+ :output-to "target/out/repl.js"
7
+ :main shadow.cljs.devtools.cli
8
+ :compiler-options {:infer-externs true
9
+ :warnings-as-errors false
10
+ :warnings {:fn-deprecated false
11
+ :protocol-multiple-impls false}}}
12
+ :node-test
13
+ {:target :node-script
14
+ :output-to "target/out/node-test.js"
15
+ :main datahike.test.nodejs-test/-main
16
+ :compiler-options {:optimizations :advanced
17
+ :infer-externs :auto
18
+ :elide-asserts false}}
19
+
20
+ :browser-integration-test
21
+ {:target :browser-test
22
+ :test-dir "target/browser-integration-test"
23
+ :ns-regexp "datahike.kabel.browser-integration-test"
24
+ :compiler-options {:optimizations :advanced
25
+ :infer-externs :auto
26
+ :elide-asserts false
27
+ :warnings-as-errors false
28
+ :warnings {:fn-deprecated false}}}
29
+
30
+ :browser-ci
31
+ {:target :karma
32
+ :output-to "target/browser-ci.js"
33
+ :ns-regexp "datahike.kabel.browser-integration-test"
34
+ :compiler-options {:optimizations :advanced
35
+ :infer-externs :auto
36
+ :elide-asserts false
37
+ :warnings-as-errors false
38
+ :warnings {:fn-deprecated false}}}
39
+
40
+ :browser-release
41
+ {:target :browser
42
+ :output-to "npm-package/browser/datahike.js"
43
+ :output-dir "npm-package/browser"
44
+ :modules {:main {:entries [datahike.js.api]}}
45
+ :compiler-options {:optimizations :advanced
46
+ :infer-externs :auto
47
+ :output-feature-set :es2020}}
48
+
49
+ :npm-release
50
+ {:target :npm-module
51
+ :output-dir "npm-package"
52
+ :entries [datahike.js.api
53
+ konserve.node-filestore]
54
+ :compiler-options {:optimizations :advanced
55
+ :infer-externs :auto
56
+ :output-feature-set :es6}}}}
@@ -0,0 +1,7 @@
1
+ {datahike/Datom datahike.readers/datom-from-reader
2
+ datahike/DB datahike.readers/db-from-reader
3
+ datahike/HistoricalDB datahike.readers/history-from-reader
4
+ datahike/SinceDB datahike.readers/since-from-reader
5
+ datahike/AsOfDB datahike.readers/as-of-from-reader
6
+ datahike/Connection datahike.readers/connection-from-reader
7
+ db/id datahike.readers/tempid}