657:    def test_progress_after_start(self, api, db):
658-        f = _seed_campaign_fixture(db, user_id_of(api), n_recipients=3)
659-        cid = api.post("/campaigns/create", headers=H(api),
660-                       json=_cIn(f, name="Prog")).json()["id"]
661-        api.post(f"/campaigns/{cid}/start", headers=H(api))
662-        d = api.get(f"/campaigns/{cid}/progress").json()
663-        assert d["campaign_id"] == cid
664-        assert d["status"] == "running"
665-        assert d["total"] == 3 and d["queued"] == 3
666-        assert d["jobs_total"] == 3
667-        assert d["progress_pct"] == 0.0
668-
669-    @requires_broker
670-    def test_recipients_monitor_table(self, api, db):
671-        f = _seed_campaign_fixture(db, user_id_of(api), n_recipients=3)
672-        cid = api.post("/campaigns/create", headers=H(api),
673-                       json=_cIn(f, name="Mon")).json()["id"]
674-        api.post(f"/campaigns/{cid}/start", headers=H(api))
675-        d = api.get(f"/campaigns/{cid}/recipients").json()
676-        assert d["total"] == 3
677-        rows = d["recipients"]
678-        assert [r["seq"] for r in rows] == [1, 2, 3]
679-        r0 = rows[0]
680-        assert {"seq", "email", "status", "account", "subject", "template",
681-                "invoice_number", "amount", "attempts", "message_id"} <= set(r0)
682-        assert r0["account"] and "camp-acc" in r0["account"]
683-        assert r0["subject"]  # subject text resolved
684-        # filters work
685-        s = api.get(f"/campaigns/{cid}/recipients", params={"status": "queued"}).json()
686-        assert s["total"] == 3
687-        s2 = api.get(f"/campaigns/{cid}/recipients", params={"search": "camp2"}).json()
688-        assert s2["total"] == 1
689-
690-    @requires_broker
691-    def test_export_csv(self, api, db):
692-        f = _seed_campaign_fixture(db, user_id_of(api), n_recipients=2)
693-        cid = api.post("/campaigns/create", headers=H(api),
694-                       json=_cIn(f, name="Export Me")).json()["id"]
695-        api.post(f"/campaigns/{cid}/start", headers=H(api))
696-        r = api.get(f"/campaigns/{cid}/export")
697-        assert r.status_code == 200
698-        assert "text/csv" in r.headers["content-type"]
699-        rows = list(csv.reader(io.StringIO(r.text)))
700-        assert rows[0][0] == "seq"
701-        assert len(rows) == 3  # header + 2 recipients
702-        assert rows[1][1] == "camp1@x.com"
703-        assert rows[1][2] == "queued"
704-
705-    @requires_broker
706-    def test_sse_stream_first_events(self, api, db, db_engine):
707-        """SSE contract end-to-end. starlette's TestClient runs the whole ASGI app
