mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 14:30:37 +00:00
The script walks the filesystem, so it stamped generated copies too, such as media-processor/target/package.json: --check counted files git does not track and failed whenever one of those copies was stale. Prune target/ like the other generated trees, with a test for it. Memory: the JDK and ImageMagick image pins have traps the code cannot show, now in workflow/docker-images; memory-maintenance gains the rule that a memory must not restate what the code already says; updating-pnpm notes the lockfile-only refresh. AI-assisted-by: deepseek-v4.1-flash
170 lines
5.6 KiB
Python
170 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for scripts/sync-pnpm-version.
|
|
|
|
Run with:
|
|
|
|
python3 scripts/test_sync_pnpm_version.py
|
|
|
|
Covers stamping the `packageManager` field across a package.json tree:
|
|
stale values get updated, missing fields get inserted, formatting of each
|
|
file is preserved (only the stamped line may change), --check reports
|
|
drift without writing, and stamping is idempotent.
|
|
"""
|
|
|
|
import json
|
|
import pathlib
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
# Loading scripts/sync-pnpm-version via subprocess; no bytecode emitted.
|
|
sys.dont_write_bytecode = True
|
|
|
|
SCRIPT_PATH = pathlib.Path(__file__).resolve().parent / "sync-pnpm-version"
|
|
|
|
FIELD = "pnpm@99.0.0+sha512." + "ab" * 64
|
|
|
|
|
|
def run_script(*args):
|
|
"""Run the script, returning the CompletedProcess."""
|
|
return subprocess.run(
|
|
["bash", str(SCRIPT_PATH), *args],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
|
|
def make_fixture(root):
|
|
"""Build a 3-file tree: stale value, already synced, missing field."""
|
|
stale = root / "stale"
|
|
stale.mkdir()
|
|
(stale / "package.json").write_text(
|
|
'{\n'
|
|
' "name": "stale",\n'
|
|
' "version": "1.0.0",\n'
|
|
' "private": true,\n'
|
|
' "packageManager": "pnpm@10.0.0+sha512.' + "00" * 64 + '",\n'
|
|
' "scripts": {\n'
|
|
' "test": "echo ok"\n'
|
|
' }\n'
|
|
'}\n'
|
|
)
|
|
synced = root / "synced"
|
|
synced.mkdir()
|
|
# 4-space indent on purpose: the stamp must not reformat the file.
|
|
(synced / "package.json").write_text(
|
|
'{\n'
|
|
' "name": "synced",\n'
|
|
' "version": "1.0.0",\n'
|
|
f' "packageManager": "{FIELD}"\n'
|
|
'}\n'
|
|
)
|
|
missing = root / "missing"
|
|
missing.mkdir()
|
|
(missing / "package.json").write_text(
|
|
'{\n'
|
|
' "name": "missing",\n'
|
|
' "version": "1.0.0",\n'
|
|
' "scripts": {\n'
|
|
' "test": "echo ok"\n'
|
|
' }\n'
|
|
'}\n'
|
|
)
|
|
return stale, synced, missing
|
|
|
|
|
|
class SyncPnpmVersionTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = pathlib.Path(tempfile.mkdtemp(prefix="sync-pnpm-"))
|
|
self.stale, self.synced, self.missing = make_fixture(self.tmp)
|
|
self.synced_before = (self.synced / "package.json").read_bytes()
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tmp, ignore_errors=True)
|
|
|
|
def stamp(self, *extra):
|
|
return run_script("--root", str(self.tmp), "--field", FIELD, *extra)
|
|
|
|
def read(self, path):
|
|
return (path / "package.json").read_text()
|
|
|
|
def test_stamp_updates_stale_inserts_missing_keeps_synced(self):
|
|
proc = self.stamp()
|
|
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
|
|
stale_lines = self.read(self.stale).splitlines()
|
|
self.assertIn(f' "packageManager": "{FIELD}",', stale_lines)
|
|
data = json.loads(self.read(self.stale))
|
|
self.assertEqual(data["packageManager"], FIELD)
|
|
# Only the packageManager line changed in the stale file.
|
|
self.assertEqual(len(stale_lines), 9)
|
|
|
|
# The already-synced file is byte-identical (indent preserved).
|
|
self.assertEqual(
|
|
(self.synced / "package.json").read_bytes(), self.synced_before
|
|
)
|
|
|
|
# The missing field was inserted; the file stays valid JSON.
|
|
data = json.loads(self.read(self.missing))
|
|
self.assertEqual(data["packageManager"], FIELD)
|
|
self.assertEqual(data["name"], "missing")
|
|
|
|
def test_check_passes_once_synced(self):
|
|
self.assertEqual(self.stamp().returncode, 0)
|
|
proc = self.stamp("--check")
|
|
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
|
|
|
def test_check_fails_on_drift_without_writing(self):
|
|
self.assertEqual(self.stamp().returncode, 0)
|
|
before = (self.stale / "package.json").read_bytes()
|
|
(self.stale / "package.json").write_text(
|
|
self.read(self.stale).replace(FIELD, "pnpm@1.0.0+sha512." + "ff" * 64)
|
|
)
|
|
proc = self.stamp("--check")
|
|
self.assertNotEqual(proc.returncode, 0)
|
|
self.assertIn("stale/package.json", proc.stdout + proc.stderr)
|
|
# --check must not write: reverting the tamper restores a pass.
|
|
(self.stale / "package.json").write_bytes(before)
|
|
self.assertEqual(self.stamp("--check").returncode, 0)
|
|
|
|
def test_check_reports_missing_field(self):
|
|
proc = self.stamp("--check")
|
|
self.assertNotEqual(proc.returncode, 0)
|
|
self.assertIn("missing/package.json", proc.stdout + proc.stderr)
|
|
|
|
def test_target_dirs_are_never_stamped(self):
|
|
generated = self.tmp / "module" / "target"
|
|
generated.mkdir(parents=True)
|
|
package = generated / "package.json"
|
|
package.write_text(
|
|
'{\n'
|
|
' "name": "generated",\n'
|
|
' "packageManager": "pnpm@10.0.0+sha512.' + "00" * 64 + '"\n'
|
|
'}\n'
|
|
)
|
|
before = package.read_bytes()
|
|
|
|
self.assertEqual(self.stamp().returncode, 0)
|
|
self.assertEqual(package.read_bytes(), before)
|
|
# The stale copy must not make --check fail either.
|
|
self.assertEqual(self.stamp("--check").returncode, 0)
|
|
|
|
def test_stamp_is_idempotent(self):
|
|
self.assertEqual(self.stamp().returncode, 0)
|
|
first = {
|
|
p: (self.tmp / p / "package.json").read_bytes()
|
|
for p in ("stale", "synced", "missing")
|
|
}
|
|
second = self.stamp()
|
|
self.assertEqual(second.returncode, 0, second.stderr)
|
|
for name, content in first.items():
|
|
self.assertEqual(
|
|
(self.tmp / name / "package.json").read_bytes(), content
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|