feat(security): isolate bounded work and support required auth actions
This commit is contained in:
+50
-6
@@ -8,10 +8,11 @@ import tempfile
|
||||
import time
|
||||
import unittest
|
||||
import zipfile
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from email import policy
|
||||
from email.parser import BytesParser
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pyzipper
|
||||
@@ -3545,13 +3546,17 @@ class ApiSmokeTests(unittest.TestCase):
|
||||
self.assertEqual(len(jobs.json()["jobs"]), 1)
|
||||
job_summary = jobs.json()["jobs"][0]
|
||||
self.assertEqual(job_summary["campaign_version_id"], version_id)
|
||||
self.assertNotIn("resolved_recipients", job_summary)
|
||||
self.assertEqual(job_summary["resolved_recipients"]["to"][0]["email"], "recipient@example.org")
|
||||
self.assertNotIn("attachments", job_summary)
|
||||
detail = self.client.get(
|
||||
f"/api/v1/campaigns/{campaign_id}/jobs/{job_summary['id']}",
|
||||
headers=headers,
|
||||
)
|
||||
self.assertEqual(detail.status_code, 200, detail.text)
|
||||
job = detail.json()["job"]
|
||||
self.assertEqual(job_summary["resolved_recipients"], {
|
||||
kind: job["resolved_recipients"][kind] for kind in ("to", "cc", "bcc")
|
||||
})
|
||||
self.assertEqual(job["resolved_recipients"]["to"][0]["email"], "recipient@example.org")
|
||||
self.assertEqual(
|
||||
{
|
||||
@@ -4613,7 +4618,9 @@ class ApiSmokeTests(unittest.TestCase):
|
||||
self.assertEqual(queued_summary.json()["status_counts"]["send"]["queued"], 2)
|
||||
|
||||
from govoplan_campaign.backend.db.models import Campaign, CampaignJob, CampaignVersion, SendAttempt
|
||||
from govoplan_campaign.backend.sending.jobs import send_campaign_job
|
||||
from govoplan_campaign.backend.sending.jobs import _begin_job_delivery_recovery, send_campaign_job
|
||||
from govoplan_campaign.backend.services.delivery_recovery import job_recovery_metadata
|
||||
from govoplan_core.core.runtime_coordination import DistributedLease, RuntimeNode, process_runtime_identity
|
||||
|
||||
with SessionLocal() as session:
|
||||
jobs = (
|
||||
@@ -4645,7 +4652,42 @@ class ApiSmokeTests(unittest.TestCase):
|
||||
|
||||
with SessionLocal() as session:
|
||||
result = send_campaign_job(session, job_id=uncertain_job_id, use_rate_limit=False)
|
||||
self.assertEqual(result.status, "outcome_unknown")
|
||||
# Observing SENDING is not proof that its owner has stopped.
|
||||
self.assertEqual(result.status, "already_sending")
|
||||
job = session.get(CampaignJob, uncertain_job_id)
|
||||
version = session.get(CampaignVersion, version_id)
|
||||
recovery = _begin_job_delivery_recovery(
|
||||
job=job, context=SimpleNamespace(version=version), claim_token=job.claim_token,
|
||||
)
|
||||
self.assertTrue(recovery.operation_id)
|
||||
session.expire_all()
|
||||
lease = session.query(DistributedLease).filter(
|
||||
DistributedLease.resource_key == f"campaign:delivery:{job.tenant_id}:{job.id}",
|
||||
).one()
|
||||
lease.holder_node_id = "smoke-stopped-worker"
|
||||
lease.holder_incarnation = "smoke-old-incarnation"
|
||||
lease.expires_at = datetime.now(timezone.utc) - timedelta(minutes=1)
|
||||
session.add(RuntimeNode(
|
||||
installation_id=process_runtime_identity().installation_id,
|
||||
node_id="smoke-stopped-worker", incarnation="smoke-old-incarnation",
|
||||
role="worker", software_version="test", composition_hash="c" * 64,
|
||||
state="stopped",
|
||||
))
|
||||
session.commit()
|
||||
# Read the committed representation, just as an independent HTTP
|
||||
# reader does (SQLite drops timezone objects during persistence).
|
||||
session.expire_all()
|
||||
metadata = job_recovery_metadata(session, [job])[job.id]["smtp"]
|
||||
self.assertTrue(metadata["eligible"])
|
||||
|
||||
recovered = self.client.post(
|
||||
f"/api/v1/campaigns/{campaign_id}/jobs/{uncertain_job_id}/recover-claim",
|
||||
headers=headers,
|
||||
json={"channel": "smtp", "expected_revision": metadata["revision"],
|
||||
"note": "Fixture worker is confirmed stopped; inspect provider evidence next."},
|
||||
)
|
||||
self.assertEqual(recovered.status_code, 200, recovered.text)
|
||||
self.assertTrue(recovered.json()["result"]["reconciliation_required"])
|
||||
|
||||
retry_unknown = self.client.post(
|
||||
f"/api/v1/campaigns/{campaign_id}/jobs/retry",
|
||||
@@ -4672,7 +4714,8 @@ class ApiSmokeTests(unittest.TestCase):
|
||||
self.assertEqual(page.json()["total"], 2)
|
||||
self.assertEqual(page.json()["pages"], 2)
|
||||
self.assertEqual(page.json()["counts"]["send"]["outcome_unknown"], 1)
|
||||
self.assertNotIn("resolved_recipients", page.json()["jobs"][0])
|
||||
self.assertIn("resolved_recipients", page.json()["jobs"][0])
|
||||
self.assertNotIn("attachments", page.json()["jobs"][0])
|
||||
|
||||
filtered_page = self.client.get(
|
||||
f"/api/v1/campaigns/{campaign_id}/jobs",
|
||||
@@ -4851,7 +4894,8 @@ class ApiSmokeTests(unittest.TestCase):
|
||||
self.assertEqual(first_jobs.json()["total_unfiltered"], 1)
|
||||
self.assertEqual(first_jobs.json()["review"]["required_count"], 0)
|
||||
self.assertIn("reviewed", first_jobs.json()["jobs"][0])
|
||||
self.assertNotIn("resolved_recipients", first_jobs.json()["jobs"][0])
|
||||
self.assertEqual(first_jobs.json()["jobs"][0]["resolved_recipients"]["to"][0]["email"], "recipient-1@example.org")
|
||||
self.assertNotIn("attachments", first_jobs.json()["jobs"][0])
|
||||
|
||||
first_csv = self.client.get(
|
||||
f"/api/v1/campaigns/{campaign_id}/report/jobs.csv",
|
||||
|
||||
Reference in New Issue
Block a user