From 8b083c952321c9af007ff6214940fa3dae4e685c Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 24 Sep 2026 14:49:03 -0700 Subject: [PATCH] test(integration): read team member alert deliveries without a shared accumulator --- tests/integration/_support/mail.py | 8 ++++-- .../spend/test_team_member_budget_alerts.py | 27 +++++++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/integration/_support/mail.py b/tests/integration/_support/mail.py index c46ab074581..ef4f8f9a0ca 100644 --- a/tests/integration/_support/mail.py +++ b/tests/integration/_support/mail.py @@ -35,6 +35,9 @@ class Mailbox: port: int received: SimpleQueue[Delivery] + def pending(self) -> int: + return self.received.qsize() + def drain(self) -> tuple[Delivery, ...]: return tuple(self.received.get_nowait() for _ in range(self.received.qsize())) @@ -64,14 +67,15 @@ def smtp_sink() -> Generator[Mailbox, None, None]: def _session(self) -> None: self._reply("220 integration-smtp ready") + # rebind-ok: the SMTP envelope is built across MAIL/RCPT lines and reset after DATA or RSET. sender = "" recipients: tuple[str, ...] = () while True: raw: Final = self.rfile.readline() if not raw: return - line = raw.decode().rstrip("\r\n") - verb = line.split(" ", 1)[0].upper() + line: Final = raw.decode().rstrip("\r\n") + verb: Final = line.split(" ", 1)[0].upper() if verb in {"EHLO", "HELO"}: self._reply("250 integration-smtp") elif verb == "MAIL": diff --git a/tests/integration/spend/test_team_member_budget_alerts.py b/tests/integration/spend/test_team_member_budget_alerts.py index 0dcb70d4836..49c3837aefa 100644 --- a/tests/integration/spend/test_team_member_budget_alerts.py +++ b/tests/integration/spend/test_team_member_budget_alerts.py @@ -6,7 +6,7 @@ import pytest import yaml from integration._support.client import Gateway, eventually from integration._support.database import read_rows -from integration._support.mail import Delivery, Mailbox, smtp_sink +from integration._support.mail import smtp_sink from integration._support.process import owned_proxy MEMBER_BUDGET: Final = 0.10 @@ -20,11 +20,6 @@ def _membership_spend(user_id: str, team_id: str) -> float: return float(str(rows[0]["spend"])) if rows else 0.0 -def _deliveries_for(mailbox: Mailbox, seen: list[Delivery]) -> list[Delivery]: - seen.extend(mailbox.drain()) - return seen - - def test_team_member_budget_thresholds_email_member_and_configured_recipients(gateway: Gateway, tmp_path: Path) -> None: member_email: Final = f"member-{uuid.uuid4().hex}@integration.test" finance_email: Final = f"finance-{uuid.uuid4().hex}@integration.test" @@ -49,7 +44,6 @@ def test_team_member_budget_thresholds_email_member_and_configured_recipients(ga ) candidate.post("/team/member_add", {"team_id": team_id, "member": {"user_id": user_id, "role": "user"}}) key: Final = scenario.key(team_id=team_id, user_id=user_id) - received: Final[list[Delivery]] = [] first: Final = candidate.request( "POST", @@ -62,7 +56,7 @@ def test_team_member_budget_thresholds_email_member_and_configured_recipients(ga eventually( lambda: _membership_spend(user_id, team_id), lambda spend: spend == pytest.approx(CALL_COST), seconds=70 ) - assert _deliveries_for(mailbox, received) == [], "no threshold is reached before the first call is recorded" + assert mailbox.drain() == (), "no threshold is reached before the first call is recorded" second: Final = candidate.request( "POST", @@ -71,9 +65,8 @@ def test_team_member_budget_thresholds_email_member_and_configured_recipients(ga key=key, ) assert second.status_code == 200, second.text - halfway: Final = eventually( - lambda: _deliveries_for(mailbox, received), lambda values: len(values) >= 1, seconds=30 - ) + eventually(mailbox.pending, lambda count: count >= 1, seconds=30) + halfway: Final = mailbox.drain() assert [delivery.recipients for delivery in halfway] == [(member_email,)], halfway assert "50%" in halfway[0].subject, halfway[0].subject assert f"${MEMBER_BUDGET}" in halfway[0].html, halfway[0].html @@ -90,14 +83,12 @@ def test_team_member_budget_thresholds_email_member_and_configured_recipients(ga key=key, ) assert third.status_code == 422 and third.json()["error"]["type"] == "budget_exceeded", third.text - capped: Final = eventually( - lambda: _deliveries_for(mailbox, received), lambda values: len(values) >= 3, seconds=30 - ) - hundred: Final = capped[1:] - assert all("100%" in delivery.subject for delivery in hundred), capped + eventually(mailbox.pending, lambda count: count >= 2, seconds=30) + hundred: Final = mailbox.drain() + assert all("100%" in delivery.subject for delivery in hundred), hundred assert {recipient for delivery in hundred for recipient in delivery.recipients} == { member_email, finance_email, - }, capped + }, hundred assert all(member_email in delivery.html and f"${MEMBER_BUDGET}" in delivery.html for delivery in hundred) - assert len(capped) == 3, capped + assert len(hundred) == 2, hundred