from __future__ import annotations from pathlib import Path import sys import unittest from unittest import mock META_ROOT = Path(__file__).resolve().parents[1] RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release" if str(RELEASE_TOOLS_ROOT) not in sys.path: sys.path.insert(0, str(RELEASE_TOOLS_ROOT)) from govoplan_release.module_directory import ( # noqa: E402 module_directory_payloads, safe_path_part, ) class ReleaseModuleDirectoryTests(unittest.TestCase): def test_signed_catalog_timestamp_makes_derived_files_reproducible(self) -> None: catalog = { "generated_at": "2026-07-22T12:00:00Z", "sequence": 7, "modules": [], } first = module_directory_payloads( catalog_payload=catalog, keyring_payload={}, channel="stable", ) second = module_directory_payloads( catalog_payload=catalog, keyring_payload={}, channel="stable", ) self.assertEqual(first, second) self.assertEqual( "2026-07-22T12:00:00Z", first[-1][1]["generated_at"], ) def test_dot_segments_and_noncanonical_ids_never_become_paths(self) -> None: for value in (".", "..", "../escape", "/absolute", "module/child"): with self.subTest(value=value), self.assertRaises(ValueError): safe_path_part(value) for module_id in ("..", "../escape", "UPPER", "bad.id"): with self.subTest(module_id=module_id), mock.patch( "govoplan_release.module_directory.module_rows", return_value=[ { "module_id": module_id, "version": "1.2.3", } ], ): with self.assertRaises(ValueError): module_directory_payloads( catalog_payload={}, keyring_payload={}, channel="stable", ) def test_noncanonical_version_and_channel_fail_before_paths(self) -> None: with mock.patch( "govoplan_release.module_directory.module_rows", return_value=[{"module_id": "demo", "version": "../1.2.3"}], ): with self.assertRaises(ValueError): module_directory_payloads( catalog_payload={}, keyring_payload={}, channel="stable" ) with self.assertRaises(ValueError): module_directory_payloads( catalog_payload={}, keyring_payload={}, channel="../stable" ) if __name__ == "__main__": unittest.main()