from __future__ import annotations from pathlib import Path import tempfile import unittest import zipfile try: import pyzipper except ImportError: # pragma: no cover pyzipper = None from govoplan_campaign.backend.services.zip_service import ( create_zip_archive, zip_archive_evidence, ) class ZipServiceTests(unittest.TestCase): def test_standard_password_zip_uses_zipcrypto_readable_by_stdlib(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: root = Path(temp_dir) source = root / "source.txt" source.write_text("Hello Windows ZIP", encoding="utf-8") output = root / "standard.zip" create_zip_archive(output, [(source, "message.txt")], "secret", "zip_standard") with zipfile.ZipFile(output) as archive: info = archive.getinfo("message.txt") self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED) self.assertTrue(info.flag_bits & 0x1) self.assertEqual(archive.read("message.txt", pwd=b"secret"), b"Hello Windows ZIP") with self.assertRaises(RuntimeError): archive.read("message.txt", pwd=b"wrong-password") evidence = zip_archive_evidence( output, [(source, "message.txt")], password_protected=True, method="zip_standard", ) self.assertEqual("Legacy ZipCrypto", evidence["format"]) self.assertEqual("govoplan-campaign.zipcrypto", evidence["implementation"]) self.assertNotIn("password", evidence) self.assertEqual(64, len(str(evidence["archive_sha256"]))) self.assertEqual(64, len(str(evidence["members"][0]["sha256"]))) @unittest.skipIf(pyzipper is None, "pyzipper is not installed") def test_aes_password_zip_keeps_aes_encryption(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: root = Path(temp_dir) source = root / "source.txt" source.write_text("Hello AES ZIP", encoding="utf-8") output = root / "aes.zip" create_zip_archive(output, [(source, "message.txt")], "secret", "aes") with zipfile.ZipFile(output) as archive: info = archive.getinfo("message.txt") self.assertEqual(info.compress_type, 99) self.assertTrue(info.flag_bits & 0x1) with pyzipper.AESZipFile(output) as archive: archive.setpassword(b"secret") self.assertEqual(archive.read("message.txt"), b"Hello AES ZIP") def test_unprotected_zip_ignores_encryption_mode(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: root = Path(temp_dir) source = root / "source.txt" source.write_text("Plain ZIP", encoding="utf-8") output = root / "plain.zip" create_zip_archive(output, [(source, "message.txt")], "", "zip_standard") with zipfile.ZipFile(output) as archive: info = archive.getinfo("message.txt") self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED) self.assertFalse(info.flag_bits & 0x1) self.assertEqual(archive.read("message.txt"), b"Plain ZIP") def test_unknown_password_method_fails_without_downgrade_or_output(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: root = Path(temp_dir) source = root / "source.txt" source.write_text("No downgrade", encoding="utf-8") output = root / "unknown.zip" with self.assertRaisesRegex(ValueError, "Unsupported"): create_zip_archive(output, [source], "secret", "unknown") self.assertFalse(output.exists()) if __name__ == "__main__": unittest.main()