Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
with:
python-version: '3.14'

- name: Run Hyperdrive binding tests
- name: Run Hyperdrive tests
working-directory: packages/runtime-sdk
run: |
uv run --frozen pytest -v --color=yes -m hyperdrive tests
Expand Down
37 changes: 37 additions & 0 deletions packages/runtime-sdk/tests/test_django_databases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# pyright: reportAttributeAccessIssue=false, reportMissingImports=false

"""Stock Django database backends running through Hyperdrive in workerd."""

from pathlib import Path

import pytest
from conftest import COMPAT_CONFIGS, CompatConfig, register_in_worker_suites

DJANGO_DATABASES_DIR: Path = (
Path(__file__).parent / "web-frameworks-test" / "django-databases"
)
DJANGO_DATABASES_SRC_DIR: Path = DJANGO_DATABASES_DIR / "src"
PYTHON_314_CONFIGS = [
config for config in COMPAT_CONFIGS if config.python_version == "3.14"
]
SUITE_MARKS: dict[str, pytest.MarkDecorator] = {
"postgresql": pytest.mark.hyperdrive,
"mysql": pytest.mark.hyperdrive,
}


@pytest.fixture(scope="module")
def worker_project_dir() -> Path:
return DJANGO_DATABASES_DIR


@pytest.fixture(
scope="module",
params=PYTHON_314_CONFIGS,
ids=[config.python_version for config in PYTHON_314_CONFIGS],
)
def compat_config(request: pytest.FixtureRequest) -> CompatConfig:
return request.param


register_in_worker_suites(globals(), DJANGO_DATABASES_SRC_DIR, marks=SUITE_MARKS)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[project]
name = "django-databases-test"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
"django>=5.2.8",
"mysqlclient",
"psycopg[binary]",
"pytest",
"testlib",
"workers-runtime-sdk",
]

[tool.uv.sources]
testlib = { path = "../packages/testlib" }
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# pyright: reportMissingImports=false

"""Django settings for stock backends running through Hyperdrive."""

import os

import django
from django.conf import settings

from workers import env

os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")

HYPERDRIVE_POSTGRESQL = "hyperdrive_postgresql"
HYPERDRIVE_MYSQL = "hyperdrive_mysql"


def _postgresql_database() -> dict[str, object]:
hyperdrive = env.HYPERDRIVE_PG
return {
"ENGINE": "django.db.backends.postgresql",
"HOST": hyperdrive.host,
"PORT": str(hyperdrive.port),
"USER": hyperdrive.user,
"PASSWORD": hyperdrive.password,
"NAME": hyperdrive.database,
"CONN_MAX_AGE": 0,
"OPTIONS": {"sslmode": "disable"},
"DISABLE_SERVER_SIDE_CURSORS": True,
}


def _mysql_database() -> dict[str, object]:
hyperdrive = env.HYPERDRIVE_MYSQL
return {
"ENGINE": "django.db.backends.mysql",
"HOST": hyperdrive.host,
"PORT": str(hyperdrive.port),
"USER": hyperdrive.user,
"PASSWORD": hyperdrive.password,
"NAME": hyperdrive.database,
"CONN_MAX_AGE": 0,
"OPTIONS": {"ssl_mode": "DISABLED"},
}


settings.configure(
DEBUG=False,
SECRET_KEY="test-secret-key-for-workers-py",
INSTALLED_APPS=[],
DATABASES={
"default": {"ENGINE": "django.db.backends.dummy"},
HYPERDRIVE_POSTGRESQL: _postgresql_database(),
HYPERDRIVE_MYSQL: _mysql_database(),
},
TIME_ZONE="UTC",
USE_TZ=True,
DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
)

