docs: surface permission-gated Campaign tasks
This commit is contained in:
@@ -5,8 +5,7 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from govoplan_campaign.backend.documentation import documentation_topics
|
||||
from govoplan_campaign.backend.manifest import get_manifest
|
||||
from govoplan_campaign.backend.documentation import CAMPAIGN_USER_DOCUMENTATION, documentation_topics
|
||||
from govoplan_core.core.modules import DocumentationContext
|
||||
|
||||
|
||||
@@ -47,6 +46,8 @@ def _topics(scopes: set[str], integrations: set[str] | None = None, *, documenta
|
||||
|
||||
|
||||
def test_campaign_runtime_documentation_provider_is_registered() -> None:
|
||||
from govoplan_campaign.backend.manifest import get_manifest
|
||||
|
||||
assert documentation_topics in get_manifest().documentation_providers
|
||||
|
||||
|
||||
@@ -150,9 +151,201 @@ def test_runtime_documentation_full_composition_uses_only_user_facing_names() ->
|
||||
assert "Managed file versions" in rendered
|
||||
assert "Address records" in rendered
|
||||
assert "In-app notifications" in rendered
|
||||
assert topic.metadata["help_contexts"] == ["campaigns.list", "campaign.overview"]
|
||||
for technical_name in integrations:
|
||||
assert technical_name not in rendered
|
||||
assert "0.1." not in rendered
|
||||
assert "0.2." not in rendered
|
||||
assert "hostname" not in rendered.lower()
|
||||
assert "secret" not in rendered.lower()
|
||||
|
||||
|
||||
def _visible_static_topics(
|
||||
scopes: set[str],
|
||||
*,
|
||||
modules: set[str] | None = None,
|
||||
capabilities: set[str] | None = None,
|
||||
) -> set[str]:
|
||||
installed = modules or {"campaigns"}
|
||||
available_capabilities = capabilities or set()
|
||||
principal = _Principal(frozenset(scopes))
|
||||
visible: set[str] = set()
|
||||
for topic in CAMPAIGN_USER_DOCUMENTATION:
|
||||
condition = topic.conditions[0]
|
||||
if not set(condition.required_modules).issubset(installed):
|
||||
continue
|
||||
if not set(condition.required_capabilities).issubset(available_capabilities):
|
||||
continue
|
||||
if not all(principal.has(scope) for scope in condition.required_scopes):
|
||||
continue
|
||||
visible.add(topic.id)
|
||||
return visible
|
||||
|
||||
|
||||
def test_campaign_manager_sees_only_authoring_tasks_from_the_static_handbook() -> None:
|
||||
visible = _visible_static_topics(
|
||||
{
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:campaign:create",
|
||||
"campaigns:campaign:update",
|
||||
"campaigns:campaign:copy",
|
||||
"campaigns:campaign:validate",
|
||||
"campaigns:campaign:build",
|
||||
"campaigns:recipient:read",
|
||||
"campaigns:recipient:write",
|
||||
"campaigns:recipient:import",
|
||||
"campaigns:report:read",
|
||||
}
|
||||
)
|
||||
|
||||
assert {
|
||||
"campaigns.workflow.create-campaign",
|
||||
"campaigns.workflow.create-editable-successor",
|
||||
"campaigns.workflow.import-recipients",
|
||||
"campaigns.workflow.view-delivery-report",
|
||||
}.issubset(visible)
|
||||
assert "campaigns.workflow.queue-delivery" not in visible
|
||||
assert "campaigns.workflow.send-small-controlled-run" not in visible
|
||||
assert "campaigns.workflow.export-delivery-report" not in visible
|
||||
assert "campaigns.workflow.share-campaign" not in visible
|
||||
assert "campaigns.workflow.archive-campaign" not in visible
|
||||
assert "campaigns.workflow.delete-untouched-draft" not in visible
|
||||
|
||||
|
||||
def test_sender_sees_queue_and_send_only_with_the_mail_contract_and_profile_authority() -> None:
|
||||
scopes = {
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:campaign:queue",
|
||||
"campaigns:campaign:send",
|
||||
"campaigns:recipient:read",
|
||||
"campaigns:report:read",
|
||||
"mail:profile:use",
|
||||
}
|
||||
|
||||
without_mail = _visible_static_topics(scopes)
|
||||
with_mail = _visible_static_topics(
|
||||
scopes,
|
||||
modules={"campaigns", "mail"},
|
||||
capabilities={"mail.campaign_delivery"},
|
||||
)
|
||||
|
||||
assert "campaigns.workflow.queue-delivery" not in without_mail
|
||||
assert "campaigns.workflow.send-small-controlled-run" not in without_mail
|
||||
assert "campaigns.workflow.queue-delivery" in with_mail
|
||||
assert "campaigns.workflow.send-small-controlled-run" in with_mail
|
||||
|
||||
|
||||
def test_connected_authoring_tasks_require_their_declared_contracts_and_permissions() -> None:
|
||||
attachment_scopes = {
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:campaign:update",
|
||||
"campaigns:campaign:validate",
|
||||
"campaigns:recipient:read",
|
||||
"files:file:read",
|
||||
"files:file:share",
|
||||
}
|
||||
source_scopes = {
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:campaign:update",
|
||||
"campaigns:recipient:read",
|
||||
"campaigns:recipient:write",
|
||||
"campaigns:recipient:import",
|
||||
}
|
||||
|
||||
files_visible = _visible_static_topics(
|
||||
attachment_scopes,
|
||||
modules={"campaigns", "files"},
|
||||
capabilities={"files.campaign_attachments"},
|
||||
)
|
||||
addresses_visible = _visible_static_topics(
|
||||
source_scopes,
|
||||
modules={"campaigns", "addresses"},
|
||||
capabilities={"addresses.recipient_source"},
|
||||
)
|
||||
|
||||
assert "campaigns.workflow.use-managed-attachments" in files_visible
|
||||
assert "campaigns.workflow.import-address-source" in addresses_visible
|
||||
assert "campaigns.workflow.use-managed-attachments" not in _visible_static_topics(attachment_scopes)
|
||||
assert "campaigns.workflow.import-address-source" not in _visible_static_topics(source_scopes)
|
||||
|
||||
|
||||
def test_report_export_and_lifecycle_tasks_are_independently_permission_gated() -> None:
|
||||
exporter = _visible_static_topics(
|
||||
{
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:report:export",
|
||||
"campaigns:recipient:export",
|
||||
}
|
||||
)
|
||||
custodian = _visible_static_topics(
|
||||
{
|
||||
"campaigns:campaign:read",
|
||||
"campaigns:campaign:share",
|
||||
"campaigns:campaign:archive",
|
||||
"campaigns:campaign:delete",
|
||||
}
|
||||
)
|
||||
|
||||
assert "campaigns.workflow.export-delivery-report" in exporter
|
||||
assert "campaigns.workflow.view-delivery-report" not in exporter
|
||||
assert "campaigns.workflow.share-campaign" in custodian
|
||||
assert "campaigns.workflow.archive-campaign" in custodian
|
||||
assert "campaigns.workflow.delete-untouched-draft" in custodian
|
||||
assert "campaigns.workflow.export-delivery-report" not in custodian
|
||||
|
||||
|
||||
def test_static_campaign_handbook_has_unique_ids_help_contexts_and_no_planned_resend_claim() -> None:
|
||||
from govoplan_campaign.backend.manifest import get_manifest
|
||||
|
||||
topics = get_manifest().documentation
|
||||
ids = [topic.id for topic in topics]
|
||||
rendered_static = "\n".join(
|
||||
(topic.title + "\n" + topic.summary + "\n" + topic.body).lower()
|
||||
for topic in CAMPAIGN_USER_DOCUMENTATION
|
||||
)
|
||||
known_help_contexts = {
|
||||
"campaigns.list",
|
||||
"campaign.overview",
|
||||
"campaign.settings",
|
||||
"campaign.fields",
|
||||
"campaign.template",
|
||||
"campaign.attachments",
|
||||
"campaign.recipients",
|
||||
"campaign.recipient-data",
|
||||
"campaign.server-settings",
|
||||
"campaign.global-settings",
|
||||
"campaign.review-send",
|
||||
"campaign.report",
|
||||
"campaign.audit",
|
||||
"campaign.json",
|
||||
}
|
||||
|
||||
assert len(ids) == len(set(ids))
|
||||
assert "single resend" not in rendered_static
|
||||
for topic in CAMPAIGN_USER_DOCUMENTATION:
|
||||
assert topic.metadata["kind"] == "workflow"
|
||||
assert topic.metadata["prerequisites"]
|
||||
assert topic.metadata["steps"]
|
||||
assert topic.metadata["outcome"]
|
||||
assert topic.metadata["verification"]
|
||||
assert set(topic.metadata["help_contexts"]).issubset(known_help_contexts)
|
||||
|
||||
existing_user_workflows = {
|
||||
topic.id: topic
|
||||
for topic in topics
|
||||
if topic.id
|
||||
in {
|
||||
"campaigns.mail-profile-user-journey",
|
||||
"campaigns.workflow.prepare-validate-and-build",
|
||||
"campaigns.workflow.complete-review",
|
||||
"campaigns.workflow.retry-and-reconcile",
|
||||
}
|
||||
}
|
||||
assert set(existing_user_workflows) == {
|
||||
"campaigns.mail-profile-user-journey",
|
||||
"campaigns.workflow.prepare-validate-and-build",
|
||||
"campaigns.workflow.complete-review",
|
||||
"campaigns.workflow.retry-and-reconcile",
|
||||
}
|
||||
for topic in existing_user_workflows.values():
|
||||
assert topic.metadata["help_contexts"]
|
||||
|
||||
Reference in New Issue
Block a user