File: //usr/share/lvemanager-xray/plugins/tests/test_upload_reports_destination.py
"""
Product-behaviour tests for the xray report-upload scripts' destination guard.
These tests pin the post-fix contract of ``upload-reports.sh`` and
``upload-reports-test.sh`` at the repo root: each script accepts exactly one
plain SSH destination and forwards *only* that destination to ``ssh`` ahead of
the fixed ``manage-files rollout`` remote command. An invocation that supplies
no destination, more than one positional, or an option-like destination (one
starting with ``-``) must be rejected before any ``ssh`` is spawned.
The scripts are exercised as black boxes. ``ssh``, ``tar`` and the
``build-utils/build-reports.sh`` build step are replaced with recording stubs
so the test has no real side effects (no network, no build): a stub ``ssh``
writes its argv to a file, letting the test assert positively on what the
script would have run.
Run from the repo root with:
python3 -m unittest discover -s plugins/tests -p 'test_*.py'
or directly:
python3 plugins/tests/test_upload_reports_destination.py
"""
import os
import shutil
import subprocess
import tempfile
import textwrap
import unittest
HERE = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.normpath(os.path.join(HERE, "..", ".."))
# Each script and the remote "subpath" argument it pins on the manage-files
# rollout command. The guard logic is byte-identical across both scripts; we
# parametrise so a regression in either sibling is caught.
SCRIPTS = {
"upload-reports.sh": "xray/reports",
"upload-reports-test.sh": "xray-test/reports",
}
def _write_exec(path, body):
with open(path, "w") as fh:
fh.write(textwrap.dedent(body))
os.chmod(path, 0o755)
class UploadReportsDestinationGuardTests(unittest.TestCase):
"""The upload scripts must forward only a single validated destination."""
def setUp(self):
# The stub interpreters must be executable, so the sandbox lives on the
# repo filesystem (the system temp mount may be mounted noexec). It is
# created beside this test file and removed in tearDown.
self.sandbox = tempfile.mkdtemp(dir=HERE)
self.bin_dir = os.path.join(self.sandbox, "bin")
self.run_dir = os.path.join(self.sandbox, "run")
os.makedirs(self.bin_dir)
os.makedirs(os.path.join(self.run_dir, "dist"))
os.makedirs(os.path.join(self.run_dir, "build-utils"))
# File where the stub ssh records the argv it was invoked with. Its
# absence after a run means ssh was never called.
self.ssh_argv_file = os.path.join(self.sandbox, "ssh_argv")
# Stub the local build step the scripts call by relative path.
_write_exec(
os.path.join(self.run_dir, "build-utils", "build-reports.sh"),
"""\
#!/bin/sh
exit 0
""",
)
# Stub ssh: record argv, succeed. tar: succeed silently.
_write_exec(
os.path.join(self.bin_dir, "ssh"),
"""\
#!/bin/sh
printf '%s\\n' "$@" > "{argv_file}"
exit 0
""".format(argv_file=self.ssh_argv_file),
)
_write_exec(
os.path.join(self.bin_dir, "tar"),
"""\
#!/bin/sh
exit 0
""",
)
def tearDown(self):
shutil.rmtree(self.sandbox, ignore_errors=True)
def _run(self, script_name, *args):
"""Run a copy of the script in the sandbox with stubbed ssh/tar/build.
Returns the CompletedProcess. The script copy runs with the sandbox
bin dir prepended to PATH so it resolves the stub ssh/tar.
"""
script_copy = os.path.join(self.run_dir, script_name)
shutil.copy(os.path.join(REPO_ROOT, script_name), script_copy)
env = dict(os.environ)
env["PATH"] = self.bin_dir + os.pathsep + env.get("PATH", "")
return subprocess.run(
["bash", script_name, *args],
cwd=self.run_dir,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def _ssh_argv(self):
"""Return the recorded ssh argv as a list, or None if ssh never ran."""
if not os.path.exists(self.ssh_argv_file):
return None
with open(self.ssh_argv_file) as fh:
return [line for line in fh.read().splitlines()]
def test_single_plain_destination_is_forwarded_to_ssh(self):
"""A lone plain destination reaches ssh as the only host token."""
for script_name, subpath in SCRIPTS.items():
with self.subTest(script=script_name):
result = self._run(script_name, "files.example.com")
self.assertEqual(
result.returncode,
0,
msg="stderr=%r" % result.stderr,
)
argv = self._ssh_argv()
self.assertIsNotNone(argv, "ssh was not invoked")
# ssh -T <dest> manage-files rollout --replace <subpath>
self.assertEqual(
argv,
["-T", "files.example.com", "manage-files", "rollout", "--replace", subpath],
)
def test_destination_with_at_and_port_is_forwarded_verbatim(self):
"""A realistic user@host destination is accepted and passed through."""
for script_name, subpath in SCRIPTS.items():
with self.subTest(script=script_name):
result = self._run(script_name, "deploy@cdn-host")
self.assertEqual(
result.returncode,
0,
msg="stderr=%r" % result.stderr,
)
argv = self._ssh_argv()
self.assertIsNotNone(argv, "ssh was not invoked")
self.assertEqual(argv[0], "-T")
self.assertEqual(argv[1], "deploy@cdn-host")
def test_option_like_destination_is_rejected_without_invoking_ssh(self):
"""A leading-dash destination is refused before ssh is spawned."""
for script_name in SCRIPTS:
with self.subTest(script=script_name):
result = self._run(script_name, "-oProxyCommand=evil")
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(
self._ssh_argv(),
"ssh must not be invoked for an option-like destination",
)
def test_multiple_arguments_are_rejected_without_invoking_ssh(self):
"""More than one positional is refused before ssh is spawned."""
for script_name in SCRIPTS:
with self.subTest(script=script_name):
result = self._run(script_name, "host", "extra-arg")
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(
self._ssh_argv(),
"ssh must not be invoked for a multi-arg invocation",
)
def test_no_arguments_are_rejected_without_invoking_ssh(self):
"""A missing destination is refused before ssh is spawned."""
for script_name in SCRIPTS:
with self.subTest(script=script_name):
result = self._run(script_name)
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(
self._ssh_argv(),
"ssh must not be invoked with no destination",
)
if __name__ == "__main__":
unittest.main()