From 55315b16d7f704173433cf9ffd51d380d8a2eaa2 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:33:06 +0100 Subject: [PATCH 01/10] Update conftest.py for using in container PyTest Signed-off-by: Petr "Stone" Hracek --- test/conftest.py | 15 ++++++++++++--- test/run-pytest | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100755 test/run-pytest diff --git a/test/conftest.py b/test/conftest.py index e4f969de..36403da2 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -38,6 +38,9 @@ def get_previous_major_version(): + """ + Get the previous major version of the PostgreSQL container. + """ version_dict = { "13": "12", "15": "13", @@ -47,11 +50,14 @@ def get_previous_major_version(): def get_upgrade_path(): + """ + Get the upgrade path of the PostgreSQL container. + """ upgrade_path = { "rhel8": "none 12 13 15 16 none", - "rhel9": "none 13 15 16 none", - "rhel10": "none 13 15 16 none", - "fedora": "none 12 13 14 15 16 none", + "rhel9": "none 13 15 16 18 none", + "rhel10": "none 16 18 none", + "fedora": "none 15 16 18 none", } for version in upgrade_path.keys(): if version == VARS.VERSION: @@ -63,6 +69,9 @@ def get_upgrade_path(): def get_image_id(version): + """ + Get the image ID of the PostgreSQL container. + """ ns = { "rhel8": f"registry.redhat.io/rhel8/postgresql-{version}", "rhel9": f"registry.redhat.io/rhel9/postgresql-{version}", diff --git a/test/run-pytest b/test/run-pytest new file mode 100755 index 00000000..202bd144 --- /dev/null +++ b/test/run-pytest @@ -0,0 +1,17 @@ +#!/bin/bash +# +# IMAGE_NAME specifies a name of the candidate image used for testing. +# The image has to be available before this script is executed. +# VERSION specifies the major version of the PostgreSQL in format of X.Y +# OS specifies RHEL version (e.g. OS=rhel10) +# + +THISDIR=$(dirname ${BASH_SOURCE[0]}) + +git show -s + +PYTHON_VERSION="3.12" +if [[ ! -f "/usr/bin/python$PYTHON_VERSION" ]]; then + PYTHON_VERSION="3.13" +fi +cd "${THISDIR}" && "python${PYTHON_VERSION}" -m pytest -s -rA --showlocals -vv test_container_*.py From 2c1067d372815495d7d61bc5072b7dc0d73c44c3 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:33:45 +0100 Subject: [PATCH 02/10] Add test_container_basics.py testing suite run_s2i_test -> test_container_basics.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_basics.py | 122 ++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 test/test_container_basics.py diff --git a/test/test_container_basics.py b/test/test_container_basics.py new file mode 100644 index 00000000..151785f8 --- /dev/null +++ b/test/test_container_basics.py @@ -0,0 +1,122 @@ +import shutil +import tempfile + +from pathlib import Path + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.utils import ContainerTestLibUtils + +from conftest import VARS + + +def build_s2i_app(app_path: Path) -> ContainerTestLib: + container_lib = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + app_name = app_path.name + s2i_app = container_lib.build_as_df( + app_path=app_path, + s2i_args="--pull-policy=never", + src_image=VARS.IMAGE_NAME, + dst_image=f"{VARS.IMAGE_NAME}-{app_name}", + ) + return s2i_app + + +class TestPostgreSQLBasicsContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.app_image = build_s2i_app(app_path=VARS.TEST_APP) + self.app_image.db_lib.db_type = "postgresql" + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.app_image.cleanup() + + def test_backup_functionality(self): + """ + Test backup functionality of the PostgreSQL container. + Steps are: + 1. Test if the container creation fails with invalid combinations of arguments + 2. Test if the container creation succeeds with valid combinations of arguments + 3. Test if the database connection works + 4. Test if the backup functionality works + """ + with tempfile.NamedTemporaryFile(prefix="/tmp/psql-temp-file") as temp_file: + cid_create = "conf_backup" + psql_password = "password" + psql_database = "db" + psql_user = "user" + psql_admin_password = psql_password + psql_backup_user = "backuser" + psql_backup_password = "pass" + self.app_image.assert_container_creation_fails( + cid_file_name=cid_create, + command="", + container_args=[ + f"-e POSTGRESQL_PASSWORD={psql_password}", + f"-e POSTGRESQL_DATABASE={psql_database}", + ], + ) + assert self.app_image.create_container( + cid_file_name=cid_create, + docker_args=[ + f"-e POSTGRESQL_USER={psql_user}", + f"-e POSTGRESQL_PASSWORD={psql_password}", + f"-e POSTGRESQL_DATABASE={psql_database}", + f"-e POSTGRESQL_BACKUP_USER={psql_backup_user}", + f"-e POSTGRESQL_BACKUP_PASSWORD={psql_backup_password}", + f"-e POSTGRESQL_ADMIN_PASSWORD={psql_admin_password}", + ], + ) + cip, cid = self.app_image.get_cip_cid(cid_file_name=cid_create) + assert cip and cid + self.check_psql_connection(cip, psql_user, psql_password) + backup_user_script = ( + VARS.TEST_DIR / "test-app/postgresql-init/backup_user.sh" + ) + shutil.copy(backup_user_script, temp_file.name) + ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:rw- {temp_file.name}", + ] + ) + + cid_backup = "cid_backup" + mount_point = "/opt/app-root/src/postgresql-init/add_backup_user.sh" + assert self.app_image.create_container( + cid_file_name=cid_backup, + docker_args=[ + f"-e POSTGRESQL_USER={psql_user}", + f"-e POSTGRESQL_PASSWORD={psql_password}", + f"-e POSTGRESQL_DATABASE={psql_database}", + f"-e POSTGRESQL_BACKUP_USER={psql_backup_user}", + f"-e POSTGRESQL_BACKUP_PASSWORD={psql_backup_password}", + f"-e POSTGRESQL_ADMIN_PASSWORD={psql_admin_password}", + f"-v {temp_file.name}:{mount_point}:z,ro", + ], + ) + cip, cid = self.app_image.get_cip_cid(cid_file_name=cid_backup) + assert cip and cid + self.check_psql_connection(cip, psql_backup_user, psql_backup_password) + + def check_psql_connection(self, cip, psql_user, psql_password): + """ + Check the PostgreSQL connection. + Check also connection to the backup database. + """ + assert self.app_image.test_db_connection( + container_ip=cip, username=psql_user, password=psql_password + ) + assert self.app_image.test_db_connection( + container_ip=cip, + username=psql_user, + password=psql_password, + database="backup", + ) From a1894bcd5596e18ea45742cee9d0f86d408ab615 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:34:34 +0100 Subject: [PATCH 03/10] Add testing suite test_container_configuration.py run_container_creation_tests -> test_container_configuration.py run_test_cfg_hook -> test_container_configuration.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_configuration.py | 299 +++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 test/test_container_configuration.py diff --git a/test/test_container_configuration.py b/test/test_container_configuration.py new file mode 100644 index 00000000..20e3ac23 --- /dev/null +++ b/test/test_container_configuration.py @@ -0,0 +1,299 @@ +import tempfile +import pytest + +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.utils import ContainerTestLibUtils +from container_ci_suite.engines.database import DatabaseWrapper + +from conftest import VARS + +volume_dir = tempfile.mkdtemp(prefix="/tmp/psql-volume-dir") +ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:-wx {volume_dir}", + ] +) + + +class TestPostgreSQLInvalidConfigurations: + """ + Test PostgreSQL container invalid configurations tests. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db.cleanup() + + def test_container_creation_fails(self): + """ + Test container creation fails with no arguments. + """ + assert self.db.assert_container_creation_fails( + cid_file_name="creation_fails", container_args=[], command="" + ) + + @pytest.mark.parametrize( + "psql_user, psql_password, psql_database, psql_admin_password", + [ + ( + "user", + "pass", + "", + "", + ), + ( + "user", + "pass", + "", + "admin_pass", + ), + ( + "user", + "", + "db", + "", + ), + ( + "user", + "", + "db", + "admin_pass", + ), + ( + "", + "pass", + "db", + "", + ), + ( + "", + "pass", + "db", + "admin_pass", + ), + (VARS.VERY_LONG_IDENTIFIER, "pass", "db", ""), + (VARS.VERY_LONG_IDENTIFIER, "pass", "db", "admin_pass"), + ( + "user", + "pass", + VARS.VERY_LONG_IDENTIFIER, + "", + ), + ( + "user", + "pass", + VARS.VERY_LONG_IDENTIFIER, + "admin_pass", + ), + ], + ) + def test_try_image_invalid_combinations( + self, psql_user, psql_password, psql_database, psql_admin_password + ): + """ + Test container creation fails with invalid combinations of arguments. + """ + cid_file_name = "try_image_invalid_combinations" + psql_user_arg = f"-e POSTGRESQL_USER={psql_user}" if psql_user else "" + psql_password_arg = ( + f"-e POSTGRESQL_PASSWORD={psql_password}" if psql_password else "" + ) + psql_database_arg = ( + f"-e POSTGRESQL_DATABASE={psql_database}" if psql_database else "" + ) + psql_admin_password_arg = ( + f"-e POSTGRESQL_ADMIN_PASSWORD={psql_admin_password}" + if psql_admin_password + else "" + ) + assert self.db.assert_container_creation_fails( + cid_file_name=cid_file_name, + container_args=[ + psql_user_arg, + psql_password_arg, + psql_database_arg, + psql_admin_password_arg, + ], + command="", + ) + + +class TestPostgreSQLValidConfigurations: + """ + Test PostgreSQL container valid configurations tests. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db.cleanup() + + @pytest.mark.parametrize( + "psql_user, psql_password, psql_database, psql_admin_password", + [ + [ + "user", + "pass", + "db", + "admin_pass", + ], + [ + "user", + "pass", + "9invalid", + "admin_pass", + ], + [ + "user", + "pass", + "db", + "", + ], + [ + "", + "", + "", + "the @password", + ], + [ + "the user", + "the pass", + "the db", + "", + ], + ], + ) + def test_correct_configuration_tests( + self, + psql_user, + psql_password, + psql_database, + psql_admin_password, + ): + """ + Test correct configuration combinations for PostgreSQL container. + """ + psql_user_arg = f'-e POSTGRESQL_USER="{psql_user}"' if psql_user else "" + psql_password_arg = ( + f'-e POSTGRESQL_PASSWORD="{psql_password}"' if psql_password else "" + ) + psql_database_arg = ( + f'-e POSTGRESQL_DATABASE="{psql_database}"' if psql_database else "" + ) + psql_admin_password_arg = ( + f'-e POSTGRESQL_ADMIN_PASSWORD="{psql_admin_password}"' + if psql_admin_password + else "" + ) + container_args = [ + psql_user_arg, + psql_password_arg, + psql_database_arg, + psql_admin_password_arg, + ] + assert self.db.assert_container_creation_succeeds( + container_args=container_args, + command="", + ) + cid_file_name = "cid_success_test" + assert self.db.create_container( + cid_file_name=cid_file_name, + container_args=container_args, + command="", + ) + cip, cid = self.db.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + if psql_user and psql_password: + assert self.db.test_db_connection( + container_ip=cip, + username=psql_user, + password=psql_password, + database=psql_database, + ) + if psql_admin_password: + assert self.db.test_db_connection( + container_ip=cip, + username="postgres", + password=psql_admin_password, + database=psql_database, + ) + + +class TestPostgreSQLBufferHooks: + """ + Test PostgreSQL container buffer hooks tests. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db.cleanup() + + def test_configuration_hook(self): + """ + Test buffer hook configurations. + """ + self.shared_buffer_test("32MB") + self.shared_buffer_test("113MB") + self.shared_buffer_test("111MB") + + def shared_buffer_test(self, shared_buffer_value): + """ + Test buffer hook configurations. + Args: + shared_buffer_value: The value of the shared buffer to test. + Steps: + 1. Create a container with the given shared buffer value. + 2. Wait for the database to be ready. + 3. Check if the shared buffer value is set correctly. + """ + cid_file_name = f"test_pg_hook_{shared_buffer_value}" + container_args = [ + "-e POSTGRESQL_ADMIN_PASSWORD=password", + f"-e POSTGRESQL_SHARED_BUFFERS={shared_buffer_value}", + f"-v {volume_dir}:/opt/app-root/src:Z", + ] + + assert self.db.create_container( + cid_file_name=cid_file_name, + container_args=container_args, + command="", + ) + cip, cid = self.db.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd='psql -tA -c "SHOW shared_buffers;"', + ) + assert shared_buffer_value in output, ( + f"Shared buffers should be {shared_buffer_value}, but is {output}" + ) From c3bdf8b1b57de3b95e7ef37bd6c10a855951e336 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:35:42 +0100 Subject: [PATCH 04/10] Add testing suite for extensions `test_container_extensions.py` run_pgaudit_test -> test_container_extensions.py run_pgvector_test -> test_container_extensions.py run_env_extension_load_test -> test_container_extensions.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_extensions.py | 199 ++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 test/test_container_extensions.py diff --git a/test/test_container_extensions.py b/test/test_container_extensions.py new file mode 100644 index 00000000..7af23969 --- /dev/null +++ b/test/test_container_extensions.py @@ -0,0 +1,199 @@ +import re +import os +import tempfile + +from time import sleep +from pathlib import Path + +from container_ci_suite.container_lib import ( + ContainerTestLib, + ContainerTestLibUtils, +) +from container_ci_suite.engines.database import DatabaseWrapper +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper +from container_ci_suite.utils import get_file_content + +import pytest + +from conftest import VARS + +pg_audit_volume_dir = tempfile.mkdtemp(prefix="/tmp/psql-pgaudit-volume-dir") +assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:-wx {pg_audit_volume_dir}", + ] +) + +pg_vector_volume_dir = tempfile.mkdtemp(prefix="/tmp/psql-pgvector-volume-dir") +assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:-wx {pg_vector_volume_dir}", + ] +) + + +class TestPostgreSQLPluginContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db.cleanup() + + @pytest.mark.parametrize( + "env_load", + [ + (""), + ("-e POSTGRESQL_EXTENSIONS=pgaudit -e POSTGRESQL_LIBRARIES=pgaudit"), + ], + ) + def test_pgaudit_extension_installation(self, env_load): + """ + Test pgaudit extension installation. + Steps are: + 1. Create a container with the pgaudit extension enabled. + 2. Wait for the database to be ready. + 3. Check if the pgaudit extension is loaded. + 4. Execute the SQL commands to enable the pgaudit extension. + 5. Check if the SQL commands executed successfully. + 6. Check if the logs contain the expected logs. + """ + if VARS.VERSION in ["9.6", "10", "11"]: + pytest.skip("pgaudit not expected, test skipped.") + cid_file_name = "test_pg_pgaudit" + config_dir = tempfile.mkdtemp(prefix="/tmp/psql-pgaudit-volume") + sql_cmd1 = "SET pgaudit.log = 'read, ddl';\nCREATE DATABASE pgaudittest;" + sql_cmd2 = "SET pgaudit.log = 'read, ddl';\nCREATE TABLE account \ + (id int, name text, password text, description text);" + sql_cmd3 = "INSERT INTO account (id, name, password, description) \ + VALUES (1, 'user1', 'HASH1', 'blah, blah');\nSELECT * FROM account;" + ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -R -m u:26:rwx {config_dir}", + f"cp -r {VARS.TEST_DIR}/examples/pgaudit/* {config_dir}/", + f"setfacl -R -m u:26:rwx {config_dir}", + f"echo '{sql_cmd1}' > {config_dir}/enable-extension.sql", + f"echo '{sql_cmd2}' > {config_dir}/insert-data.sql", + f"echo '{sql_cmd3}' >> {config_dir}/insert-data.sql", + f"cat {config_dir}/enable-extension.sql", + f"cat {config_dir}/insert-data.sql", + ] + ) + container_args = [ + env_load, + "-e POSTGRESQL_ADMIN_PASSWORD=password", + f"-v {config_dir}:/opt/app-root/src:Z", + f"-v {pg_audit_volume_dir}:/var/lib/pgsql/data:Z", + ] + + assert self.db.create_container( + cid_file_name=cid_file_name, + container_args=container_args, + command="", + ) + cip, cid = self.db.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd='psql -tA -c "SHOW shared_preload_libraries;"', + ) + assert "pgaudit" in output, ( + f"pgaudit should be in the shared_preload_libraries, but is {output}" + ) + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + output = PodmanCLIWrapper.call_podman_command( + cmd=f'exec {cid} bash -c "psql < /opt/app-root/src/enable-extension.sql"', + ) + output = PodmanCLIWrapper.call_podman_command( + cmd=f'exec {cid} bash -c "psql < /opt/app-root/src/insert-data.sql"', + ) + sleep(1) + log_files_to_check = [] + for f in os.listdir(Path(pg_audit_volume_dir) / "userdata" / "log"): + if f.startswith("postgresql-"): + log_files_to_check.append( + Path(pg_audit_volume_dir) / "userdata" / "log" / f + ) + for f in log_files_to_check: + output = get_file_content( + filename=f, + ) + words_to_check = [ + "AUDIT: SESSION,.*,.*,DDL,CREATE DATABASE,,,CREATE DATABASE pgaudittest", + "AUDIT: SESSION,.*,.*,READ,SELECT,,,SELECT", + ] + for word in words_to_check: + assert re.search(word, output), ( + f"{word} should be in the output, but is {output}" + ) + + def test_pgvector(self): + """ + Test pgvector installation. + Steps are: + 1. Create a container with the pgvector extension enabled. + 2. Wait for the database to be ready. + 3. Check if the pgvector extension is loaded. + 4. Execute the SQL commands to enable the pgvector extension. + 5. Check if the SQL commands executed successfully. + """ + if VARS.VERSION in ["11", "12", "13", "15"]: + pytest.skip("pgvector not expected on this version, test skipped.") + if VARS.OS == "rhel8": + pytest.skip("pgvector not expected on this OS, test skipped.") + cid_file_name = "test_pg_pgvector" + config_dir = tempfile.mkdtemp(prefix="/tmp/psql-pgvector-volume") + sql_cmd = "CREATE EXTENSION vector;\nCREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));" + ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"cp -r {VARS.TEST_DIR}/examples/pgvector/* {config_dir}/", + f"setfacl -R -m u:26:rwx {config_dir}", + f"echo '{sql_cmd}' > {config_dir}/enable-vector.sql", + ] + ) + container_args = [ + "-e POSTGRESQL_ADMIN_PASSWORD=password", + f"-v {config_dir}:/opt/app-root/src:Z", + f"-v {pg_vector_volume_dir}:/var/lib/pgsql/data:Z", + ] + + assert self.db.create_container( + cid_file_name=cid_file_name, + container_args=container_args, + command="", + ) + cip, cid = self.db.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd='psql -tA -c "SHOW shared_preload_libraries;"', + ) + assert "vector" in output, ( + f"pgaudit should be in the shared_preload_libraries, but is {output}" + ) + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + output = PodmanCLIWrapper.call_podman_command( + cmd=f'exec {cid} bash -c "psql < /opt/app-root/src/enable-vector.sql"', + ) + sleep(1) + assert output From 6da50e5b8d4d44798e767f721be32340e2f7a086 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:36:51 +0100 Subject: [PATCH 05/10] Add general tests to test_container_generall.py run_general_tests -> test_container_general.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_general.py | 215 +++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 test/test_container_general.py diff --git a/test/test_container_general.py b/test/test_container_general.py new file mode 100644 index 00000000..1e5df38d --- /dev/null +++ b/test/test_container_general.py @@ -0,0 +1,215 @@ +import re +import pytest + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper +from container_ci_suite.container_lib import DatabaseWrapper + +from conftest import VARS + + +class TestPostgreSQLGeneralContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db_image = ContainerTestLib( + image_name=VARS.IMAGE_NAME, db_type="postgresql" + ) + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db_image.cleanup() + + @pytest.mark.parametrize( + "container_args, psql_user, psql_password, psql_database, root_password, test_name", + [ + ("", "user", "pass", "", "", "no_admin"), + ("", "user1", "pass1", "", "r00t", "admin"), + ("", "", "", "postgres", "r00t", "only_admin"), + ("-u 12345", "user2", "pass", "", "", "no_admin_altuid"), + ("-u 12345", "user3", "pass1", "", "r00t", "admin_altuid"), + ("-u 12345", "", "", "postgres", "r00t", "only_admin_altuid"), + ], + ) + def test_run( + self, + container_args, + psql_user, + psql_password, + psql_database, + root_password, + test_name, + ): + """ + Test PostgreSQL container general usage. + Steps are: + 1. Create a container with the given arguments. + 2. Check if the container is created successfully + 3. Check if the database connection works. + 4. Check if the PostgreSQL version is correct. + 5. Check if the login access works. + 6. Check if the local access works. + 7. Test the database creation. + """ + psql_max_connections = 42 if test_name == "no_admin" else 100 + psql_max_prepared_transactions = 42 if test_name == "no_admin" else 0 + psql_shared_buffers = "64MB" if test_name == "no_admin" else "32MB" + expected_success = expected_admin_success = False + psql_user_arg = psql_pwd_arg = db_name_arg = admin_root_password_arg = "" + if psql_user != "": + psql_user_arg = f"-e POSTGRESQL_USER={psql_user}" + expected_success = True + if psql_password: # empty password is allowed + psql_pwd_arg = f"-e POSTGRESQL_PASSWORD={psql_password}" + if psql_user and psql_password: + db_name_arg = "-e POSTGRESQL_DATABASE=db" + if root_password == "r00t": + admin_root_password_arg = f"-e POSTGRESQL_ADMIN_PASSWORD={root_password}" + expected_admin_success = True + container_all_args = [ + psql_user_arg, + psql_pwd_arg, + db_name_arg, + admin_root_password_arg, + f"-e POSTGRESQL_MAX_CONNECTIONS={psql_max_connections}", + f"-e POSTGRESQL_MAX_PREPARED_TRANSACTIONS={psql_max_prepared_transactions}", + f"-e POSTGRESQL_SHARED_BUFFERS={psql_shared_buffers}", + f"{container_args}", + ] + cid_file_name = test_name + assert self.db_image.create_container( + cid_file_name=cid_file_name, container_args=container_all_args + ) + cip, cid = self.db_image.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + if root_password: + assert self.db_image.test_db_connection( + container_ip=cip, + username="postgres", + password=root_password, + database="postgres", + max_attempts=10, + ) + else: + assert self.db_image.test_db_connection( + container_ip=cip, + username=psql_user, + password=psql_password, + max_attempts=10, + ) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="psql --version", + ) + assert VARS.VERSION in output + login_access = True + if psql_database == "": + psql_database = "db" + for user, pwd, ret_value in [ + (psql_user, psql_password, expected_success), + (psql_user, f"{psql_password}_foo", False), + ("postgres", root_password, expected_admin_success), + ("postgres", f"{root_password}_foo", False), + ]: + test_assert = self.db_image.db_lib.assert_login_access( + container_ip=cip, + username=user, + password=pwd, + expected_success=ret_value, + database=psql_database, + ) + if not test_assert: + print( + f"Login access failed for {user}:{pwd} with expected success {ret_value}" + ) + login_access = False + assert login_access + assert self.db_image.db_lib.assert_local_access(container_id=cid) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="cat /var/lib/pgsql/openshift-custom-postgresql.conf", + ) + + rows = [ + rf"max_connections\s*=\s*{psql_max_connections}", + rf"max_prepared_transactions\s*=\s*{psql_max_prepared_transactions}", + rf"shared_buffers\s*=\s*{psql_shared_buffers}", + ] + for row in rows: + assert re.search(row, output), f"Row {row} not found in {output}" + # test_postgresql + if test_name == "admin": + assert self.db_api.run_sql_command( + container_ip=cip, + username="postgres", + password=root_password, + container_id=VARS.IMAGE_NAME, + database="postgres", + sql_cmd="-At -c 'CREATE EXTENSION \"uuid-ossp\";'", + ) + if psql_password == "": + psql_password = root_password + + self.database_test(cip, psql_user, psql_password, psql_database) + + def database_test(self, cip, psql_user, psql_password, psql_database): + """ + Test PostgreSQL database creation and data insertion is valid. + Steps are: + 1. Create a table with the given name and columns. + 2. Insert data into the table. + 3. Select data from the table. + 4. Drop the table. + """ + self.db_api.run_sql_command( + container_ip=cip, + username=psql_user, + password=psql_password, + container_id=VARS.IMAGE_NAME, + database=psql_database, + sql_cmd='-At -c "CREATE TABLE tbl (a integer, b integer);"', + ) + + self.db_api.run_sql_command( + container_ip=cip, + username=psql_user, + password=psql_password, + container_id=VARS.IMAGE_NAME, + database=psql_database, + sql_cmd=[ + '-At -c "INSERT INTO tbl VALUES (1, 2);"', + '-At -c "INSERT INTO tbl VALUES (3, 4);"', + '-At -c "INSERT INTO tbl VALUES (5, 6);"', + ], + ) + output = self.db_api.run_sql_command( + container_ip=cip, + username=psql_user, + password=psql_password, + container_id=VARS.IMAGE_NAME, + database=psql_database, + sql_cmd='-At -c "SELECT * FROM tbl;"', + ) + expected_db_output = [ + "1|2", + "3|4", + "5|6", + ] + for row in expected_db_output: + assert re.search(row, output), f"Row {row} not found in {output}" + self.db_api.run_sql_command( + container_ip=cip, + username=psql_user, + password=psql_password, + container_id=VARS.IMAGE_NAME, + database=psql_database, + sql_cmd='-At -c "DROP TABLE tbl;"', + ) From 169bf2d6355eb77b68f005ce6d307b82e83e8d9a Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:37:28 +0100 Subject: [PATCH 06/10] Add logging test for Postgresql run_logging_test -> test_container_logging.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_logging.py | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/test_container_logging.py diff --git a/test/test_container_logging.py b/test/test_container_logging.py new file mode 100644 index 00000000..c5b5fd1d --- /dev/null +++ b/test/test_container_logging.py @@ -0,0 +1,86 @@ +import re + +from pathlib import Path + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.container import ContainerTestLibUtils +from container_ci_suite.engines.database import DatabaseWrapper +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper +from container_ci_suite.utils import tempfile + +from conftest import VARS + + +class TestPostgreSQLLoggingContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.db.cleanup() + + def test_logging_destination(self): + """ + Test logging destination. + """ + cid_file_name = "test_pg_logging" + logging_dir = tempfile.mkdtemp(prefix="/tmp/psql-logging-volume-dir") + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:-wx {logging_dir}", + ] + ) + container_args = [ + "-e POSTGRESQL_ADMIN_PASSWORD=password", + "-e POSTGRESQL_LOG_DESTINATION=/dev/stderr", + f"-v {logging_dir}:/var/lib/pgsql/data:Z", + ] + + assert self.db.create_container( + cid_file_name=cid_file_name, + container_args=container_args, + command="", + ) + cip, cid = self.db.get_cip_cid(cid_file_name=cid_file_name) + assert cip and cid + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + + output = PodmanCLIWrapper.call_podman_command( + cmd=f"exec {cid} bash -c \"psql -tA -c 'SHOW log_destination;'\"", + ) + assert "stderr" in output, ( + f"stderr should be in the log_destination, but is {output}" + ) + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + + PodmanCLIWrapper.call_podman_command( + cmd=f"exec {cid} bash -c 'psql -U nonexistent'", + ignore_error=True, + ) + + assert self.db_api.wait_for_database( + container_id=cid, command="/usr/libexec/check-container" + ) + logs = PodmanCLIWrapper.podman_logs( + container_id=cid, + ) + assert re.search('FATAL:\\s*role "nonexistent" does not exist', logs), ( + "ERROR: the container log does not include expected error message" + ) + assert not (Path(logging_dir) / "userdata" / "log").exists(), ( + "ERROR: the traditional log file should not exist" + ) From 288311dfbc5d866e7581000a75c0b4d62f3ed590 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:38:10 +0100 Subject: [PATCH 07/10] Add password test suite run_change_password_test -> test_container_password Signed-off-by: Petr "Stone" Hracek --- test/test_container_password.py | 180 ++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 test/test_container_password.py diff --git a/test/test_container_password.py b/test/test_container_password.py new file mode 100644 index 00000000..cd78c126 --- /dev/null +++ b/test/test_container_password.py @@ -0,0 +1,180 @@ +import tempfile +import re + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.container_lib import DatabaseWrapper +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + +pwd_dir = tempfile.mkdtemp(prefix="/tmp/psql-pwd") +ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"setfacl -m u:26:-wx {pwd_dir}", + ] +) + + +class TestPostgreSQLPasswordChangeContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.pwd_change = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.pwd_change.set_new_db_type(db_type="postgresql") + self.dw_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.pwd_change.cleanup() + + def test_password_change(self): + """ + Test password change. + """ + pwd_file_name = "test_password_change" + volume_options = f"-v {pwd_dir}:/var/lib/pgsql/data:Z" + database = "db" + username = "user" + password = "password" + admin_password = "adminPassword" + assert self.pwd_change.create_container( + cid_file_name=pwd_file_name, + container_args=[ + f"-e POSTGRESQL_USER={username}", + f"-e POSTGRESQL_PASSWORD={password}", + f"-e POSTGRESQL_DATABASE={database}", + f"-e POSTGRESQL_ADMIN_PASSWORD={admin_password}", + volume_options, + ], + ) + cip1, cid1 = self.pwd_change.get_cip_cid(cid_file_name=pwd_file_name) + assert cip1 and cid1 + assert self.pwd_change.test_db_connection( + container_ip=cip1, + username=username, + password=password, + max_attempts=10, + ) + for user, pwd in [ + (username, password), + ("postgresql", admin_password), + ]: + assert self.pwd_change.db_lib.assert_login_access( + container_ip=cip1, + username=user, + password=pwd, + expected_success=True, + ) + assert self.pwd_change.test_db_connection( + container_ip=cip1, username=username, password=password + ) + # test_postgresql + self.dw_api.run_sql_command( + container_ip=cip1, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + database=database, + sql_cmd='-At -c "CREATE TABLE tbl (a integer, b integer);"', + ) + + self.dw_api.run_sql_command( + container_ip=cip1, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + database=database, + sql_cmd=[ + '-At -c "INSERT INTO tbl VALUES (1, 2);"', + '-At -c "INSERT INTO tbl VALUES (3, 4);"', + '-At -c "INSERT INTO tbl VALUES (5, 6);"', + ], + ) + output = self.dw_api.run_sql_command( + container_ip=cip1, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + database=database, + sql_cmd='-At -c "SELECT * FROM tbl;"', + ) + expected_db_output = [ + "1|2", + "3|4", + "5|6", + ] + for row in expected_db_output: + assert re.search(row, output), f"Row {row} not found in {output}" + + PodmanCLIWrapper.call_podman_command(cmd=f"kill {cid1}") + PodmanCLIWrapper.call_podman_command(cmd=f"rm {cid1}") + pwd_file_name_new = "test_password_change_new_password" + new_password = f"NEW_{password}" + new_admin_password = f"NEW_{admin_password}" + assert self.pwd_change.create_container( + cid_file_name=pwd_file_name_new, + container_args=[ + f"-e POSTGRESQL_USER={username}", + f"-e POSTGRESQL_PASSWORD={new_password}", + f"-e POSTGRESQL_DATABASE={database}", + f"-e POSTGRESQL_ADMIN_PASSWORD={new_admin_password}", + volume_options, + ], + ) + cip_new, cid_new = self.pwd_change.get_cip_cid(cid_file_name=pwd_file_name_new) + assert cip_new and cid_new + + assert self.pwd_change.test_db_connection( + container_ip=cip_new, + username=username, + password=new_password, + max_attempts=10, + ) + login_access = True + for user, pwd, ret_value in [ + (username, new_password, True), + (username, password, False), + ("postgres", new_admin_password, True), + ("postgres", "admin_password", False), + ]: + test_assert = self.pwd_change.db_lib.assert_login_access( + container_ip=cip_new, + username=user, + password=pwd, + expected_success=ret_value, + ) + if not test_assert: + print( + f"Login access failed for {user}:{pwd} with expected success {ret_value}" + ) + login_access = False + assert login_access + self.dw_api.run_sql_command( + container_ip=cip_new, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + ) + output = self.dw_api.run_sql_command( + container_ip=cip_new, + username=username, + password=new_password, + container_id=VARS.IMAGE_NAME, + database=database, + sql_cmd='-At -c "SELECT * FROM tbl;"', + ) + expected_db_output = [ + "1|2", + "3|4", + "5|6", + ] + for row in expected_db_output: + assert re.search(row, output), f"Row {row} not found in {output}" From 59ad165c815e945e6b86c0b8b9018c8a630d74ac Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:38:49 +0100 Subject: [PATCH 08/10] Add test suite for replication run_replication_test -> test_container_replication.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_replication.py | 110 +++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/test_container_replication.py diff --git a/test/test_container_replication.py b/test/test_container_replication.py new file mode 100644 index 00000000..7ae00cb6 --- /dev/null +++ b/test/test_container_replication.py @@ -0,0 +1,110 @@ +import re +from time import sleep + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.database import DatabaseWrapper + +from conftest import VARS + + +class TestPostgreSQLReplicationContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.replication_db = ContainerTestLib( + image_name=VARS.IMAGE_NAME, db_type="postgresql" + ) + self.db_wrapper_api = DatabaseWrapper( + image_name=VARS.IMAGE_NAME, db_type="postgresql" + ) + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.replication_db.cleanup() + + def test_replication(self): + """ + Test replication. + """ + database = "postgres" + master_user = "master" + master_password = "master" + master_hostname = "postgresql-master" + master_cid_name = "master-basic" + slave_cid_name = "slave-basic" + + container_args = [ + "-e POSTGRESQL_ADMIN_PASSWORD=pass", + f"-e POSTGRESQL_MASTER_USER={master_user}", + f"-e POSTGRESQL_MASTER_PASSWORD={master_password}", + ] + # Run the PostgreSQL master + assert self.replication_db.create_container( + cid_file_name=master_cid_name, + container_args=container_args, + command="run-postgresql-master", + ) + # Run the PostgreSQL replica + master_cip, master_cid = self.replication_db.get_cip_cid( + cid_file_name=master_cid_name + ) + assert master_cip and master_cid + assert self.db_wrapper_api.wait_for_database( + container_id=master_cid, + command="/usr/libexec/check-container", + ) + container_args += [ + f"--add-host {master_hostname}:{master_cip}", + f"-e POSTGRESQL_MASTER_IP={master_hostname}", + ] + assert self.replication_db.create_container( + cid_file_name=slave_cid_name, + container_args=container_args, + command="run-postgresql-slave", + ) + slave_cip, slave_cid = self.replication_db.get_cip_cid( + cid_file_name=slave_cid_name + ) + assert slave_cip and slave_cid + assert self.db_wrapper_api.wait_for_database( + container_id=slave_cid, + command="/usr/libexec/check-container", + ) + output = self.db_wrapper_api.run_sql_command( + container_ip=master_cip, + username=master_user, + password=master_password, + database=database, + sql_cmd="-c 'select client_addr from pg_stat_replication;'", + expected_output=f"{slave_cip}", + ) + assert slave_cip in output, ( + f"Replica {slave_cip} not found in MASTER {master_cip}" + ) + # Test the replication + output = self.db_wrapper_api.run_sql_command( + container_ip=master_cip, + username=master_user, + password=master_password, + database=database, + sql_cmd="-c 'CREATE TABLE t1 (a integer); INSERT INTO t1 VALUES (24);'", + ) + # let's wait for the table to be created and available for replication + sleep(3) + output = self.db_wrapper_api.run_sql_command( + container_ip=slave_cip, + username=master_user, + password=master_password, + database=database, + sql_cmd="-At -c 'select * from t1;'", + ) + assert re.search(r"^24", output), ( + f"Value 24 not found in REPLICA {slave_cip} for table t1" + ) From 7eedd725b24df357cae2bfdfa6f0f94a5e654971 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:39:12 +0100 Subject: [PATCH 09/10] Add test suite for SSL functionality run_s2i_bake_data_test -> test_container_ssl.py run_s2i_enable_ssl_test -> test_container_ssl.py Signed-off-by: Petr "Stone" Hracek --- test/test_container_ssl.py | 137 +++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 test/test_container_ssl.py diff --git a/test/test_container_ssl.py b/test/test_container_ssl.py new file mode 100644 index 00000000..5b49a637 --- /dev/null +++ b/test/test_container_ssl.py @@ -0,0 +1,137 @@ +import re + +from pathlib import Path + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.database import DatabaseWrapper + +from conftest import VARS + + +def build_s2i_app(app_path: Path) -> ContainerTestLib: + container_lib = ContainerTestLib(image_name=VARS.IMAGE_NAME, db_type="postgresql") + app_name = app_path.name + s2i_app = container_lib.build_as_df( + app_path=app_path, + s2i_args="--pull-policy=never", + src_image=VARS.IMAGE_NAME, + dst_image=f"{VARS.IMAGE_NAME}-{app_name}", + ) + return s2i_app + + +class TestPostgreSQLS2ISSLContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.ssl_db = build_s2i_app(app_path=VARS.TEST_DIR / "examples" / "enable-ssl") + self.ssl_db.db_lib.db_type = "postgresql" + self.dw_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.ssl_db.cleanup() + + def test_ssl(self): + """ + Test SSL. + """ + cid_ssl_name = "enable-ssl-test" + admin_password = "password" + + assert self.ssl_db.create_container( + cid_file_name=cid_ssl_name, + container_args=[ + f"-e POSTGRESQL_ADMIN_PASSWORD={admin_password}", + ], + ) + ssl_cip, ssl_cid = self.ssl_db.get_cip_cid(cid_file_name=cid_ssl_name) + assert ssl_cip and ssl_cid + assert self.dw_api.wait_for_database( + container_id=ssl_cid, + command="/usr/libexec/check-container", + ) + assert self.dw_api.assert_login_access( + container_ip=ssl_cip, + username="postgres", + password=admin_password, + database="postgres", + expected_success=True, + ) + + output = self.dw_api.postgresql_cmd( + container_ip=ssl_cip, + container_id=VARS.IMAGE_NAME, + username="postgres", + password=admin_password, + database="postgres", + uri_params={"sslmode": "require"}, + sql_command="-At -c 'SELECT 1;'", + ) + assert re.search(r"1", output), f"1 not found in {output}" + + +class TestPostgreSQLS2IBakeDataContainer: + """ + Test PostgreSQL container configuration. + """ + + def setup_method(self): + """ + Setup the test environment. + """ + self.ssl_db = build_s2i_app( + app_path=VARS.TEST_DIR / "examples" / "s2i-dump-data" + ) + self.ssl_db.db_lib.db_type = "postgresql" + self.dw_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="postgresql") + + def teardown_method(self): + """ + Teardown the test environment. + """ + self.ssl_db.cleanup() + + def test_ssl(self): + """ + Test SSL. + """ + cid_ssl_name = "bake-data-test" + admin_password = "password" + + assert self.ssl_db.create_container( + cid_file_name=cid_ssl_name, + container_args=[ + f"-e POSTGRESQL_ADMIN_PASSWORD={admin_password}", + ], + ) + ssl_cip, ssl_cid = self.ssl_db.get_cip_cid(cid_file_name=cid_ssl_name) + assert ssl_cip and ssl_cid + assert self.dw_api.wait_for_database( + container_id=ssl_cid, + command="/usr/libexec/check-container", + ) + assert self.dw_api.assert_login_access( + container_ip=ssl_cip, + username="postgres", + password=admin_password, + database="postgres", + expected_success=True, + ) + + output = self.dw_api.postgresql_cmd( + container_ip=ssl_cip, + container_id=VARS.IMAGE_NAME, + username="postgres", + password=admin_password, + database="postgres", + sql_command="-At -c 'SELECT * FROM test;'", + ) + assert re.search(r"hello world", output), f"hello world not found in {output}" From cc0693f607d28558cf090ac03bb5fef4f94a0d7f Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 23 Mar 2026 15:49:20 +0100 Subject: [PATCH 10/10] Fix calling PyTest suite with propoer Python interpreter Signed-off-by: Petr "Stone" Hracek --- test/run-pytest | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/run-pytest b/test/run-pytest index 202bd144..e81fcf67 100755 --- a/test/run-pytest +++ b/test/run-pytest @@ -10,8 +10,9 @@ THISDIR=$(dirname ${BASH_SOURCE[0]}) git show -s -PYTHON_VERSION="3.12" -if [[ ! -f "/usr/bin/python$PYTHON_VERSION" ]]; then - PYTHON_VERSION="3.13" +if python3 -c 'import sys; sys.exit(0 if sys.version_info < (3,13) else 1)'; then + PYTHON_VERSION="3.12" +else + PYTHON_VERSION="3" fi cd "${THISDIR}" && "python${PYTHON_VERSION}" -m pytest -s -rA --showlocals -vv test_container_*.py