fix: ImportExportServersTestCase uses sys.executable + surfaces subprocess errors

Two latent bugs in pgadmin/setup/tests/test_export_import_servers.py
caused this test to fail on macOS / modern Linux distros:

1. The test invoked the subprocesses with a hardcoded "python" via
   os.system. macOS and many modern Linux distros do not provide a
   "python" alias — only "python3" or a venv-specific binary. The
   subprocess fails with "sh: python: command not found", exits non-
   zero, and writes nothing to the dump-servers output file.

   Same root cause that a50a553b0 ("chore: feature tests use
   sys.executable") fixed for AppStarter; this is the same fix in the
   regression test.

2. Both os.system calls redirected stderr to /dev/null. When the
   subprocess failed the empty output file then tripped json.loads
   with the misleading "Expecting value: line 1 column 1 (char 0)"
   instead of surfacing the underlying "command not found" error.

Switch to subprocess.run with a list (so there is no shell quoting and
no command-injection surface), use sys.executable, capture stderr, and
self.fail() with the captured stderr if the subprocess exits non-zero.

Verified: setup.tests goes from 4 pass / 1 fail / 1 skip to 5 pass / 0
fail / 1 skip on PG18.
This commit is contained in:
Ashesh Vashi
2026-05-05 15:03:00 +05:30
parent 9923eefcab
commit 208541cc48
@@ -10,6 +10,8 @@
from pgadmin.utils.route import BaseTestGenerator
import os
import json
import subprocess
import sys
import tempfile
import config
@@ -34,16 +36,37 @@ class ImportExportServersTestCase(BaseTestGenerator):
path = os.path.dirname(__file__)
setup = os.path.realpath(os.path.join(path, "../../../setup.py"))
# Load the servers
os.system(
"python \"%s\" load-servers \"%s\" 2> %s" %
(setup, os.path.join(path, "servers.json"), os.devnull)
# Use sys.executable instead of bare "python" — macOS / many Linux
# distros do not provide a "python" alias, only "python3" or a venv-
# specific binary. Use subprocess.run with a list so there is no
# shell quoting and no command injection surface. Surface the
# subprocess error directly instead of letting a silent failure
# produce an empty file that downstream json.loads then misreports
# as "Expecting value: line 1 column 1".
load_result = subprocess.run(
[sys.executable, setup, "load-servers",
os.path.join(path, "servers.json")],
capture_output=True,
text=True,
check=False,
)
if load_result.returncode != 0:
self.fail(
"load-servers exited {0}: {1}".format(
load_result.returncode, load_result.stderr))
# And dump them again
tf = tempfile.NamedTemporaryFile(delete=False)
os.system("python \"%s\" dump-servers \"%s\" 2> %s" %
(setup, tf.name, os.devnull))
dump_result = subprocess.run(
[sys.executable, setup, "dump-servers", tf.name],
capture_output=True,
text=True,
check=False,
)
if dump_result.returncode != 0:
self.fail(
"dump-servers exited {0}: {1}".format(
dump_result.returncode, dump_result.stderr))
# Compare the JSON files, ignoring servers that exist in our
# generated file but not the test config (they are likely