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