Skip to content
Merged
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
7 changes: 7 additions & 0 deletions pgdevkit/testdb/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
USER = os.environ.get("PGDEVKIT_TESTDB_USER", "postgres")
PASSWORD = os.environ.get("PGDEVKIT_TESTDB_PASSWORD", "testpwd")
PG_SPEED_FLAGS = ["-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
# Production connects with session timezone=UTC; the container image's own default
# (baked into its base OS, not something pgdevkit ever set) can differ, silently
# making any ::date cast of a timestamptz column disagree between test and prod for
# rows near local midnight. Forcing UTC here -- both as the server's own timezone GUC
# and as PGTZ/TZ for any client library that consults the environment instead -- keeps
# test parity with prod instead of depending on the host/image's locale.
PG_STARTUP_FLAGS = [*PG_SPEED_FLAGS, "-c", "timezone=UTC"]


def conninfo(dbname: str, *, connect_timeout: int | None = None) -> str:
Expand Down
7 changes: 6 additions & 1 deletion pgdevkit/testdb/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ def _create_container(client: docker.DockerClient) -> None:
environment={
"POSTGRES_USER": constants.USER,
"POSTGRES_PASSWORD": constants.PASSWORD,
# Belt-and-suspenders alongside the server's own -c timezone=UTC below:
# some client libraries/tools inside the container consult TZ/PGTZ
# directly instead of asking Postgres for its configured timezone.
"TZ": "UTC",
"PGTZ": "UTC",
},
command=["postgres", *constants.PG_SPEED_FLAGS],
command=["postgres", *constants.PG_STARTUP_FLAGS],
)
except docker.errors.APIError as e:
if getattr(e, "status_code", None) == 409 or "already in use" in str(e):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ packages = ["pgdevkit"]

[project]
name = "pgdevkit"
version = "0.6.1"
version = "0.6.2"
description = "A helper for developing with Postgres"
readme = "README.md"
requires-python = ">=3.14"
Expand Down
40 changes: 40 additions & 0 deletions tests/testdb/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,36 @@ def _fail(*a, **k):
ensure_container() # must return without checking availability or calling the Docker API


class _FakeContainers:
def __init__(self):
self.run_kwargs: dict | None = None

def run(self, image, **kwargs):
self.run_kwargs = kwargs


class _FakeClient:
def __init__(self):
self.containers = _FakeContainers()


def test_create_container_forces_utc_timezone():
"""Test Postgres must run with timezone=UTC regardless of the container
image's/host's own locale default -- production connects with session
timezone=UTC, so any ::date cast of a timestamptz column would otherwise
silently disagree between test and prod for rows near local midnight."""
client = _FakeClient()

_create_container(client)

assert client.containers.run_kwargs is not None
assert "-c" in client.containers.run_kwargs["command"]
timezone_flag_index = client.containers.run_kwargs["command"].index("timezone=UTC")
assert client.containers.run_kwargs["command"][timezone_flag_index - 1] == "-c"
assert client.containers.run_kwargs["environment"]["TZ"] == "UTC"
assert client.containers.run_kwargs["environment"]["PGTZ"] == "UTC"


def _admin_dsn() -> str:
return constants.conninfo("postgres", connect_timeout=5)

Expand Down Expand Up @@ -84,3 +114,13 @@ def test_create_container_falls_back_to_start_when_name_in_use():
with con.cursor() as cur:
cur.execute("SELECT 1")
assert cur.fetchone() == (1,)


@requires_podman
def test_ensure_container_runs_with_utc_timezone():
ensure_container()

with psycopg.connect(_admin_dsn()) as con:
with con.cursor() as cur:
cur.execute("SHOW timezone")
assert cur.fetchone() == ("UTC",)
2 changes: 1 addition & 1 deletion uv.lock

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

Loading