209 lines
7.7 KiB
Python
209 lines
7.7 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
|
|
|
|
SCRIPT = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "bundle_zip.py"
|
|
|
|
|
|
def run_cli(*args: str, cwd: pathlib.Path | None = None) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
[sys.executable, str(SCRIPT), *args],
|
|
cwd=str(cwd) if cwd else None,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
|
|
def write_file(path: pathlib.Path, text: str = "content") -> pathlib.Path:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def write_file_list(path: pathlib.Path, files: list[str]) -> pathlib.Path:
|
|
path.write_text("\n".join(files) + "\n", encoding="utf-8")
|
|
return path
|
|
|
|
|
|
class BundleZipTest(unittest.TestCase):
|
|
def test_absolute_files_under_marker_create_source_relative_entries(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp) / "the-mindscape-of-bro-tsong"
|
|
first = write_file(root / "cards" / "card_index.md", "card")
|
|
second = write_file(root / "cards" / "intellectual_archaeology.md", "ia")
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(first), str(second)])
|
|
output = pathlib.Path(tmp) / "bundle.zip"
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"the-mindscape-of-bro-tsong",
|
|
"--output",
|
|
str(output),
|
|
"--verify",
|
|
"--json",
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["status"], "PASS")
|
|
self.assertEqual(
|
|
summary["entries"],
|
|
["cards/card_index.md", "cards/intellectual_archaeology.md"],
|
|
)
|
|
with zipfile.ZipFile(output) as archive:
|
|
self.assertEqual(archive.namelist(), summary["entries"])
|
|
|
|
def test_mixed_absolute_and_cwd_relative_files_create_correct_entries(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp) / "repo"
|
|
absolute_file = write_file(root / "cards" / "absolute.md")
|
|
relative_file = write_file(root / "notes" / "relative.md")
|
|
file_list = write_file_list(
|
|
root / "files.txt",
|
|
[str(absolute_file), r"notes\relative.md"],
|
|
)
|
|
output = pathlib.Path(tmp) / "mixed.zip"
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(output),
|
|
"--verify",
|
|
"--json",
|
|
cwd=root,
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["entries"], ["cards/absolute.md", "notes/relative.md"])
|
|
|
|
def test_existing_destination_zip_is_replaced_after_validation(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp) / "repo"
|
|
source = write_file(root / "src" / "new.md", "new")
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(source)])
|
|
output = pathlib.Path(tmp) / "bundle.zip"
|
|
with zipfile.ZipFile(output, "w") as archive:
|
|
archive.writestr("old.txt", "old")
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(output),
|
|
"--verify",
|
|
"--json",
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
with zipfile.ZipFile(output) as archive:
|
|
self.assertEqual(archive.namelist(), ["src/new.md"])
|
|
self.assertEqual(archive.read("src/new.md"), b"new")
|
|
|
|
def test_missing_source_fails_before_existing_output_is_touched(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp) / "repo"
|
|
missing = root / "missing.md"
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(missing)])
|
|
output = pathlib.Path(tmp) / "bundle.zip"
|
|
with zipfile.ZipFile(output, "w") as archive:
|
|
archive.writestr("old.txt", "old")
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(output),
|
|
"--json",
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["status"], "FAIL")
|
|
self.assertIn("does not exist", summary["errors"][0])
|
|
with zipfile.ZipFile(output) as archive:
|
|
self.assertEqual(archive.namelist(), ["old.txt"])
|
|
|
|
def test_file_outside_root_marker_fails(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
outside = write_file(pathlib.Path(tmp) / "outside" / "file.md")
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(outside)])
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(pathlib.Path(tmp) / "bundle.zip"),
|
|
"--json",
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["status"], "FAIL")
|
|
self.assertIn("root marker", summary["errors"][0])
|
|
|
|
def test_duplicate_relative_entries_fail(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
first = write_file(pathlib.Path(tmp) / "left" / "repo" / "same.md", "first")
|
|
second = write_file(pathlib.Path(tmp) / "right" / "repo" / "same.md", "second")
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(first), str(second)])
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(pathlib.Path(tmp) / "bundle.zip"),
|
|
"--json",
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["status"], "FAIL")
|
|
self.assertIn("Duplicate zip entry", "\n".join(summary["errors"]))
|
|
|
|
def test_output_path_matching_source_file_fails(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp) / "repo"
|
|
source = write_file(root / "bundle.zip")
|
|
file_list = write_file_list(pathlib.Path(tmp) / "files.txt", [str(source)])
|
|
|
|
result = run_cli(
|
|
"--file-list",
|
|
str(file_list),
|
|
"--relative-root-marker",
|
|
"repo",
|
|
"--output",
|
|
str(source),
|
|
"--json",
|
|
)
|
|
|
|
self.assertNotEqual(result.returncode, 0)
|
|
summary = json.loads(result.stdout)
|
|
self.assertEqual(summary["status"], "FAIL")
|
|
self.assertIn("same as a source file", "\n".join(summary["errors"]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|