feat(files): surface configured handbook tasks
This commit is contained in:
190
tests/test_documentation.py
Normal file
190
tests/test_documentation.py
Normal file
@@ -0,0 +1,190 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user