feat: add institutional governance and recovery contracts

This commit is contained in:
2026-08-01 17:46:54 +02:00
parent b65b48832b
commit 7192d32e65
61 changed files with 12539 additions and 168 deletions
+169
View File
@@ -0,0 +1,169 @@
from __future__ import annotations
from io import BytesIO
from pathlib import Path
import tempfile
import unittest
from govoplan_core.core.object_storage import (
LocalFilesystemStorageBackend,
S3StorageBackend,
StorageBackendError,
StorageObjectMissing,
normalize_storage_key,
)
class _S3Error(RuntimeError):
def __init__(self, code: str, status: int) -> None:
super().__init__(code)
self.response = {
"Error": {"Code": code},
"ResponseMetadata": {"HTTPStatusCode": status},
}
class _FakeS3Client:
def __init__(self) -> None:
self.objects: dict[str, bytes] = {}
self.head_error: Exception | None = None
def put_object(self, *, Key: str, Body: bytes, **_kwargs) -> None:
self.objects[Key] = Body
def get_object(self, *, Key: str, **_kwargs):
try:
payload = self.objects[Key]
except KeyError as exc:
raise _S3Error("NoSuchKey", 404) from exc
return {"Body": BytesIO(payload), "ContentLength": len(payload)}
def head_object(self, *, Key: str, **_kwargs):
if self.head_error is not None:
raise self.head_error
try:
payload = self.objects[Key]
except KeyError as exc:
raise _S3Error("NotFound", 404) from exc
return {"ContentLength": len(payload)}
def delete_object(self, *, Key: str, **_kwargs) -> None:
self.objects.pop(Key, None)
class ObjectStorageTests(unittest.TestCase):
def test_local_backend_round_trip_pagination_and_safe_keys(self) -> None:
with tempfile.TemporaryDirectory(prefix="govoplan-object-store-") as directory:
backend = LocalFilesystemStorageBackend(Path(directory))
backend.put_bytes("campaign/a.eml", b"a")
backend.put_bytes("campaign/b.eml", b"bb")
first = backend.list_objects(prefix="campaign/", limit=1)
second = backend.list_objects(
prefix="campaign/",
after=first.next_cursor,
limit=1,
)
self.assertEqual(b"a", backend.get_bytes("campaign/a.eml"))
self.assertEqual(1, backend.stat("campaign/a.eml").size_bytes)
self.assertEqual(
("campaign/a.eml",), tuple(item.key for item in first.objects)
)
self.assertEqual("campaign/a.eml", first.next_cursor)
self.assertEqual(
("campaign/b.eml",), tuple(item.key for item in second.objects)
)
self.assertIsNone(second.next_cursor)
with self.assertRaises(StorageBackendError):
normalize_storage_key("../outside")
with self.assertRaises(StorageObjectMissing):
backend.get_bytes("campaign/missing.eml")
def test_local_listing_is_globally_sorted_and_ignores_symlinks(self) -> None:
with (
tempfile.TemporaryDirectory(prefix="govoplan-object-store-") as directory,
tempfile.TemporaryDirectory(prefix="govoplan-object-outside-") as outside,
):
root = Path(directory)
backend = LocalFilesystemStorageBackend(root)
backend.put_bytes("a/x.txt", b"nested")
backend.put_bytes("a.txt", b"sibling")
backend.put_bytes("b.txt", b"last")
outside_file = Path(outside) / "secret.txt"
outside_file.write_bytes(b"outside")
(root / "linked-file.txt").symlink_to(outside_file)
(root / "linked-directory").symlink_to(
Path(outside), target_is_directory=True
)
first = backend.list_objects(prefix="", limit=2)
second = backend.list_objects(
prefix="",
after=first.next_cursor,
limit=2,
)
self.assertEqual(
("a.txt", "a/x.txt"),
tuple(item.key for item in first.objects),
)
self.assertEqual("a/x.txt", first.next_cursor)
self.assertEqual(
("b.txt",),
tuple(item.key for item in second.objects),
)
self.assertIsNone(second.next_cursor)
with self.assertRaises(StorageBackendError):
backend.exists("../outside")
def test_s3_backend_distinguishes_missing_objects_from_backend_failure(
self,
) -> None:
backend = S3StorageBackend(
bucket="files",
endpoint_url="http://garage:3900",
region_name="garage",
access_key_id="key",
secret_access_key="secret",
deployment_managed=True,
)
client = _FakeS3Client()
backend._client = client
backend.put_bytes("campaign/message.eml", b"message/rfc822")
self.assertTrue(backend.exists("campaign/message.eml"))
self.assertEqual(b"message/rfc822", backend.get_bytes("campaign/message.eml"))
self.assertFalse(backend.exists("campaign/missing.eml"))
client.head_error = _S3Error("AccessDenied", 403)
with self.assertRaises(StorageBackendError):
backend.exists("campaign/message.eml")
def test_external_s3_requires_explicit_https_deployment_trust(self) -> None:
trusted = S3StorageBackend(
bucket="files",
endpoint_url="https://s3.internal.example.test",
region_name="eu-test-1",
access_key_id="key",
secret_access_key="secret",
endpoint_trusted=True,
)
trusted._client = _FakeS3Client()
trusted.put_bytes("files/document.txt", b"document")
self.assertEqual(b"document", trusted.get_bytes("files/document.txt"))
rejected = S3StorageBackend(
bucket="files",
endpoint_url="http://s3.internal.example.test",
region_name="eu-test-1",
access_key_id="key",
secret_access_key="secret",
endpoint_trusted=True,
)
with self.assertRaisesRegex(StorageBackendError, "HTTPS origin"):
rejected.client
if __name__ == "__main__":
unittest.main()