def test_preview_renders_real_messages(self, api, db): f = _seed_campaign_fixture(db, user_id_of(api), n_recipients=3) cid = api.post("/campaigns/create", headers=H(api), json=_cIn(f, name="Prev")).json()["id"] r = api.post(f"/campaigns/{cid}/preview?sample_size=3", headers=H(api)) assert r.status_code == 200, r.text d = r.json() assert d["sample_size"] == 3 msgs = d["messages"] assert len(msgs) == 3 m0 = msgs[0] assert "Camp1" in m0["body_html"] # substituted first name assert "%FirstName%" not in m0["body_html"] # no raw tokens remain assert "camp-acc" in m0["from_account"] # real account email assert m0["subject"] # subject rendered # invoice numbers unique per recipient invs = [m["raw_variables"]["invoice_number"] for m in msgs] assert len(set(invs)) == len(invs) assert all(m["amount"] is not None for m in msgs) # preview must NOT create campaign_recipients (nothing persisted) from app.models import CampaignRecipient assert db.query(CampaignRecipient).count() == 0 === 262:@router.post("/clear-all") 263:async def clear_all_campaigns(db: AsyncSession = Depends(get_db), @router.post("/clear-all") async def clear_all_campaigns(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), _: None = Depends(require_csrf)): """Delete ALL campaigns (not running ones). Requires explicit UI confirmation.""" rows = (await db.execute(select(Campaign).where(Campaign.user_id == user.id))).scalars().all() deleted, skipped = 0, 0 for c in rows: if c.status == "running": skipped += 1 continue await db.delete(c) deleted += 1 await db.commit() await log_audit(db, user.id, "campaign.clear_all", "campaign", None, {"deleted": deleted, "skipped": skipped}) return {"deleted": deleted, "skipped": skipped} # --------------------------------------------------------------------------- # Preview: materialize the campaign plan and render REAL messages (no sending) # --------------------------------------------------------------------------- @router.post("/{cid}/preview") async def preview_campaign(cid: int, sample_size: int = Query(0), db: AsyncSession = Depends(get_db),