feat: contribute governed campaign reports
This commit is contained in:
@@ -18,6 +18,11 @@ from govoplan_campaign.backend.reports.aggregate import (
|
||||
AggregateCampaignReportError,
|
||||
generate_aggregate_campaign_report,
|
||||
)
|
||||
from govoplan_campaign.backend.reports.provider import (
|
||||
CAMPAIGN_REPORT_PRIVACY_TRANSFORMS,
|
||||
CampaignAggregateReportProvider,
|
||||
)
|
||||
from govoplan_core.core.reporting import ReportProviderRequest
|
||||
from govoplan_campaign.backend.schemas import ReportEmailRequest
|
||||
from govoplan_core.core.change_sequence import ChangeSequenceEntry
|
||||
from govoplan_core.db.base import Base
|
||||
@@ -40,7 +45,9 @@ def test_full_report_and_job_detail_reject_aggregate_only_principal() -> None:
|
||||
campaign = SimpleNamespace(id="campaign-1", tenant_id="tenant-1")
|
||||
|
||||
with (
|
||||
patch.object(report_routes, "_get_campaign_for_principal", return_value=campaign),
|
||||
patch.object(
|
||||
report_routes, "_get_campaign_for_principal", return_value=campaign
|
||||
),
|
||||
pytest.raises(HTTPException) as full_report_denied,
|
||||
):
|
||||
report_routes.campaign_report(
|
||||
@@ -64,7 +71,9 @@ def test_full_report_and_job_detail_reject_aggregate_only_principal() -> None:
|
||||
assert job_detail_denied.value.status_code == 403
|
||||
|
||||
with (
|
||||
patch.object(report_routes, "_get_campaign_for_principal", return_value=campaign),
|
||||
patch.object(
|
||||
report_routes, "_get_campaign_for_principal", return_value=campaign
|
||||
),
|
||||
pytest.raises(HTTPException) as report_email_denied,
|
||||
):
|
||||
report_routes.email_campaign_report(
|
||||
@@ -119,10 +128,15 @@ def test_aggregate_route_uses_only_the_safe_projection() -> None:
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", ["/campaigns/aggregate-reports", "/campaigns/aggregate-reports/{campaign_id}"])
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/campaigns/aggregate-reports", "/campaigns/aggregate-reports/{campaign_id}"],
|
||||
)
|
||||
def test_aggregate_routes_require_report_read_permission(path: str) -> None:
|
||||
route = next(item for item in campaign_api.router.routes if item.path == path)
|
||||
dependency = next(item for item in route.dependant.dependencies if item.name == "principal")
|
||||
dependency = next(
|
||||
item for item in route.dependant.dependencies if item.name == "principal"
|
||||
)
|
||||
|
||||
with pytest.raises(HTTPException) as denied:
|
||||
dependency.call(_Principal())
|
||||
@@ -148,7 +162,9 @@ def test_aggregate_projection_is_tenant_isolated_and_needs_no_optional_module()
|
||||
],
|
||||
)
|
||||
with Session(engine) as session:
|
||||
session.add(Tenant(id="tenant-1", slug="tenant-1", name="Tenant 1", settings={}))
|
||||
session.add(
|
||||
Tenant(id="tenant-1", slug="tenant-1", name="Tenant 1", settings={})
|
||||
)
|
||||
campaign = Campaign(
|
||||
id="campaign-1",
|
||||
tenant_id="tenant-1",
|
||||
@@ -168,20 +184,22 @@ def test_aggregate_projection_is_tenant_isolated_and_needs_no_optional_module()
|
||||
campaign.current_version_id = version.id
|
||||
session.add_all([campaign, version])
|
||||
for index in range(5):
|
||||
session.add(CampaignJob(
|
||||
id=f"job-{index}",
|
||||
tenant_id="tenant-1",
|
||||
campaign_id=campaign.id,
|
||||
campaign_version_id=version.id,
|
||||
entry_index=index,
|
||||
recipient_email=f"private-{index}@example.test",
|
||||
subject="Private",
|
||||
build_status="built",
|
||||
validation_status="ready",
|
||||
queue_status="queued",
|
||||
send_status="smtp_accepted",
|
||||
imap_status="not_requested",
|
||||
))
|
||||
session.add(
|
||||
CampaignJob(
|
||||
id=f"job-{index}",
|
||||
tenant_id="tenant-1",
|
||||
campaign_id=campaign.id,
|
||||
campaign_version_id=version.id,
|
||||
entry_index=index,
|
||||
recipient_email=f"private-{index}@example.test",
|
||||
subject="Private",
|
||||
build_status="built",
|
||||
validation_status="ready",
|
||||
queue_status="queued",
|
||||
send_status="smtp_accepted",
|
||||
imap_status="not_requested",
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
report = generate_aggregate_campaign_report(
|
||||
@@ -192,6 +210,30 @@ def test_aggregate_projection_is_tenant_isolated_and_needs_no_optional_module()
|
||||
assert report.population.denominator.value == 5
|
||||
assert report.outcomes.smtp_accepted.value == 5
|
||||
|
||||
provider = CampaignAggregateReportProvider()
|
||||
provider_principal = _Principal(
|
||||
"campaigns:report:read",
|
||||
"tenant:*",
|
||||
)
|
||||
descriptors = provider.list_reports(session, provider_principal)
|
||||
assert descriptors[0].report_id == "delivery-outcomes"
|
||||
assert descriptors[0].reidentification_risk == "low"
|
||||
provided = provider.execute_report(
|
||||
session,
|
||||
provider_principal,
|
||||
request=ReportProviderRequest(
|
||||
report_id="delivery-outcomes",
|
||||
parameters={"campaign_id": campaign.id},
|
||||
purpose="Tenant delivery overview",
|
||||
audience_scope={"scope_type": "tenant", "scope_id": "tenant-1"},
|
||||
),
|
||||
)
|
||||
assert set(provided.applied_privacy_transforms) == set(
|
||||
CAMPAIGN_REPORT_PRIVACY_TRANSFORMS
|
||||
)
|
||||
assert "recipient_email" not in repr(provided.payload)
|
||||
assert provided.source_revisions[0]["revision_id"] == version.id
|
||||
|
||||
with pytest.raises(AggregateCampaignReportError):
|
||||
generate_aggregate_campaign_report(
|
||||
session,
|
||||
|
||||
Reference in New Issue
Block a user