Skip to content

Commit

Permalink
test(JSONObjectAgg): add option to run tests on postgresql
Browse files Browse the repository at this point in the history
  • Loading branch information
bruno-fs committed Jun 25, 2024
1 parent c4cb380 commit 861fbab
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 17 deletions.
34 changes: 27 additions & 7 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ Changelog = "https://github.com/quipucords/django-json-agg/releases"
python = "^3.8"
django = ">=4.2"

[tool.poetry.group.dev.dependencies]
[tool.poetry.group.devel.dependencies]
Pygments = ">=2.10.0"
coverage = { extras = ["toml"], version = ">=6.2" }
faker = "^25.9.1"
furo = ">=2021.11.12"
myst-parser = { version = ">=0.16.1" }
pre-commit = ">=2.16.0"
pre-commit-hooks = ">=4.1.0"
psycopg2 = "^2.9.9"
pydoclint = "^0.4.1"
pytest = ">=6.2.5"
pytest-django = "^4.8.0"
Expand Down
46 changes: 43 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
"""Testing setup."""

from __future__ import annotations

import django
import pytest
from django.conf import settings


def pytest_addoption(parser: pytest.Parser):
parser.addoption("--db-vendor", action="store", default="sqlite")
parser.addoption("--db-name", action="store", default="postgres")
parser.addoption("--db-user", action="store", default="postgres")
parser.addoption("--db-password", action="store", default="")
parser.addoption("--db-host", action="store", default="127.0.0.1")
parser.addoption("--db-port", action="store", default="5432")


@pytest.fixture(scope="session")
def db_vendor(pytestconfig):
return pytestconfig.getoption("--db-vendor")

def pytest_configure(config):
from django.conf import settings

def pytest_configure(config: pytest.Config):
db_settings = get_db_settings(config)
settings.configure(
DEBUG_PROPAGATE_EXCEPTIONS=True,
DATABASES={
"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"},
"default": db_settings,
},
SITE_ID=1,
SECRET_KEY="not a secret in tests", # noqa: S106
Expand Down Expand Up @@ -45,3 +62,26 @@ def pytest_configure(config):
)

django.setup()


def get_db_settings(config: pytest.Config):
valid_db_vendors = ["sqlite", "postgresql"]
vendor_name = config.getoption("--db-vendor")
if vendor_name not in valid_db_vendors:
raise pytest.UsageError(
f"Invalid db vendor ('{vendor_name}'). Valid values are {valid_db_vendors}."
)
settings_per_vendor = {
"sqlite": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"},
"postgresql": {
"ENGINE": "django.db.backends.postgresql",
"NAME": config.getoption("--db-name"),
"USER": config.getoption("--db-user"),
"PASSWORD": config.getoption("--db-password"),
"HOST": config.getoption("--db-host"),
"PORT": config.getoption("--db-port"),
},
}

db_settings = settings_per_vendor[vendor_name]
return db_settings
21 changes: 15 additions & 6 deletions tests/test_obj_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from __future__ import annotations

import datetime
from functools import partial
from itertools import chain
from typing import TYPE_CHECKING

import pytest
from django.db.models import DateTimeField
from django.db.models import JSONField

from json_agg import JSONObjectAgg
from tests.models import Author
Expand Down Expand Up @@ -116,12 +116,17 @@ def test_aggregate_text(faker: Faker):


@pytest.mark.django_db
def test_aggregate_datetime(faker: Faker):
def test_aggregate_datetime(faker: Faker, db_vendor: str):
"""Test JSONObjectAgg over a datetime value (Post.updated_at)."""
kw = {}
if db_vendor == "postgresql":
# enforce tz for postgresql only - sqlite don't support it.
kw = dict(tzinfo=datetime.UTC)

expected_value_per_author_name = post_factory(
faker,
value_name="updated_at",
value_factory=partial(faker.date_time),
value_factory=partial(faker.date_time, **kw),
)

queryset = Author.objects.annotate(
Expand All @@ -142,13 +147,17 @@ def test_aggregate_json(faker: Faker):
value_name="metadata",
value_factory=partial(faker.pydict, allowed_types=(str, int)),
)

# we can't use nested_output_field for JSONField because it would only work
# for sqlite, which internally stores json as text. On postgres, data is
# stored as jsonb, which is automatically translated to dict (maybe by psycopg2?).
# This distinction on how those objects are stored internally breaks JSONField
# default decoder, which doesn't expect dicts.
queryset = Author.objects.annotate(
post_metadata=JSONObjectAgg(
"posts__title",
"posts__metadata",
nested_output_field=JSONField(),
# sqlite_func="json",
# nested_output_field=JSONField(),
sqlite_func="json",
)
).all()

Expand Down

0 comments on commit 861fbab

Please sign in to comment.