django.setup()
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""ORM assertions shared by the independent Django backend suites."""

import uuid
from collections.abc import Iterator
from contextlib import contextmanager

import pytest
from django.db import connections, models, transaction
from django.test.utils import isolate_apps


class Rollback(Exception):
"""Raised to roll back an atomic block."""


def _table_name() -> str:
return f"dht_{uuid.uuid4().hex}"


@contextmanager
def _temporary_model(alias: str) -> Iterator[type[models.Model]]:
table_name = _table_name()
connection = connections[alias]

with isolate_apps():

class TemporaryModel(models.Model):
name = models.CharField(max_length=100)
value = models.IntegerField()
float_value = models.FloatField(default=0.0)
is_active = models.BooleanField(default=False)

class Meta:
app_label = "hyperdrive_tests"
db_table = table_name

created = False
try:
with connection.schema_editor() as schema_editor:
schema_editor.create_model(TemporaryModel)
created = True
yield TemporaryModel
finally:
if created:
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(TemporaryModel)


def assert_connectivity(alias: str) -> None:
connection = connections[alias]
connection.ensure_connection()
assert connection.is_usable()


def assert_crud(alias: str) -> None:
with _temporary_model(alias) as model:
objects = model.objects.using(alias)
objects.create(name="alpha", value=1)
objects.create(name="beta", value=2)

assert list(
objects.filter(name__in=["alpha", "beta"])
.order_by("name")
.values_list("name", "value")
) == [("alpha", 1), ("beta", 2)]

assert objects.filter(name="alpha").update(value=11) == 1
assert objects.get(name="alpha").value == 11

assert objects.filter(name="alpha").delete()[0] == 1
assert objects.count() == 1
assert objects.get(name="beta").value == 2


def assert_data_types(alias: str) -> None:
with _temporary_model(alias) as model:
objects = model.objects.using(alias)
created = objects.create(
name="hello",
value=42,
float_value=3.14,
is_active=True,
)
row = objects.get(pk=created.pk)

assert row.name == "hello"
assert row.value == 42
assert abs(row.float_value - 3.14) < 0.001
assert row.is_active is True


def assert_transaction_rollback(alias: str) -> None:
with _temporary_model(alias) as model:
objects = model.objects.using(alias)

with pytest.raises(Rollback), transaction.atomic(using=alias):
objects.create(name="should_disappear", value=1)
raise Rollback

assert objects.count() == 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# pyright: reportMissingImports=false

"""Stock Django MySQL backend running through the HYPERDRIVE_MYSQL binding."""

import pytest
from django.db import connections
from django_db_config import HYPERDRIVE_MYSQL
from django_db_helpers import (
assert_connectivity,
assert_crud,
assert_data_types,
assert_transaction_rollback,
)


@pytest.fixture(autouse=True)
def close_connections():
yield
connections[HYPERDRIVE_MYSQL].close()


def test_connectivity():
assert_connectivity(HYPERDRIVE_MYSQL)


def test_crud():
assert_crud(HYPERDRIVE_MYSQL)


def test_data_types():
assert_data_types(HYPERDRIVE_MYSQL)


def test_transaction_rollback():
assert_transaction_rollback(HYPERDRIVE_MYSQL)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# pyright: reportMissingImports=false

"""Stock Django PostgreSQL backend running through the HYPERDRIVE_PG binding."""

import pytest
from django.db import connections
from django_db_config import HYPERDRIVE_POSTGRESQL
from django_db_helpers import (
assert_connectivity,
assert_crud,
assert_data_types,
assert_transaction_rollback,
)


@pytest.fixture(autouse=True)
def close_connections():
yield
connections[HYPERDRIVE_POSTGRESQL].close()


def test_connectivity():
assert_connectivity(HYPERDRIVE_POSTGRESQL)


def test_crud():
assert_crud(HYPERDRIVE_POSTGRESQL)


def test_data_types():
assert_data_types(HYPERDRIVE_POSTGRESQL)


def test_transaction_rollback():
assert_transaction_rollback(HYPERDRIVE_POSTGRESQL)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# pyright: reportMissingImports=false

"""Django database backend test worker."""

from testlib.entrypoint import TestRunnerEntrypoint


class Default(TestRunnerEntrypoint):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "django-databases-test",
"main": "src/worker.py",
"compatibility_date": "%COMPAT_DATE",
"compatibility_flags": ["python_workers"],
"hyperdrive": [
{
"binding": "HYPERDRIVE_PG",
"id": "00000000-0000-0000-0000-000000000001",
"localConnectionString": "postgres://testuser:testpass@127.0.0.1:5432/testdb"
},
{
"binding": "HYPERDRIVE_MYSQL",
"id": "00000000-0000-0000-0000-000000000002",
"localConnectionString": "mysql://testuser:testpass@127.0.0.1:3306/testdb"
}
]
}
Loading