from __future__ import annotations import unittest from types import SimpleNamespace from unittest.mock import patch from sqlalchemy.orm import Session from govoplan_core.core.modules import DocumentationContext from govoplan_files.backend.documentation import documentation_topics from govoplan_files.backend.storage.connector_profiles import ConnectorProfile class _Principal: tenant_id = "tenant-1" user = SimpleNamespace(id="user-1") group_ids = frozenset({"group-1"}) def __init__(self, scopes: set[str]) -> None: self.scopes = frozenset(scopes) def has(self, scope: str) -> bool: return scope in self.scopes class FilesRuntimeDocumentationTests(unittest.TestCase): def setUp(self) -> None: self.session = Session() def tearDown(self) -> None: self.session.close() def context( self, scopes: set[str], *, settings: object | None = None, documentation_type: str = "user", ) -> DocumentationContext: return DocumentationContext( registry=object(), principal=_Principal(scopes), settings=settings, session=self.session, documentation_type=documentation_type, # type: ignore[arg-type] ) def test_upload_tasks_state_exact_current_safe_limits(self) -> None: settings = SimpleNamespace( file_upload_max_bytes=7 * 1024 * 1024, file_upload_zip_max_bytes=19 * 1024 * 1024, ) with patch( "govoplan_files.backend.documentation.visible_connector_profiles_for_actor", return_value=[], ): topics = { topic.id: topic for topic in documentation_topics( self.context( {"files:file:read", "files:file:upload"}, settings=settings ) ) } upload = topics["files.workflow.upload-managed-files"] self.assertIn("7 MiB (7,340,032 bytes)", upload.body) self.assertEqual(["files.list"], upload.metadata["help_contexts"]) self.assertEqual( {"files:file:read", "files:file:upload"}, set(upload.conditions[0].required_scopes), ) archive = topics["files.workflow.upload-and-unpack-zip"] self.assertIn("19 MiB (19,922,944 bytes)", archive.body) self.assertIn("7 MiB (7,340,032 bytes)", archive.body) self.assertIn("1,000", archive.body) self.assertIn("Actual extracted bytes are counted", archive.body) def test_connector_task_requires_authority_and_a_visible_usable_profile( self, ) -> None: usable = ConnectorProfile( id="private-profile-id", label="Private profile label", provider="webdav", scope_type="tenant", scope_id="tenant-1", endpoint_url="https://private.example.invalid/secret-root", base_path="/secret-root", credential_mode="anonymous", ) context = self.context({"files:file:read", "files:file:upload"}) with patch( "govoplan_files.backend.documentation.visible_connector_profiles_for_actor", return_value=[usable], ) as visible: topics = {topic.id: topic for topic in documentation_topics(context)} self.assertIn("files.workflow.import-managed-snapshot", topics) self.assertNotIn("files.connector-import-unavailable", topics) task = topics["files.workflow.import-managed-snapshot"] self.assertEqual("configured", task.layer) self.assertEqual( ["files.list", "files.connector-import"], task.metadata["help_contexts"] ) self.assertIn("re-authorized", task.body) self.assertNotIn("private-profile-id", repr(task)) self.assertNotIn("private.example.invalid", repr(task)) self.assertNotIn("secret-root", repr(task)) visible.assert_called_once() self.assertEqual(("group-1",), tuple(visible.call_args.kwargs["group_ids"])) without_authority = { topic.id: topic for topic in documentation_topics(self.context({"files:file:read"})) } self.assertNotIn("files.workflow.import-managed-snapshot", without_authority) self.assertIn( "both permission to view Files and permission to upload", without_authority["files.connector-import-unavailable"].body, ) def test_connector_limitation_is_precise_but_never_exposes_profile_internals( self, ) -> None: blocked = ConnectorProfile( id="sensitive-profile-id", label="Sensitive label", provider="s3", scope_type="tenant", scope_id="tenant-1", endpoint_url="https://internal.example.invalid/private", base_path="/classified", credential_mode="basic", password_value="credential-secret", ) with patch( "govoplan_files.backend.documentation.visible_connector_profiles_for_actor", return_value=[blocked], ): topics = { topic.id: topic for topic in documentation_topics( self.context({"files:file:read", "files:file:upload"}) ) } self.assertNotIn("files.workflow.import-managed-snapshot", topics) limitation = topics["files.connector-import-unavailable"] self.assertEqual("available", limitation.layer) self.assertIn("pinning-safe provider", limitation.body) payload = repr(limitation) for secret in ( "sensitive-profile-id", "Sensitive label", "internal.example.invalid", "classified", "credential-secret", ): self.assertNotIn(secret, payload) def test_connector_evaluation_errors_fail_closed_without_error_details( self, ) -> None: with patch( "govoplan_files.backend.documentation.visible_connector_profiles_for_actor", side_effect=RuntimeError("private endpoint /root credential-id"), ): topics = { topic.id: topic for topic in documentation_topics( self.context({"files:file:read", "files:file:upload"}) ) } self.assertNotIn("files.workflow.import-managed-snapshot", topics) limitation = topics["files.connector-import-unavailable"] self.assertIn("could not be safely evaluated", limitation.body) self.assertNotIn("private endpoint", repr(limitation)) self.assertNotIn("credential-id", repr(limitation)) def test_runtime_provider_only_emits_user_documentation(self) -> None: self.assertEqual( (), documentation_topics(self.context(set(), documentation_type="admin")) ) if __name__ == "__main__": unittest.main()