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()