Files
Martin Vogel dce5518c56 test(pypi): restore the publication concurrency tests for the single-file runtime
Deleting these with the multi-file runtime set threw away properties that do not
depend on how many files publish: two wrappers racing to publish the SAME cache
still must serialize, a killed publisher still must be reconciled by locked
readiness rather than trusted, a failed publication still must restore the prior
binary, and a complete foreign winner still must never be deleted.

Restored from main and adapted: the staging fixtures publish one binary instead
of binary+integrations+pack, and the assertions that read sidecar bytes are gone
with the sidecars. Each surviving test keeps its lock/race logic intact
(7/10/5/3 assertions respectively, all still reading published binary bytes).

Revert-checked against production, not just green: disabling
_runtime_backup_target_name makes test_publication_failure_restores... fail and
restoring it passes. pypi suite: 28 tests, OK.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 20:21:39 +02:00

838 lines
33 KiB
Python

import errno
import hashlib
import json
import os
import stat
import subprocess
import sys
import tarfile
import tempfile
import threading
import time
import unittest
import zipfile
from pathlib import Path, PureWindowsPath
from unittest import mock
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PACKAGE_ROOT / "src"))
from codebase_memory_mcp import _cli # noqa: E402
class BinarySelectionTests(unittest.TestCase):
"""One binary ships per platform: the cached file is the executed file."""
def test_windows_executes_the_cached_binary(self):
binary = Path("cache") / "0.8.1" / _cli._WINDOWS_BINARY_NAME
self.assertEqual(_cli._execution_path(binary, "win32"), binary)
def test_non_windows_executes_the_cached_binary(self):
binary = Path("cache") / "0.8.1" / "codebase-memory-mcp"
self.assertEqual(_cli._execution_path(binary, "linux"), binary)
def test_cache_sensitive_mutation_classification_is_explicit(self):
self.assertEqual(_cli._runtime_mutation_action(["update"]), "update")
self.assertEqual(
_cli._runtime_mutation_action(["uninstall", "--yes"]),
"uninstall",
)
self.assertEqual(
_cli._runtime_mutation_action(["install", "--yes"]), "install"
)
self.assertIsNone(_cli._runtime_mutation_action(["cli", "update"]))
self.assertIsNone(_cli._runtime_mutation_action(["daemon", "update"]))
def test_wrapper_uninstall_never_defaults_to_its_cache_binary(self):
linux_home = Path("fixtures") / "linux-home"
windows_local_app_data = Path("fixtures") / "windows-local-app-data"
defaults = (
(
"linux",
linux_home / ".local" / "bin",
mock.patch.object(Path, "home", return_value=linux_home),
),
(
"win32",
windows_local_app_data
/ "Programs"
/ "codebase-memory-mcp",
mock.patch.dict(
os.environ,
{"LOCALAPPDATA": str(windows_local_app_data)},
),
),
)
for platform_name, expected, path_fixture in defaults:
with self.subTest(platform=platform_name), mock.patch.object(
_cli.sys, "platform", platform_name
), path_fixture:
self.assertEqual(
_cli._native_args(["uninstall", "--yes"]),
["uninstall", "--yes", "--dir", str(expected)],
)
explicit_dir = Path("fixtures") / "explicit-install"
for platform_name in ("linux", "win32"):
with self.subTest(
platform=platform_name, explicit_dir=True
), mock.patch.object(_cli.sys, "platform", platform_name):
self.assertEqual(
_cli._native_args(
["uninstall", "--dir", str(explicit_dir)]
),
["uninstall", "--dir", str(explicit_dir)],
)
def test_release_archives_carry_no_sidecars(self):
# The binary is self-contained; a returning sidecar would silently make
# `pip install` reject every archive against this exact-set allowlist.
for names in (_cli._WINDOWS_ARCHIVE_NAMES, _cli._UNIX_ARCHIVE_NAMES):
self.assertNotIn("cbm-integrations.json", names)
self.assertFalse([n for n in names if n.startswith("cbm-ui-")])
self.assertEqual(len(names), 4)
def test_update_guidance_uses_pip_and_names_managed_install(self):
with mock.patch.object(_cli.sys, "argv", ["cbm", "update"]), \
mock.patch.object(_cli, "_version", return_value="0.8.1"), \
mock.patch("sys.stderr") as stderr:
with self.assertRaisesRegex(SystemExit, "2"):
_cli.main()
output = "".join(call.args[0] for call in stderr.write.call_args_list)
self.assertIn(
"python -m pip install --upgrade codebase-memory-mcp", output
)
self.assertIn("codebase-memory-mcp install --yes", output)
self.assertNotIn("install.sh", output)
class ProcessLivenessTests(unittest.TestCase):
def test_windows_probe_never_calls_os_kill(self):
with mock.patch.object(_cli.os, "name", "nt"), mock.patch.object(
_cli,
"_windows_process_is_alive",
return_value=True,
create=True,
) as windows_probe, mock.patch.object(
_cli.os,
"kill",
side_effect=AssertionError("Windows must not call os.kill"),
):
self.assertTrue(_cli._process_is_alive(1234))
windows_probe.assert_called_once_with(1234)
def test_windows_wait_result_distinguishes_dead_and_live_processes(self):
cases = (
(_cli._WINDOWS_WAIT_OBJECT_0, False),
(_cli._WINDOWS_WAIT_TIMEOUT, True),
(0xFFFFFFFF, True),
)
for wait_result, expected in cases:
with self.subTest(wait_result=wait_result):
kernel32 = mock.Mock()
kernel32.OpenProcess.return_value = 123
kernel32.WaitForSingleObject.return_value = wait_result
with mock.patch.object(
_cli,
"_windows_process_api",
return_value=(kernel32, mock.Mock()),
):
self.assertEqual(
_cli._windows_process_is_alive(4567), expected
)
kernel32.OpenProcess.assert_called_once_with(
_cli._WINDOWS_SYNCHRONIZE, False, 4567
)
kernel32.WaitForSingleObject.assert_called_once_with(123, 0)
kernel32.CloseHandle.assert_called_once_with(123)
def test_windows_open_failure_only_treats_missing_pid_as_dead(self):
cases = (
(_cli._WINDOWS_ERROR_INVALID_PARAMETER, False),
(5, True),
)
for error, expected in cases:
with self.subTest(error=error):
kernel32 = mock.Mock()
kernel32.OpenProcess.return_value = 0
get_last_error = mock.Mock(return_value=error)
with mock.patch.object(
_cli,
"_windows_process_api",
return_value=(kernel32, get_last_error),
):
self.assertEqual(
_cli._windows_process_is_alive(4567), expected
)
get_last_error.assert_called_once_with()
kernel32.WaitForSingleObject.assert_not_called()
kernel32.CloseHandle.assert_not_called()
def test_windows_api_failures_are_conservatively_live(self):
kernel32 = mock.Mock()
kernel32.OpenProcess.side_effect = OSError("injected API failure")
with mock.patch.object(
_cli,
"_windows_process_api",
return_value=(kernel32, mock.Mock()),
):
self.assertTrue(_cli._windows_process_is_alive(4567))
def test_posix_probe_behavior_is_unchanged(self):
cases = (
(None, True),
(ProcessLookupError(), False),
(PermissionError(), True),
(OSError(errno.ESRCH, "missing"), False),
)
for error, expected in cases:
with self.subTest(error=type(error).__name__):
side_effect = error if error is not None else None
with mock.patch.object(_cli.os, "name", "posix"), \
mock.patch.object(
_cli.os, "kill", side_effect=side_effect
) as kill:
self.assertEqual(_cli._process_is_alive(4567), expected)
kill.assert_called_once_with(4567, 0)
class RuntimeSetTests(unittest.TestCase):
def test_candidate_probe_requires_the_binary_to_execute(self):
candidates = (
Path("/tmp/codebase-memory-mcp"),
PureWindowsPath("/tmp/codebase-memory-mcp"),
)
for candidate in candidates:
with self.subTest(candidate=candidate):
with mock.patch.object(_cli.subprocess, "run") as run:
run.return_value.returncode = 0
_cli._verify_candidate(candidate)
self.assertEqual(
run.call_args.args[0],
[str(candidate), "--version"],
)
def test_concurrent_publishers_preserve_the_first_complete_winner(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
binary_name = "codebase-memory-mcp"
def stages(tag):
staged = directory / f".stage-{tag}-{binary_name}"
staged.write_bytes(f"binary:{tag}".encode())
return {binary_name: staged}
first_stages = stages("first")
second_stages = stages("second")
first_holds_lock = threading.Event()
allow_first = threading.Event()
second_observed_lock = threading.Event()
errors = []
replace = os.replace
read_lock_owner = _cli._read_runtime_lock_owner
def pause_first(source, destination):
replace(source, destination)
if Path(destination) == directory / binary_name:
first_holds_lock.set()
if not allow_first.wait(5):
raise RuntimeError("test did not release first publisher")
def observe_waiter(lock_path):
if threading.current_thread().name == "publisher-second":
second_observed_lock.set()
return read_lock_owner(lock_path)
def publish(label, staged_paths, replace_file=None):
try:
_cli._publish_runtime_set(
staged_paths,
directory / binary_name,
binary_name,
replace_file=replace_file,
)
except Exception as exc: # surfaced on the test thread below
errors.append((label, exc))
first = threading.Thread(
name="publisher-first",
target=publish,
args=("first", first_stages, pause_first),
)
second = threading.Thread(
name="publisher-second",
target=publish,
args=("second", second_stages),
)
with mock.patch.object(
_cli,
"_read_runtime_lock_owner",
side_effect=observe_waiter,
):
first.start()
try:
self.assertTrue(first_holds_lock.wait(2))
second.start()
self.assertTrue(second_observed_lock.wait(2))
finally:
allow_first.set()
first.join(5)
if second.ident is not None:
second.join(5)
self.assertFalse(first.is_alive())
self.assertFalse(second.is_alive())
self.assertEqual(errors, [])
self.assertEqual(
(directory / binary_name).read_bytes(), b"binary:first"
)
self.assertFalse((directory / _cli._RUNTIME_LOCK_NAME).exists())
def test_killed_publisher_is_reconciled_by_locked_readiness(self):
helper_source = r"""
import os
import sys
import time
from pathlib import Path
source_root, directory_raw, staged_raw, marker_raw, crash_name = sys.argv[1:]
sys.path.insert(0, source_root)
from codebase_memory_mcp import _cli
directory = Path(directory_raw)
staged = Path(staged_raw)
marker = Path(marker_raw)
binary_name = "codebase-memory-mcp"
staged_paths = {
binary_name: staged / binary_name,
}
replace = os.replace
def pausing_replace(source, destination):
replace(source, destination)
if Path(destination) == directory / crash_name:
marker.write_text("reached\n")
while True:
time.sleep(60)
def verifier(binary):
if not Path(binary).read_text().startswith("binary:"):
raise RuntimeError("test binary failed verification")
_cli._publish_runtime_set(
staged_paths,
directory / binary_name,
binary_name,
replace_file=pausing_replace,
verifier=verifier,
)
"""
binary_name = "codebase-memory-mcp"
for crash_name, expected_ready in (
(binary_name, True),
):
with self.subTest(crash_name=crash_name), tempfile.TemporaryDirectory() as root:
root_path = Path(root)
directory = root_path / "destination"
staged = root_path / "staged"
marker = root_path / "crash-reached"
directory.mkdir()
staged.mkdir()
(directory / binary_name).write_text("corrupt:old")
(staged / binary_name).write_text("binary:candidate")
environment = os.environ.copy()
environment["PYTHONPYCACHEPREFIX"] = str(root_path / "pycache")
process = subprocess.Popen(
[
sys.executable,
"-c",
helper_source,
str(PACKAGE_ROOT / "src"),
str(directory),
str(staged),
str(marker),
crash_name,
],
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
env=environment,
)
try:
deadline = time.monotonic() + 10
while not marker.exists() and time.monotonic() < deadline:
if process.poll() is not None:
stderr = process.communicate()[1]
self.fail(
"publication helper exited before crash gate: "
f"{stderr}"
)
time.sleep(0.01)
self.assertTrue(
marker.exists(),
"publication helper did not reach crash gate",
)
backups = [
path
for path in directory.iterdir()
if path.name.startswith(".cbm-runtime-backup-")
]
self.assertEqual(len(backups), 1)
self.assertTrue(backups[0].is_dir())
self.assertTrue(
(backups[0] / ".retirement-complete").is_file()
)
process.kill()
process.wait(5)
self.assertNotEqual(process.returncode, 0)
def verifier(binary):
if not Path(binary).read_text().startswith("binary:"):
raise RuntimeError("test binary failed verification")
with mock.patch.object(
_cli, "_bin_path", return_value=directory / binary_name
), mock.patch.object(
_cli, "_verify_candidate", side_effect=verifier
):
ready = _cli._runtime_set_ready_locked("test-version")
self.assertEqual(ready, expected_ready)
if not ready:
self.assertEqual(
(directory / binary_name).read_text(),
"corrupt:old",
)
retry_staged = root_path / "retry-staged"
retry_staged.mkdir()
retry_paths = {
binary_name: retry_staged / binary_name,
}
retry_paths[binary_name].write_text("binary:candidate")
_cli._publish_runtime_set(
retry_paths,
directory / binary_name,
binary_name,
verifier=verifier,
)
self.assertEqual(
(directory / binary_name).read_text(), "binary:candidate"
)
self.assertFalse(
any(
path.name.startswith(".cbm-runtime-backup-")
for path in directory.iterdir()
)
)
self.assertFalse(
(directory / _cli._RUNTIME_LOCK_NAME).exists()
)
finally:
if process.poll() is None:
process.kill()
process.wait(5)
if process.stderr is not None:
process.stderr.close()
def test_publication_failure_restores_prior_complete_runtime_set(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
binary_name = "codebase-memory-mcp"
(directory / binary_name).write_text("binary:old")
staged_paths = {}
for name, contents in (
(binary_name, b"binary:candidate"),
):
staged = directory / f"stage-{name}"
staged.write_bytes(contents)
staged_paths[name] = staged
replace = os.replace
failed = False
retired = []
def fail_binary_publish(source, destination):
nonlocal failed
source = Path(source)
destination = Path(destination)
if destination.parent.name.startswith(".cbm-runtime-backup-"):
retired.append(source.name)
if destination == directory / binary_name and not failed:
failed = True
raise OSError("injected binary publication failure")
replace(source, destination)
with self.assertRaises(OSError):
_cli._publish_runtime_set(
staged_paths,
directory / binary_name,
binary_name,
replace_file=fail_binary_publish,
)
self.assertTrue(failed)
self.assertEqual(retired[0], binary_name)
self.assertEqual((directory / binary_name).read_text(), "binary:old")
self.assertFalse(
any(
path.name.startswith(".cbm-runtime-backup-")
for path in directory.iterdir()
)
)
def test_failure_never_deletes_a_complete_foreign_winner(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
binary_name = "codebase-memory-mcp"
(directory / binary_name).write_bytes(b"binary:old")
staged_paths = {}
for name, contents in (
(binary_name, b"binary:candidate"),
):
staged = directory / f".stage-candidate-{name}"
staged.write_bytes(contents)
staged_paths[name] = staged
replace = os.replace
injected = False
def install_foreign_winner(source, destination):
nonlocal injected
source = Path(source)
destination = Path(destination)
if destination == directory / binary_name and not injected:
injected = True
(directory / binary_name).write_bytes(b"binary:foreign")
raise OSError("simulated non-cooperating winner")
replace(source, destination)
_cli._publish_runtime_set(
staged_paths,
directory / binary_name,
binary_name,
replace_file=install_foreign_winner,
)
self.assertTrue(injected)
self.assertEqual(
(directory / binary_name).read_bytes(), b"binary:foreign"
)
self.assertFalse(
any(
path.name.startswith(".cbm-runtime-backup-")
for path in directory.iterdir()
)
)
def test_orphan_reconciliation_rejects_multiply_linked_backup_members(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
binary = directory / "codebase-memory-mcp"
backup = directory / f".cbm-runtime-backup-{'a' * 32}"
backup.mkdir()
(backup / ".retirement-complete").write_bytes(b"")
member = backup / binary.name
member.write_text("binary:old")
os.link(member, directory / "backup-hardlink-copy")
with mock.patch.object(
_cli, "_bin_path", return_value=binary
), self.assertRaisesRegex(
RuntimeError, "unsafe package-cache backup member"
):
_cli._runtime_set_ready_locked("test-version")
self.assertTrue(backup.exists(), "unsafe backup was mutated")
self.assertFalse((directory / _cli._RUNTIME_LOCK_NAME).exists())
def test_stalled_lock_creator_never_deletes_successor(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
successor = None
def publish_successor_then_abort():
nonlocal successor
_cli._runtime_lock_claim_observer = None
successor, _ = _cli._acquire_runtime_lock(directory)
raise RuntimeError("injected stalled creator abort")
with mock.patch.object(
_cli,
"_runtime_lock_claim_observer",
side_effect=publish_successor_then_abort,
), self.assertRaisesRegex(RuntimeError, "stalled creator abort"):
_cli._acquire_runtime_lock(directory)
self.assertIsNotNone(successor)
self.assertTrue(successor[0].exists())
_cli._release_runtime_lock(successor)
def test_acquire_closes_claim_descriptor_before_unlink_and_reopens(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
captured = {}
write_owner = _cli._write_runtime_lock_owner
unlink = Path.unlink
def capture_claim_descriptor(owner_fd, token):
captured["claim_fd"] = owner_fd
write_owner(owner_fd, token)
def require_closed_claim_descriptor(path, *args, **kwargs):
if f"{_cli._RUNTIME_LOCK_NAME}.claim-" in path.name:
with self.assertRaises(OSError):
os.fstat(captured["claim_fd"])
captured["closed_before_unlink"] = True
return unlink(path, *args, **kwargs)
with mock.patch.object(
_cli,
"_write_runtime_lock_owner",
side_effect=capture_claim_descriptor,
), mock.patch.object(
Path, "unlink", new=require_closed_claim_descriptor
):
lock, _ = _cli._acquire_runtime_lock(directory)
try:
self.assertTrue(captured["closed_before_unlink"])
self.assertTrue(
_cli._same_runtime_lock_object(
os.fstat(lock[2]), lock[0].lstat()
)
)
finally:
_cli._release_runtime_lock(lock)
def test_release_closes_descriptor_and_revalidates_each_lock_name(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
lock, _ = _cli._acquire_runtime_lock(directory)
original_fd = lock[2]
events = []
read_owner = _cli._read_runtime_lock_owner
rename = os.rename
def record_owner_validation(path):
events.append(("validate", Path(path).name))
return read_owner(path)
def require_closed_descriptor(source, destination):
with self.assertRaises(OSError):
os.fstat(original_fd)
events.append(("rename", Path(source).name))
return rename(source, destination)
with mock.patch.object(
_cli,
"_read_runtime_lock_owner",
side_effect=record_owner_validation,
), mock.patch.object(
_cli.os, "rename", side_effect=require_closed_descriptor
):
_cli._release_runtime_lock(lock)
self.assertEqual(
events,
[
("validate", _cli._RUNTIME_LOCK_NAME),
("validate", _cli._RUNTIME_LOCK_NAME),
("rename", _cli._RUNTIME_LOCK_NAME),
(
"validate",
f"{_cli._RUNTIME_LOCK_NAME}.released-{lock[1]}",
),
],
)
self.assertFalse(lock[0].exists())
def test_release_rejects_owner_substitution_before_rename(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
lock, _ = _cli._acquire_runtime_lock(directory)
read_owner = _cli._read_runtime_lock_owner
canonical_reads = 0
def substitute_owner_on_reopen(path):
nonlocal canonical_reads
owner = read_owner(path)
if Path(path) == lock[0]:
canonical_reads += 1
if canonical_reads == 2:
return (os.getpid(), "f" * 32, owner[2])
return owner
with mock.patch.object(
_cli,
"_read_runtime_lock_owner",
side_effect=substitute_owner_on_reopen,
):
with self.assertRaisesRegex(RuntimeError, "ownership changed"):
_cli._release_runtime_lock(lock)
self.assertTrue(lock[0].exists())
lock[0].unlink()
def test_release_rejects_object_substitution_before_rename(self):
with tempfile.TemporaryDirectory() as root:
directory = Path(root)
lock, _ = _cli._acquire_runtime_lock(directory)
close = os.close
substituted = False
def substitute_object_after_owner_close(owner_fd):
nonlocal substituted
if (
owner_fd != lock[2]
or substituted
or not lock[0].exists()
):
close(owner_fd)
return
owner_record = lock[0].read_bytes()
replacement = directory / "replacement-lock"
replacement.write_bytes(owner_record)
try:
close(owner_fd)
os.replace(replacement, lock[0])
substituted = True
finally:
replacement.unlink(missing_ok=True)
with mock.patch.object(
_cli.os,
"close",
side_effect=substitute_object_after_owner_close,
):
with self.assertRaisesRegex(RuntimeError, "ownership changed"):
_cli._release_runtime_lock(lock)
self.assertTrue(substituted)
self.assertTrue(lock[0].exists())
lock[0].unlink()
def test_expired_lease_never_reclaims_live_owner(self):
with tempfile.TemporaryDirectory() as root:
lock_path = Path(root) / _cli._RUNTIME_LOCK_NAME
owner = {
"pid": os.getpid(),
"token": "a" * 32,
"lease_expires_ms": int((time.time() - 1) * 1000),
}
owner_record = f"{json.dumps(owner)}\n"
lock_path.write_text(owner_record)
before = lock_path.stat()
with mock.patch.object(_cli, "_process_is_alive", return_value=True):
self.assertEqual(
_cli._try_reclaim_runtime_lock(lock_path, "b" * 32),
(False, False),
)
after = lock_path.stat()
self.assertEqual(lock_path.read_text(), owner_record)
self.assertEqual(json.loads(lock_path.read_text())["token"], owner["token"])
self.assertEqual(after.st_dev, before.st_dev)
self.assertEqual(after.st_ino, before.st_ino)
def test_stale_legacy_lock_never_reclaims_live_owner(self):
with tempfile.TemporaryDirectory() as root:
lock_path = Path(root) / _cli._RUNTIME_LOCK_NAME
owner_record = json.dumps(
{"pid": os.getpid(), "token": "a" * 32}
) + "\n"
lock_path.mkdir()
(lock_path / "owner.json").write_text(owner_record)
stale = time.time() - _cli._RUNTIME_LEGACY_STALE_SECONDS - 1
os.utime(lock_path, (stale, stale))
before = lock_path.stat()
with mock.patch.object(_cli, "_process_is_alive", return_value=True):
self.assertEqual(
_cli._try_reclaim_runtime_lock(lock_path, "b" * 32),
(False, False),
)
after = lock_path.stat()
self.assertEqual((lock_path / "owner.json").read_text(), owner_record)
self.assertEqual(after.st_dev, before.st_dev)
self.assertEqual(after.st_ino, before.st_ino)
def test_stale_ownerless_lock_is_reclaimed(self):
with tempfile.TemporaryDirectory() as root:
lock_path = Path(root) / _cli._RUNTIME_LOCK_NAME
lock_path.write_text("")
stale = time.time() - _cli._RUNTIME_OWNERLESS_STALE_SECONDS - 1
os.utime(lock_path, (stale, stale))
self.assertEqual(
_cli._try_reclaim_runtime_lock(lock_path, "b" * 32),
(True, False),
)
self.assertFalse(lock_path.exists())
def test_unix_tar_rejects_an_unexpected_root_member(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "release.tar.gz"
with tarfile.open(archive, "w:gz") as tf:
for name in (*_cli._UNIX_ARCHIVE_NAMES, "unexpected-root-file"):
data_path = Path(root) / f"fixture-{len(tf.getmembers())}"
data_path.write_bytes(name.encode())
tf.add(data_path, arcname=name)
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf, self.assertRaises(SystemExit):
_cli._safe_extract_tar(
tf,
str(destination),
_cli._UNIX_ARCHIVE_NAMES,
("codebase-memory-mcp",),
)
def test_unix_tar_rejects_hardlink_members(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "release.tar.gz"
with tarfile.open(archive, "w:gz") as tf:
info = tarfile.TarInfo("codebase-memory-mcp")
info.type = tarfile.LNKTYPE
info.linkname = "LICENSE"
tf.addfile(info)
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf, self.assertRaises(SystemExit):
_cli._safe_extract_tar(
tf,
str(destination),
("codebase-memory-mcp",),
("codebase-memory-mcp",),
False,
)
def test_windows_zip_rejects_symlink_metadata(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "release.zip"
with zipfile.ZipFile(archive, "w") as zf:
info = zipfile.ZipInfo(_cli._WINDOWS_BINARY_NAME)
info.create_system = 3
info.external_attr = (stat.S_IFLNK | 0o777) << 16
zf.writestr(info, "LICENSE")
destination = Path(root) / "extract"
destination.mkdir()
with zipfile.ZipFile(archive) as zf, self.assertRaises(SystemExit):
_cli._safe_extract_zip(
zf,
str(destination),
(_cli._WINDOWS_BINARY_NAME,),
(_cli._WINDOWS_BINARY_NAME,),
False,
)