workers-runtime-sdk 1.6.6__tar.gz → 1.6.7__tar.gz

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 (158) hide show
  1. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/AGENTS.md +1 -1
  2. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/CHANGELOG.md +9 -0
  3. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/PKG-INFO +1 -1
  4. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/pyproject.toml +9 -1
  5. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/_workers_sdk_entropy_import_context.py +4 -8
  6. workers_runtime_sdk-1.6.7/tests/bindings-test/public/data.json +1 -0
  7. workers_runtime_sdk-1.6.7/tests/bindings-test/public/hello.txt +1 -0
  8. workers_runtime_sdk-1.6.7/tests/bindings-test/pyproject.toml +5 -0
  9. workers_runtime_sdk-1.6.7/tests/bindings-test/src/conftest.py +10 -0
  10. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_analytics_engine.py +24 -0
  11. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_assets.py +35 -0
  12. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_d1.py +498 -0
  13. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_do.py +224 -0
  14. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_images.py +41 -0
  15. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_kv.py +320 -0
  16. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_mtls.py +15 -0
  17. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_queue.py +125 -0
  18. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_r2.py +393 -0
  19. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_ratelimit.py +14 -0
  20. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_vectorize.py +11 -0
  21. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_version_metadata.py +17 -0
  22. workers_runtime_sdk-1.6.7/tests/bindings-test/src/test_workflow.py +215 -0
  23. workers_runtime_sdk-1.6.7/tests/bindings-test/src/worker.py +186 -0
  24. workers_runtime_sdk-1.6.7/tests/bindings-test/src/worker_durable_object.py +260 -0
  25. workers_runtime_sdk-1.6.7/tests/bindings-test/src/worker_workflow.py +130 -0
  26. workers_runtime_sdk-1.6.7/tests/bindings-test/wrangler.jsonc +65 -0
  27. workers_runtime_sdk-1.6.7/tests/conftest.py +267 -0
  28. workers_runtime_sdk-1.6.7/tests/test_bindings.py +27 -0
  29. workers_runtime_sdk-1.6.7/tests/test_fastapi.py +44 -0
  30. workers_runtime_sdk-1.6.7/tests/test_in_workerd.py +151 -0
  31. workers_runtime_sdk-1.6.7/tests/test_web_frameworks.py +41 -0
  32. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/pyproject.toml +11 -0
  33. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/_client.py +92 -0
  34. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/conftest.py +10 -0
  35. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/__init__.py +0 -0
  36. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/api_urls.py +13 -0
  37. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/auth_backend.py +77 -0
  38. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/drf_auth.py +24 -0
  39. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/drf_serializers.py +36 -0
  40. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/drf_throttling.py +12 -0
  41. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/drf_views.py +134 -0
  42. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/forms.py +39 -0
  43. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/middleware.py +17 -0
  44. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/templatetags/__init__.py +0 -0
  45. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/templatetags/custom_tags.py +13 -0
  46. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/urls.py +67 -0
  47. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/django_app/views.py +363 -0
  48. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/templates/base.html +5 -0
  49. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/templates/context_test.html +6 -0
  50. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/templates/csrf_form.html +1 -0
  51. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/templates/hello.html +3 -0
  52. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_async_utilities.py +19 -0
  53. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_auth.py +86 -0
  54. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_caching.py +62 -0
  55. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_cors.py +46 -0
  56. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_csrf.py +69 -0
  57. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_auth_permissions.py +47 -0
  58. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_content_negotiation.py +41 -0
  59. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_serializers.py +68 -0
  60. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_throttling.py +16 -0
  61. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_versioning.py +25 -0
  62. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_drf_views.py +73 -0
  63. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_error_handling.py +32 -0
  64. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_file_uploads.py +57 -0
  65. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_forms.py +50 -0
  66. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_middleware.py +27 -0
  67. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_pagination.py +36 -0
  68. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_request_response.py +98 -0
  69. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_sessions.py +95 -0
  70. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_signals.py +33 -0
  71. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_streaming.py +22 -0
  72. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_templates.py +34 -0
  73. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_url_routing.py +69 -0
  74. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/test_views_basic.py +71 -0
  75. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/src/worker.py +206 -0
  76. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/django-async/wrangler.jsonc +6 -0
  77. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/public/data.json +1 -0
  78. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/public/index.html +7 -0
  79. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/public/style.css +5 -0
  80. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/pyproject.toml +9 -0
  81. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/src/_client.py +26 -0
  82. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/src/conftest.py +10 -0
  83. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/src/test_assets.py +65 -0
  84. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/src/worker.py +143 -0
  85. workers_runtime_sdk-1.6.7/tests/web-frameworks-test/fastapi-tests/wrangler.jsonc +11 -0
  86. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi/asgi.wd-test +19 -0
  87. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi/pyproject.toml +5 -0
  88. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi/tests/test_asgi.py +401 -0
  89. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi/worker.py +240 -0
  90. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi/wrangler.jsonc +5 -0
  91. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi-ws-disconnect/asgi-ws-disconnect.wd-test +19 -0
  92. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +5 -0
  93. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi-ws-disconnect/tests/test_ws_disconnect.py +72 -0
  94. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi-ws-disconnect/worker.py +70 -0
  95. workers_runtime_sdk-1.6.7/tests/workerd-test/asgi-ws-disconnect/wrangler.jsonc +5 -0
  96. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object/durable-object.wd-test +28 -0
  97. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object/pyproject.toml +5 -0
  98. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object/worker.py +144 -0
  99. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object/wrangler.jsonc +5 -0
  100. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-abort/durable-object-abort.wd-test +27 -0
  101. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-abort/pyproject.toml +5 -0
  102. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-abort/worker.py +57 -0
  103. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-abort/wrangler.jsonc +5 -0
  104. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-inheritance/durable-object-inheritance.wd-test +40 -0
  105. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-inheritance/pyproject.toml +5 -0
  106. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-inheritance/worker.py +115 -0
  107. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-inheritance/wrangler.jsonc +5 -0
  108. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-websocket/durable-object-websocket.wd-test +39 -0
  109. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-websocket/pyproject.toml +5 -0
  110. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-websocket/tester.js +27 -0
  111. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-websocket/worker.py +43 -0
  112. workers_runtime_sdk-1.6.7/tests/workerd-test/durable-object-websocket/wrangler.jsonc +5 -0
  113. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/entropy-patches.wd-test +18 -0
  114. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/pyproject.toml +18 -0
  115. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_aiohttp_websocket.py +7 -0
  116. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_langsmith.py +8 -0
  117. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_numpy.py +9 -0
  118. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_pydantic.py +8 -0
  119. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_rust_packages.py +10 -0
  120. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/tests/test_ssl_avoidance.py +9 -0
  121. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/worker.py +27 -0
  122. workers_runtime_sdk-1.6.7/tests/workerd-test/entropy-patches/wrangler.jsonc +5 -0
  123. workers_runtime_sdk-1.6.7/tests/workerd-test/python-rpc/pyproject.toml +5 -0
  124. workers_runtime_sdk-1.6.7/tests/workerd-test/python-rpc/python-rpc.wd-test +34 -0
  125. workers_runtime_sdk-1.6.7/tests/workerd-test/python-rpc/worker.js +101 -0
  126. workers_runtime_sdk-1.6.7/tests/workerd-test/python-rpc/worker.py +384 -0
  127. workers_runtime_sdk-1.6.7/tests/workerd-test/python-rpc/wrangler.jsonc +5 -0
  128. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/pyproject.toml +5 -0
  129. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/sdk.wd-test +42 -0
  130. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/server.py +35 -0
  131. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/tests/test_sdk.py +608 -0
  132. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/worker.py +71 -0
  133. workers_runtime_sdk-1.6.7/tests/workerd-test/sdk/wrangler.jsonc +5 -0
  134. workers_runtime_sdk-1.6.7/tests/workerd-test/wsgi/pyproject.toml +5 -0
  135. workers_runtime_sdk-1.6.7/tests/workerd-test/wsgi/worker.py +205 -0
  136. workers_runtime_sdk-1.6.7/tests/workerd-test/wsgi/wrangler.jsonc +5 -0
  137. workers_runtime_sdk-1.6.7/tests/workerd-test/wsgi/wsgi.wd-test +19 -0
  138. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/uv.lock +119 -1
  139. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/.gitignore +0 -0
  140. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/README.md +0 -0
  141. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/_cloudflare_compat_flags.pyi +0 -0
  142. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/_pyodide_entrypoint_helper.pyi +0 -0
  143. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/_workers_sdk_entropy_import_context.pth +0 -0
  144. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/_workers_sdk_entropy_import_context_loader.py +0 -0
  145. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/asgi.py +0 -0
  146. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/__init__.py +0 -0
  147. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/blob.py +0 -0
  148. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/entrypoints.py +0 -0
  149. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/fetch.py +0 -0
  150. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/formdata.py +0 -0
  151. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/py.typed +0 -0
  152. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/request.py +0 -0
  153. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/response.py +0 -0
  154. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/rpc.py +0 -0
  155. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/types.py +0 -0
  156. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/utils.py +0 -0
  157. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/workflows.py +0 -0
  158. {workers_runtime_sdk-1.6.6 → workers_runtime_sdk-1.6.7}/src/workers/wsgi.py +0 -0
