From c4548999795337d7a620fc81cd4a62c93d39fb88 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Thu, 9 Jul 2026 13:53:30 +0200 Subject: [PATCH 1/3] Dockerfile: replace pipenv with python3-pytest; quote pyroute2 version - drop pipenv (unused) - add python3-pytest from apt (available in bullseye at 6.0.2) - quote 'pyroute2>=0.5.17' so the shell does not strip the >= operator --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1df68994d..1ea332536 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,8 +44,8 @@ RUN apt-get update \ make \ autoconf \ openssl \ - pipenv \ python3-nose \ + python3-pytest \ python3 \ python3-setuptools \ python3-psycopg2 \ @@ -74,7 +74,7 @@ RUN apt-get update \ postgresql-${PGVERSION} \ && rm -rf /var/lib/apt/lists/* -RUN pip3 install pyroute2>=0.5.17 +RUN pip3 install 'pyroute2>=0.5.17' RUN adduser --disabled-password --gecos '' docker RUN adduser docker sudo From 89e58c28181c006890c3971daccf9412d7375bf8 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Thu, 9 Jul 2026 13:53:39 +0200 Subject: [PATCH 2/3] tests: switch test runner from nosetests to pytest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python3-nose crashes on Python 3.11+ (collections.Callable removed in 3.10; nose/suite.py still uses it). pytest is the modern replacement and is now installed via apt alongside python3-nose (kept for the nose.tools helpers that a few tests still use). Makefile changes: - NOSETESTS → PYTEST variable using `which pytest` - TEST_ARGUMENT format: nose --where/--tests style → pytest path style (tests/*.py for all, tests/foo.py for a named test) - Use tests/*.py glob (not tests/) to avoid descending into tests/tablespaces/ which has its own import layout and make target Makefile.citus: apply the same TEST_ARGUMENT pattern update that the main Makefile received. --- Makefile | 23 +++++++++++------------ Makefile.citus | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 733e15540..743f3b75d 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ include Makefile.azure # # LIST TESTS # -NOSETESTS = $(shell which nosetests3 || which nosetests) +PYTEST = $(shell which pytest || which pytest3) # Tests for the monitor TESTS_MONITOR = test_extension_update @@ -78,17 +78,17 @@ TESTS_MULTI += test_multi_standbys # Included Makefile may define TEST_ARGUMENT (like for citus) TEST ?= ifeq ($(TEST),) - TEST_ARGUMENT = --where=tests + TEST_ARGUMENT = tests/*.py else ifeq ($(TEST),multi) - TEST_ARGUMENT = --where=tests --tests=$(TESTS_MULTI) + TEST_ARGUMENT = $(TESTS_MULTI:%=tests/%.py) else ifeq ($(TEST),single) - TEST_ARGUMENT = --where=tests --tests=$(TESTS_SINGLE) + TEST_ARGUMENT = $(TESTS_SINGLE:%=tests/%.py) else ifeq ($(TEST),monitor) - TEST_ARGUMENT = --where=tests --tests=$(TESTS_MONITOR) + TEST_ARGUMENT = $(TESTS_MONITOR:%=tests/%.py) else ifeq ($(TEST),ssl) - TEST_ARGUMENT = --where=tests --tests=$(TESTS_SSL) + TEST_ARGUMENT = $(TESTS_SSL:%=tests/%.py) else - TEST_ARGUMENT = $(TEST:%=tests/%.py) + TEST_ARGUMENT = tests/$(TEST).py endif # @@ -167,11 +167,10 @@ ifeq ($(TEST),tablespaces) $(MAKE) -C tests/tablespaces run-test else sudo -E env "PATH=${PATH}" USER=$(shell whoami) \ - $(NOSETESTS) \ - --verbose \ - --nologcapture \ - --nocapture \ - --stop \ + $(PYTEST) \ + -v \ + -s \ + -x \ ${TEST_ARGUMENT} endif diff --git a/Makefile.citus b/Makefile.citus index d15511f2c..75f621098 100644 --- a/Makefile.citus +++ b/Makefile.citus @@ -19,7 +19,7 @@ ifeq ($(TEST),citus) TESTS_CITUS += test_nonha_citus_operation TESTS_CITUS += test_citus_skip_pg_hba - TEST_ARGUMENT = --where=tests --tests=$(TESTS_CITUS) + TEST_ARGUMENT = $(TESTS_CITUS:%=tests/%.py) endif # this target is defined and used later in the main Makefile From 158dc3a4ebed143b39521eecc2c3046d27360e07 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Thu, 9 Jul 2026 13:53:53 +0200 Subject: [PATCH 3/3] =?UTF-8?q?tests:=20fix=20pytest=20compatibility=20?= =?UTF-8?q?=E2=80=94=20binary=20path,=20CI=20gating,=20network=20locale,?= =?UTF-8?q?=20conftest,=20autocommit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Makefile: use 'python3 -m pytest' instead of 'which pytest' Debian's python3-pytest package does not install a 'pytest' binary in PATH (only 'py.test-3' and 'pytest-3'); 'python3 -m pytest' is the reliable invocation regardless of binary name. 2. .github/workflows/run-tests.yml: gate build_images on style_checker so a style failure stops CI before any image builds start. 3. tests/network.py: two fixes for pyroute2 >= 0.5.17 strict UTF-8: - replace universal_newlines=True with encoding='utf-8', errors='replace' in both NSPopen calls so non-ASCII bytes from pg_autoctl are replaced rather than raising UnicodeDecodeError. - add LC_ALL=C LANG=C to subprocess env so PostgreSQL produces ASCII-only error messages inside network namespaces. 4. tests/tablespaces/conftest.py: add project root to sys.path so the tablespace tests can import as 'from tests.tablespaces.xxx'. nosetests did this implicitly; pytest does not. 5. tests/pgautofailover_utils.py: fix run_sql_query when autocommit=True. psycopg2's 'with conn:' context manager wraps an implicit BEGIN even when autocommit is on (behaviour varies by psycopg2 version), blocking DDL like CREATE TABLESPACE. Use explicit commit/rollback/finally. --- .github/workflows/run-tests.yml | 1 + Makefile | 2 +- tests/network.py | 4 ++++ tests/pgautofailover_utils.py | 13 +++++++++---- tests/tablespaces/conftest.py | 8 ++++++++ 5 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/tablespaces/conftest.py diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6ca92f83f..322561aa4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,6 +30,7 @@ jobs: build_images: name: Build test image (PG${{ matrix.PGVERSION }}) + needs: style_checker runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/Makefile b/Makefile index 743f3b75d..73f062b48 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ include Makefile.azure # # LIST TESTS # -PYTEST = $(shell which pytest || which pytest3) +PYTEST = python3 -m pytest # Tests for the monitor TESTS_MONITOR = test_extension_update diff --git a/tests/network.py b/tests/network.py index 030d87db6..338d7297b 100644 --- a/tests/network.py +++ b/tests/network.py @@ -153,6 +153,8 @@ def run(self, command, user=os.getenv("USER")): user, "env", "PATH=" + os.getenv("PATH"), + "LC_ALL=C", + "LANG=C", ] + command return managed_nspopen( self.namespace, @@ -179,6 +181,8 @@ def run_unmanaged(self, command, user=os.getenv("USER")): user, "env", "PATH=" + os.getenv("PATH"), + "LC_ALL=C", + "LANG=C", ] + command return NSPopen( self.namespace, diff --git a/tests/pgautofailover_utils.py b/tests/pgautofailover_utils.py index 7aef6ecfe..7777e151b 100644 --- a/tests/pgautofailover_utils.py +++ b/tests/pgautofailover_utils.py @@ -271,16 +271,21 @@ def run_sql_query(self, query, autocommit, *args): conn = psycopg2.connect(self.connection_string()) conn.autocommit = autocommit - with conn: + try: with conn.cursor() as cur: cur.execute(query, args) try: result = cur.fetchall() except psycopg2.ProgrammingError: pass - # leaving contexts closes the cursor, however - # leaving contexts doesn't close the connection - conn.close() + if not autocommit: + conn.commit() + except Exception: + if not autocommit: + conn.rollback() + raise + finally: + conn.close() return result diff --git a/tests/tablespaces/conftest.py b/tests/tablespaces/conftest.py new file mode 100644 index 000000000..694e344d3 --- /dev/null +++ b/tests/tablespaces/conftest.py @@ -0,0 +1,8 @@ +import sys +import os + +# tablespace tests import as "from tests.tablespaces.xxx import ..." which +# requires the project root (parent of tests/) on sys.path. pytest does not +# add it automatically; nosetests used to, by treating tests/__init__.py as a +# package root. Add it here so both test runners work. +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))