Packages are ordinary Git repositories carrying a sjnetpkg.toml, so sjnetpkg only needs to resolve, clone, track and run them instead of inventing an archive format or build system. This adds the v0.1 CLI (install from registry or Git URL, remove, list/inspect, registry update, run) on top of the Python standard library, together with a test suite. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
141 lines
5.5 KiB
Python
141 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from sjnetpkg import config, package
|
|
|
|
|
|
def write_package(root: Path, directory: str, manifest: str) -> Path:
|
|
target = root / directory
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
(target / "sjnetpkg.toml").write_text(manifest, encoding="utf-8")
|
|
return target
|
|
|
|
|
|
class ValidNameTests(unittest.TestCase):
|
|
def test_valid_names(self) -> None:
|
|
for name in ("cool-tool", "a", "tool_2", "tool.py", "A1"):
|
|
self.assertTrue(package.is_valid_name(name), name)
|
|
|
|
def test_invalid_names(self) -> None:
|
|
for name in ("", ".", "..", "-tool", "tool-", "bad name", "../escape", "a/b"):
|
|
self.assertFalse(package.is_valid_name(name), name)
|
|
|
|
|
|
class ManifestTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self._tmp = tempfile.TemporaryDirectory()
|
|
self.root = Path(self._tmp.name)
|
|
|
|
def tearDown(self) -> None:
|
|
self._tmp.cleanup()
|
|
|
|
def test_reads_name_version_and_run_command(self) -> None:
|
|
directory = write_package(
|
|
self.root,
|
|
"cool-tool",
|
|
'name = "cool-tool"\nversion = "1.0.0"\n\n[run]\ncommand = "cargo run"\n',
|
|
)
|
|
manifest = package.read_manifest(directory)
|
|
self.assertEqual(manifest.name, "cool-tool")
|
|
self.assertEqual(manifest.version, "1.0.0")
|
|
self.assertEqual(manifest.run_command, "cargo run")
|
|
self.assertEqual(manifest.path, directory)
|
|
|
|
def test_run_section_is_optional(self) -> None:
|
|
directory = write_package(
|
|
self.root, "lib", 'name = "lib"\nversion = "0.1.0"\n'
|
|
)
|
|
self.assertIsNone(package.read_manifest(directory).run_command)
|
|
|
|
def test_missing_manifest_raises(self) -> None:
|
|
directory = self.root / "empty"
|
|
directory.mkdir()
|
|
with self.assertRaises(package.PackageError):
|
|
package.read_manifest(directory)
|
|
|
|
def test_invalid_toml_raises(self) -> None:
|
|
directory = write_package(self.root, "broken", "name = = \n")
|
|
with self.assertRaises(package.PackageError):
|
|
package.read_manifest(directory)
|
|
|
|
def test_missing_name_raises(self) -> None:
|
|
directory = write_package(self.root, "noname", 'version = "1.0.0"\n')
|
|
with self.assertRaises(package.PackageError):
|
|
package.read_manifest(directory)
|
|
|
|
def test_missing_version_raises(self) -> None:
|
|
directory = write_package(self.root, "noversion", 'name = "noversion"\n')
|
|
with self.assertRaises(package.PackageError):
|
|
package.read_manifest(directory)
|
|
|
|
def test_invalid_name_raises(self) -> None:
|
|
directory = write_package(
|
|
self.root, "bad", 'name = "../escape"\nversion = "1.0.0"\n'
|
|
)
|
|
with self.assertRaises(package.PackageError):
|
|
package.read_manifest(directory)
|
|
|
|
|
|
class InstalledPackagesTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self._tmp = tempfile.TemporaryDirectory()
|
|
self.paths = config.get_paths(Path(self._tmp.name) / ".sjnetpkg")
|
|
self.paths.pkg.mkdir(parents=True)
|
|
|
|
def tearDown(self) -> None:
|
|
self._tmp.cleanup()
|
|
|
|
def test_list_is_empty_without_packages_directory(self) -> None:
|
|
self.assertEqual(package.list_installed(config.get_paths(self.paths.home / "x")), [])
|
|
|
|
def test_list_ignores_invalid_and_non_directory_entries(self) -> None:
|
|
write_package(self.paths.pkg, "cool-tool", 'name = "cool-tool"\nversion = "1.0.0"\n')
|
|
write_package(self.paths.pkg, "broken", "nonsense")
|
|
(self.paths.pkg / "loose-file").write_text("hello")
|
|
names = [m.name for m in package.list_installed(self.paths)]
|
|
self.assertEqual(names, ["cool-tool"])
|
|
|
|
def test_find_installed_by_directory_name(self) -> None:
|
|
directory = write_package(
|
|
self.paths.pkg, "repo-name", 'name = "manifest-name"\nversion = "1.0.0"\n'
|
|
)
|
|
self.assertEqual(package.find_installed(self.paths, "repo-name"), directory)
|
|
|
|
def test_find_installed_by_manifest_name(self) -> None:
|
|
directory = write_package(
|
|
self.paths.pkg, "repo-name", 'name = "manifest-name"\nversion = "1.0.0"\n'
|
|
)
|
|
self.assertEqual(package.find_installed(self.paths, "manifest-name"), directory)
|
|
|
|
def test_find_installed_unknown_raises(self) -> None:
|
|
with self.assertRaises(package.NotInstalledError):
|
|
package.find_installed(self.paths, "missing")
|
|
|
|
def test_remove_package(self) -> None:
|
|
directory = write_package(self.paths.pkg, "cool-tool", 'name = "cool-tool"\nversion = "1.0.0"\n')
|
|
self.assertEqual(package.remove_package(self.paths, "cool-tool"), directory)
|
|
self.assertFalse(directory.exists())
|
|
|
|
def test_remove_missing_package_raises(self) -> None:
|
|
with self.assertRaises(package.NotInstalledError):
|
|
package.remove_package(self.paths, "missing")
|
|
|
|
def test_remove_refuses_to_leave_package_directory(self) -> None:
|
|
outside = Path(self._tmp.name) / "outside"
|
|
outside.mkdir()
|
|
link = self.paths.pkg / "link"
|
|
try:
|
|
link.symlink_to(outside, target_is_directory=True)
|
|
except (OSError, NotImplementedError): # pragma: no cover - platform dependent
|
|
self.skipTest("symlinks are not supported on this platform")
|
|
with self.assertRaises(package.PackageError):
|
|
package.remove_package(self.paths, "link")
|
|
self.assertTrue(outside.exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|