@@ -20,4 +20,4 @@ This package provides utilities for:
20
20
  ## Testing
21
21
 
22
22
  - All the tests should be done inside `workerd`, not in the host environment.
23
- - Tests should be implemented in `../cli/tests` directory.
23
+ - Tests should be implemented in this package's `tests/` directory.
@@ -2,6 +2,15 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.6.7 (2026-08-05)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Fix entropy patch for newer pydantic versions
10
+ ([#153](https://github.com/cloudflare/workers-py/pull/153),
11
+ [`81a4b6b`](https://github.com/cloudflare/workers-py/commit/81a4b6bad37373e0b448dd2dbedbf6cec0f47b8c))
12
+
13
+
5
14
  ## v1.6.6 (2026-08-03)
6
15
 
7
16
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workers-runtime-sdk
3
- Version: 1.6.6
3
+ Version: 1.6.7
4
4
  Summary: Python SDK for Cloudflare Workers
5
5
  Project-URL: Homepage, https://github.com/cloudflare/workers-py
6
6
  Project-URL: Bug Tracker, https://github.com/cloudflare/workers-py/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "workers-runtime-sdk"
7
- version = "1.6.6"
7
+ version = "1.6.7"
8
8
  description = "Python SDK for Cloudflare Workers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -20,6 +20,7 @@ dev = [
20
20
  "pytest>=7.0.0",
21
21
  "ruff>=0.1.0",
22
22
  "mypy>=1.0.0",
23
+ "requests>=2.28.0",
23
24
  ]
24
25
 
25
26
  [project.urls]
@@ -56,6 +57,13 @@ lint.ignore = [
56
57
  "UP038", # Use X | Y instead of (X, Y)
57
58
  ]
58
59
  lint.flake8-comprehensions.allow-dict-calls-with-keyword-arguments = true
60
+ lint.per-file-ignores."tests/**" = ["PLR2004", "PLR0913"]
61
+ lint.per-file-ignores."tests/workerd-test/**" = ["C901", "PLR0911", "PLR0912", "PLR2004", "PLW0603"]
62
+ lint.per-file-ignores."tests/bindings-test/**" = ["C901", "PLR0911", "PLR0912", "PLR2004", "PLW0603"]
63
+ lint.per-file-ignores."tests/web-frameworks-test/**" = ["C901", "PLR0911", "PLR0912", "PLR2004", "PLW0603"]
64
+
65
+ [tool.pytest.ini_options]
66
+ addopts = ["--ignore=tests/workerd-test", "--ignore=tests/bindings-test", "--ignore=tests/web-frameworks-test"]
59
67
 
60
68
  [tool.mypy]
61
69
  packages = ["workers_runtime_sdk"]
@@ -93,14 +93,10 @@ def pydantic_core_context(module):
93
93
  with allow_bad_entropy_calls(1):
94
94
  yield
95
95
  finally:
96
- try:
97
- with allow_bad_entropy_calls(1):
98
- # validate_core_schema makes an ahash::AHashMap which makes
99
- # another entropy call for its hash seed. It will throw an error
100
- # but only after making the needed entropy call.
101
- module.validate_core_schema(None)
102
- except module.SchemaError:
103
- pass
96
+ # Build a tiny validator instead so any lazy hash-map seeds are initialized
97
+ # before the snapshot is captured.
98
+ with allow_bad_entropy_calls(1):
99
+ module.SchemaValidator({"type": "any"})
104
100
 
105
101
 
106
102
  @register_exec_patch("aiohttp.http_websocket")
@@ -0,0 +1 @@
1
+ {"key": "value", "number": 42}
@@ -0,0 +1 @@
1
+ hello from static assets
@@ -0,0 +1,5 @@
1
+ [project]
2
+ name = "bindings-test"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.12"
5
+ dependencies = ["pytest", "pytest-asyncio<1.2.0"]
@@ -0,0 +1,10 @@
1
+ # pyright: reportMissingImports=false
2
+
3
+ import pytest
4
+
5
+ from workers import env as _env
6
+
7
+
8
+ @pytest.fixture
9
+ def env():
10
+ return _env
@@ -0,0 +1,24 @@
1
+ import pytest
2
+
3
+ from workers.rpc import _BindingWrapper
4
+
5
+
6
+ @pytest.mark.asyncio
7
+ async def test_is_wrapped(env):
8
+ assert isinstance(env.ANALYTICS, _BindingWrapper)
9
+
10
+
11
+ @pytest.mark.asyncio
12
+ async def test_write_data_point_blobs_and_doubles(env):
13
+ env.ANALYTICS.writeDataPoint(
14
+ {
15
+ "blobs": ["blob1", "blob2"],
16
+ "doubles": [1.0, 2.5],
17
+ "indexes": ["idx"],
18
+ }
19
+ )
20
+
21
+
22
+ @pytest.mark.asyncio
23
+ async def test_write_data_point_empty(env):
24
+ env.ANALYTICS.writeDataPoint({})
@@ -0,0 +1,35 @@
1
+ import pytest
2
+
3
+ from workers.rpc import _FetcherWrapper
4
+
5
+
6
+ @pytest.mark.asyncio
7
+ async def test_is_fetcher_wrapper(env):
8
+ assert isinstance(env.ASSETS, _FetcherWrapper)
9
+
10
+
11
+ @pytest.mark.asyncio
12
+ async def test_fetch_text_file(env):
13
+ resp = await env.ASSETS.fetch("https://assets.local/hello.txt")
14
+ text = await resp.text()
15
+ assert text.strip() == "hello from static assets"
16
+
17
+
18
+ @pytest.mark.asyncio
19
+ async def test_fetch_json_file(env):
20
+ resp = await env.ASSETS.fetch("https://assets.local/data.json")
21
+ data = await resp.json()
22
+ assert data["key"] == "value"
23
+ assert data["number"] == 42
24
+
25
+
26
+ @pytest.mark.asyncio
27
+ async def test_fetch_returns_404_for_missing(env):
28
+ resp = await env.ASSETS.fetch("https://assets.local/nonexistent.txt")
29
+ assert resp.status == 404
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_fetch_content_type(env):
34
+ resp = await env.ASSETS.fetch("https://assets.local/data.json")
35
+ assert "application/json" in resp.headers.get("content-type")
@@ -0,0 +1,498 @@
1
+ import pytest
2
+ from pyodide.ffi import JsException
3
+
4
+ from workers.rpc import _BindingWrapper, _RpcStubWrapper
5
+
6
+ TEST_TABLE = "_test_d1"
7
+ TEST_TABLE_TYPES = "_test_d1_types"
8
+ TEST_TABLE_BATCH = "_test_d1_batch"
9
+ EXEC_TABLE = "_test_d1_exec_tmp"
10
+ EXEC_MULTI_TABLE = "_test_d1_exec_multi"
11
+
12
+
13
+ async def _cleanup_d1(db):
14
+ for table in [
15
+ TEST_TABLE,
16
+ TEST_TABLE_TYPES,
17
+ TEST_TABLE_BATCH,
18
+ EXEC_TABLE,
19
+ EXEC_MULTI_TABLE,
20
+ ]:
21
+ try:
22
+ await db.exec(f"DROP TABLE IF EXISTS {table}")
23
+ except Exception:
24
+ pass
25
+
26
+
27
+ async def _ensure_tables(db):
28
+ await db.exec(
29
+ f"CREATE TABLE IF NOT EXISTS {TEST_TABLE} "
30
+ f"(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, value TEXT)"
31
+ )
32
+ await db.exec(
33
+ f"CREATE TABLE IF NOT EXISTS {TEST_TABLE_TYPES} "
34
+ f"(id INTEGER PRIMARY KEY, txt TEXT, num REAL, intval INTEGER)"
35
+ )
36
+ await db.exec(
37
+ f"CREATE TABLE IF NOT EXISTS {TEST_TABLE_BATCH} "
38
+ f"(id INTEGER PRIMARY KEY, val TEXT)"
39
+ )
40
+
41
+
42
+ @pytest.mark.asyncio
43
+ async def test_chained_statements_stay_binding_wrappers(env):
44
+ # A binding method returning another binding object must keep the plain
45
+ # binding wrapper.
46
+ statement = env.DB.prepare(f"SELECT 1 FROM {TEST_TABLE} WHERE name = ?")
47
+ assert isinstance(statement, _BindingWrapper)
48
+ assert not isinstance(statement, _RpcStubWrapper)
49
+ assert not callable(statement)
50
+
51
+ bound = statement.bind("x")
52
+ assert isinstance(bound, _BindingWrapper)
53
+ assert not isinstance(bound, _RpcStubWrapper)
54
+ assert not callable(bound)
55
+
56
+
57
+ @pytest.mark.asyncio
58
+ async def test_insert_and_select_via_run(env):
59
+ db = env.DB
60
+ await _cleanup_d1(db)
61
+ await _ensure_tables(db)
62
+ insert_result = (
63
+ await db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
64
+ .bind("run_test", "hello")
65
+ .run()
66
+ )
67
+ assert insert_result["success"] is True
68
+ meta = insert_result["meta"]
69
+ assert meta["changes"] >= 1
70
+ assert meta["last_row_id"] > 0
71
+ row_id = meta["last_row_id"]
72
+ select_result = (
73
+ await db.prepare(f"SELECT id, name, value FROM {TEST_TABLE} WHERE id = ?")
74
+ .bind(row_id)
75
+ .run()
76
+ )
77
+ rows = select_result["results"]
78
+ assert len(rows) == 1
79
+ assert rows[0]["name"] == "run_test"
80
+ assert rows[0]["value"] == "hello"
81
+
82
+
83
+ @pytest.mark.asyncio
84
+ async def test_all_returns_results(env):
85
+ db = env.DB
86
+ await _cleanup_d1(db)
87
+ await _ensure_tables(db)
88
+ await (
89
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
90
+ .bind("all_test", "v1")
91
+ .run()
92
+ )
93
+ result = (
94
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ?")
95
+ .bind("all_test")
96
+ .all()
97
+ )
98
+ assert result["success"] is True
99
+ rows = result["results"]
100
+ assert len(rows) >= 1
101
+ assert rows[0]["name"] == "all_test"
102
+
103
+
104
+ @pytest.mark.asyncio
105
+ async def test_first_returns_single_row(env):
106
+ db = env.DB
107
+ await _cleanup_d1(db)
108
+ await _ensure_tables(db)
109
+ await (
110
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
111
+ .bind("first_test", "fv")
112
+ .run()
113
+ )
114
+ row = (
115
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
116
+ .bind("first_test")
117
+ .first()
118
+ )
119
+ assert row is not None, "first() returned None"
120
+ assert isinstance(row, dict)
121
+ assert row["name"] == "first_test"
122
+ assert row["value"] == "fv"
123
+
124
+
125
+ @pytest.mark.asyncio
126
+ async def test_first_with_column_name(env):
127
+ db = env.DB
128
+ await _cleanup_d1(db)
129
+ await _ensure_tables(db)
130
+ await (
131
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
132
+ .bind("first_col", "col_val")
133
+ .run()
134
+ )
135
+ value = (
136
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
137
+ .bind("first_col")
138
+ .first("value")
139
+ )
140
+ assert value == "col_val"
141
+
142
+
143
+ @pytest.mark.asyncio
144
+ async def test_first_on_empty_result(env):
145
+ db = env.DB
146
+ await _cleanup_d1(db)
147
+ await _ensure_tables(db)
148
+ row = (
149
+ await db.prepare(f"SELECT * FROM {TEST_TABLE} WHERE name = ?")
150
+ .bind("__nonexistent__xyz__")
151
+ .first()
152
+ )
153
+ assert row is None
154
+
155
+
156
+ @pytest.mark.asyncio
157
+ async def test_raw_returns_arrays(env):
158
+ db = env.DB
159
+ await _cleanup_d1(db)
160
+ await _ensure_tables(db)
161
+ await (
162
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
163
+ .bind("raw_test", "rv")
164
+ .run()
165
+ )
166
+ rows = (
167
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
168
+ .bind("raw_test")
169
+ .raw()
170
+ )
171
+ assert isinstance(rows, list)
172
+ assert len(rows) == 1
173
+ assert rows[0] == ["raw_test", "rv"]
174
+
175
+
176
+ @pytest.mark.asyncio
177
+ async def test_raw_with_column_names(env):
178
+ db = env.DB
179
+ await _cleanup_d1(db)
180
+ await _ensure_tables(db)
181
+ await (
182
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
183
+ .bind("raw_cols", "rc")
184
+ .run()
185
+ )
186
+ rows = (
187
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
188
+ .bind("raw_cols")
189
+ .raw(columnNames=True)
190
+ )
191
+ assert len(rows) == 2
192
+ assert rows[0] == ["name", "value"]
193
+ assert rows[1] == ["raw_cols", "rc"]
194
+
195
+
196
+ @pytest.mark.asyncio
197
+ async def test_bind_types(env):
198
+ db = env.DB
199
+ await _cleanup_d1(db)
200
+ await _ensure_tables(db)
201
+
202
+ await (
203
+ db.prepare(
204
+ f"INSERT INTO {TEST_TABLE_TYPES} (id, txt, num, intval) VALUES (?, ?, ?, ?)"
205
+ )
206
+ .bind(2, "hello, D1!", 3.14, 42)
207
+ .run()
208
+ )
209
+ await (
210
+ db.prepare(f"INSERT INTO {TEST_TABLE_TYPES} (id, intval) VALUES (?, ?)")
211
+ .bind(3, True)
212
+ .run()
213
+ )
214
+ await (
215
+ db.prepare(f"INSERT INTO {TEST_TABLE_TYPES} (id, intval) VALUES (?, ?)")
216
+ .bind(4, False)
217
+ .run()
218
+ )
219
+
220
+ row2 = (
221
+ await db.prepare(
222
+ f"SELECT txt, num, intval FROM {TEST_TABLE_TYPES} WHERE id = ?"
223
+ )
224
+ .bind(2)
225
+ .first()
226
+ )
227
+ assert row2["txt"] == "hello, D1!"
228
+ assert abs(row2["num"] - 3.14) < 0.001
229
+ assert row2["intval"] == 42
230
+
231
+ row3 = (
232
+ await db.prepare(f"SELECT intval FROM {TEST_TABLE_TYPES} WHERE id = ?")
233
+ .bind(3)
234
+ .first()
235
+ )
236
+ row4 = (
237
+ await db.prepare(f"SELECT intval FROM {TEST_TABLE_TYPES} WHERE id = ?")
238
+ .bind(4)
239
+ .first()
240
+ )
241
+ assert row3["intval"] == 1
242
+ assert row4["intval"] == 0
243
+
244
+
245
+ @pytest.mark.asyncio
246
+ async def test_exec_create_and_query(env):
247
+ db = env.DB
248
+ await _cleanup_d1(db)
249
+ result = await db.exec(
250
+ f"CREATE TABLE IF NOT EXISTS {EXEC_TABLE} (id INTEGER PRIMARY KEY, val TEXT)"
251
+ )
252
+ assert result["count"] >= 1
253
+ assert result["duration"] >= 0
254
+
255
+
256
+ @pytest.mark.asyncio
257
+ async def test_exec_multiple_statements(env):
258
+ db = env.DB
259
+ await _cleanup_d1(db)
260
+ result = await db.exec(
261
+ f"CREATE TABLE IF NOT EXISTS {EXEC_MULTI_TABLE} (id INTEGER PRIMARY KEY, val TEXT);\n"
262
+ f"INSERT INTO {EXEC_MULTI_TABLE} (val) VALUES ('a');\n"
263
+ f"INSERT INTO {EXEC_MULTI_TABLE} (val) VALUES ('b');"
264
+ )
265
+ assert result["count"] >= 3
266
+ rows = await db.prepare(f"SELECT val FROM {EXEC_MULTI_TABLE} ORDER BY val").raw()
267
+ assert rows == [["a"], ["b"]]
268
+
269
+
270
+ @pytest.mark.asyncio
271
+ async def test_batch_multiple_inserts(env):
272
+ db = env.DB
273
+ await _cleanup_d1(db)
274
+ await _ensure_tables(db)
275
+ statements = [
276
+ db.prepare(f"INSERT INTO {TEST_TABLE_BATCH} (id, val) VALUES (?, ?)").bind(
277
+ 1, "batch_a"
278
+ ),
279
+ db.prepare(f"INSERT INTO {TEST_TABLE_BATCH} (id, val) VALUES (?, ?)").bind(
280
+ 2, "batch_b"
281
+ ),
282
+ db.prepare(f"INSERT INTO {TEST_TABLE_BATCH} (id, val) VALUES (?, ?)").bind(
283
+ 3, "batch_c"
284
+ ),
285
+ ]
286
+ results = await db.batch(statements)
287
+ assert results is not None, "batch returned None"
288
+ all_rows = await db.prepare(
289
+ f"SELECT id, val FROM {TEST_TABLE_BATCH} ORDER BY id"
290
+ ).all()
291
+ rows = all_rows["results"]
292
+ assert len(rows) == 3
293
+ assert [row["val"] for row in rows] == ["batch_a", "batch_b", "batch_c"]
294
+
295
+
296
+ @pytest.mark.asyncio
297
+ async def test_run_metadata_fields(env):
298
+ db = env.DB
299
+ await _cleanup_d1(db)
300
+ await _ensure_tables(db)
301
+ result = (
302
+ await db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
303
+ .bind("meta_test", "mv")
304
+ .run()
305
+ )
306
+ assert result["success"] is True
307
+ meta = result["meta"]
308
+ for key in [
309
+ "duration",
310
+ "changes",
311
+ "last_row_id",
312
+ "changed_db",
313
+ "rows_read",
314
+ "rows_written",
315
+ "size_after",
316
+ ]:
317
+ assert key in meta
318
+ assert meta["changes"] >= 1
319
+ assert meta["changed_db"] is True
320
+
321
+
322
+ @pytest.mark.asyncio
323
+ async def test_update_row(env):
324
+ db = env.DB
325
+ await _cleanup_d1(db)
326
+ await _ensure_tables(db)
327
+ insert_result = (
328
+ await db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
329
+ .bind("update_me", "old_value")
330
+ .run()
331
+ )
332
+ row_id = insert_result["meta"]["last_row_id"]
333
+ update_result = (
334
+ await db.prepare(f"UPDATE {TEST_TABLE} SET value = ? WHERE id = ?")
335
+ .bind("new_value", row_id)
336
+ .run()
337
+ )
338
+ assert update_result["meta"]["changes"] == 1
339
+ row = (
340
+ await db.prepare(f"SELECT value FROM {TEST_TABLE} WHERE id = ?")
341
+ .bind(row_id)
342
+ .first()
343
+ )
344
+ assert row["value"] == "new_value"
345
+
346
+
347
+ @pytest.mark.asyncio
348
+ async def test_delete_row(env):
349
+ db = env.DB
350
+ await _cleanup_d1(db)
351
+ await _ensure_tables(db)
352
+ insert_result = (
353
+ await db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
354
+ .bind("delete_me", "gone")
355
+ .run()
356
+ )
357
+ row_id = insert_result["meta"]["last_row_id"]
358
+ delete_result = (
359
+ await db.prepare(f"DELETE FROM {TEST_TABLE} WHERE id = ?").bind(row_id).run()
360
+ )
361
+ assert delete_result["meta"]["changes"] == 1
362
+ row = (
363
+ await db.prepare(f"SELECT * FROM {TEST_TABLE} WHERE id = ?")
364
+ .bind(row_id)
365
+ .first()
366
+ )
367
+ assert row is None
368
+
369
+
370
+ @pytest.mark.asyncio
371
+ async def test_session_prepare_and_query(env):
372
+ db = env.DB
373
+ await _cleanup_d1(db)
374
+ await _ensure_tables(db)
375
+ await (
376
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
377
+ .bind("session_test", "sv")
378
+ .run()
379
+ )
380
+ session = db.withSession()
381
+ assert session is not None, "withSession() returned None"
382
+ result = await session.prepare(f"SELECT COUNT(*) as cnt FROM {TEST_TABLE}").all()
383
+ assert result["success"] is True
384
+ assert result["results"][0]["cnt"] >= 1
385
+
386
+
387
+ @pytest.mark.asyncio
388
+ async def test_session_bookmark(env):
389
+ db = env.DB
390
+ await _cleanup_d1(db)
391
+ await _ensure_tables(db)
392
+ session = db.withSession()
393
+ bookmark_before = session.getBookmark()
394
+ assert bookmark_before is None
395
+ await session.prepare("SELECT 1").all()
396
+ bookmark_after = session.getBookmark()
397
+ assert bookmark_after is not None
398
+ assert isinstance(bookmark_after, str)
399
+ assert len(bookmark_after) > 0
400
+
401
+
402
+ @pytest.mark.asyncio
403
+ async def test_session_batch(env):
404
+ db = env.DB
405
+ await _cleanup_d1(db)
406
+ await _ensure_tables(db)
407
+ session = db.withSession()
408
+ statements = [
409
+ session.prepare(f"INSERT INTO {TEST_TABLE_BATCH} (id, val) VALUES (?, ?)").bind(
410
+ 1, "sa"
411
+ ),
412
+ session.prepare(f"INSERT INTO {TEST_TABLE_BATCH} (id, val) VALUES (?, ?)").bind(
413
+ 2, "sb"
414
+ ),
415
+ ]
416
+ results = await session.batch(statements)
417
+ assert results is not None, "session batch returned None"
418
+ rows = await session.prepare(
419
+ f"SELECT id, val FROM {TEST_TABLE_BATCH} ORDER BY id"
420
+ ).all()
421
+ assert len(rows["results"]) == 2
422
+ assert rows["results"][0]["val"] == "sa"
423
+ assert rows["results"][1]["val"] == "sb"
424
+
425
+
426
+ @pytest.mark.asyncio
427
+ async def test_invalid_sql_raises_error(env):
428
+ db = env.DB
429
+ await _cleanup_d1(db)
430
+ with pytest.raises(JsException, match="syntax error"):
431
+ await db.prepare("INVALID SQL GIBBERISH").run()
432
+
433
+
434
+ @pytest.mark.asyncio
435
+ async def test_raw_dict_column_names(env):
436
+ db = env.DB
437
+ await _cleanup_d1(db)
438
+ await _ensure_tables(db)
439
+ await (
440
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
441
+ .bind("dict_raw", "dr")
442
+ .run()
443
+ )
444
+ rows = await (
445
+ db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
446
+ .bind("dict_raw")
447
+ .raw({"columnNames": True})
448
+ )
449
+ assert len(rows) == 2
450
+ assert rows[0] == ["name", "value"]
451
+ assert rows[1] == ["dict_raw", "dr"]
452
+
453
+
454
+ @pytest.mark.asyncio
455
+ async def test_bind_null(env):
456
+ import sys
457
+
458
+ if sys.version_info < (3, 13):
459
+ pytest.skip("Pyodide 0.26 (Python 3.12) cannot represent JS null")
460
+ db = env.DB
461
+ await _cleanup_d1(db)
462
+ await _ensure_tables(db)
463
+ await (
464
+ db.prepare(
465
+ f"INSERT INTO {TEST_TABLE_TYPES} (id, txt, num, intval) VALUES (?, ?, ?, ?)"
466
+ )
467
+ .bind(1, None, None, None)
468
+ .run()
469
+ )
470
+ row = (
471
+ await db.prepare(
472
+ f"SELECT txt, num, intval FROM {TEST_TABLE_TYPES} WHERE id = ?"
473
+ )
474
+ .bind(1)
475
+ .first()
476
+ )
477
+ assert row["txt"] is None
478
+ assert row["num"] is None
479
+ assert row["intval"] is None
480
+
481
+
482
+ @pytest.mark.asyncio
483
+ async def test_none_options_raw(env):
484
+ db = env.DB
485
+ await _cleanup_d1(db)
486
+ await _ensure_tables(db)
487
+ await (
488
+ db.prepare(f"INSERT INTO {TEST_TABLE} (name, value) VALUES (?, ?)")
489
+ .bind("none_raw", "nr")
490
+ .run()
491
+ )
492
+ rows = (
493
+ await db.prepare(f"SELECT name, value FROM {TEST_TABLE} WHERE name = ? LIMIT 1")
494
+ .bind("none_raw")
495
+ .raw(None)
496
+ )
497
+ assert len(rows) == 1
498
+ assert rows[0] == ["none_raw", "nr"]