111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from govoplan_campaign.backend.campaign.mail_profile_boundary import (
|
|
campaign_editor_state_for_edit,
|
|
)
|
|
from govoplan_campaign.backend.schemas import (
|
|
CampaignVersionResponse,
|
|
CampaignVersionUpdateRequest,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"editor_state",
|
|
[
|
|
{"smtp": {"host": "smtp.example.test", "password": "secret"}},
|
|
{"transport": {"imap_password": "secret"}},
|
|
{
|
|
"review_send": {
|
|
"build_token": "forged",
|
|
"inspection_complete": True,
|
|
"reviewed_message_keys": [],
|
|
"updated_at": "2026-07-21T00:00:00+00:00",
|
|
"updated_by_user_id": "user-1",
|
|
}
|
|
},
|
|
],
|
|
)
|
|
def test_version_updates_reject_arbitrary_or_server_owned_editor_state(
|
|
editor_state: dict[str, object],
|
|
) -> None:
|
|
with pytest.raises(ValidationError, match="unsupported or transport-owned"):
|
|
CampaignVersionUpdateRequest(editor_state=editor_state)
|
|
|
|
|
|
def test_version_response_omits_legacy_secret_bearing_editor_state() -> None:
|
|
response = CampaignVersionResponse.model_validate(
|
|
{
|
|
"id": "version-1",
|
|
"campaign_id": "campaign-1",
|
|
"version_number": 1,
|
|
"schema_version": "1.0",
|
|
"editor_state": {
|
|
"opt_ins": {"inline_guidance": True},
|
|
"smtp": {"host": "smtp.internal.example", "password": "provider-secret"},
|
|
},
|
|
"created_at": datetime.now(UTC),
|
|
"updated_at": datetime.now(UTC),
|
|
}
|
|
)
|
|
|
|
assert response.editor_state == {"opt_ins": {"inline_guidance": True}}
|
|
assert "provider-secret" not in repr(response)
|
|
assert "internal.example" not in repr(response)
|
|
|
|
|
|
def test_review_build_token_is_visible_only_in_operator_diagnostics() -> None:
|
|
value = {
|
|
"id": "version-1",
|
|
"campaign_id": "campaign-1",
|
|
"version_number": 1,
|
|
"schema_version": "1.0",
|
|
"editor_state": {
|
|
"review_send": {
|
|
"build_token": "internal-build-token",
|
|
"inspection_complete": True,
|
|
"reviewed_message_keys": ["entry-1"],
|
|
"updated_at": "2026-07-21T00:00:00+00:00",
|
|
"updated_by_user_id": "reviewer-1",
|
|
}
|
|
},
|
|
"created_at": datetime.now(UTC),
|
|
"updated_at": datetime.now(UTC),
|
|
}
|
|
|
|
public = CampaignVersionResponse.model_validate(value)
|
|
operator = CampaignVersionResponse.model_validate(
|
|
value,
|
|
context={"include_diagnostics": True},
|
|
)
|
|
|
|
assert "build_token" not in public.editor_state["review_send"]
|
|
assert operator.editor_state["review_send"]["build_token"] == "internal-build-token"
|
|
|
|
|
|
def test_fork_copy_keeps_only_client_owned_bounded_metadata() -> None:
|
|
copied = campaign_editor_state_for_edit(
|
|
{
|
|
"created_from": "minimal_campaign",
|
|
"field_overrides": {"department": False},
|
|
"review_send": {
|
|
"build_token": "build-1",
|
|
"inspection_complete": True,
|
|
"reviewed_message_keys": ["entry-1"],
|
|
"updated_at": "2026-07-21T00:00:00+00:00",
|
|
"updated_by_user_id": "reviewer-1",
|
|
},
|
|
"credentials": {"password": "legacy-secret"},
|
|
}
|
|
)
|
|
|
|
assert copied == {
|
|
"created_from": "minimal_campaign",
|
|
"field_overrides": {"department": False},
|
|
}
|
|
assert "legacy-secret" not in repr(copied)
|